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         return res;
978     }
979     
980 });
981
982
983  /*
984  * Based on:
985  * Ext JS Library 1.1.1
986  * Copyright(c) 2006-2007, Ext JS, LLC.
987  *
988  * Originally Released Under LGPL - original licence link has changed is not relivant.
989  *
990  * Fork - LGPL
991  * <script type="text/javascript">
992  */
993
994 /**
995  * @class Date
996  *
997  * The date parsing and format syntax is a subset of
998  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
999  * supported will provide results equivalent to their PHP versions.
1000  *
1001  * Following is the list of all currently supported formats:
1002  *<pre>
1003 Sample date:
1004 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1005
1006 Format  Output      Description
1007 ------  ----------  --------------------------------------------------------------
1008   d      10         Day of the month, 2 digits with leading zeros
1009   D      Wed        A textual representation of a day, three letters
1010   j      10         Day of the month without leading zeros
1011   l      Wednesday  A full textual representation of the day of the week
1012   S      th         English ordinal day of month suffix, 2 chars (use with j)
1013   w      3          Numeric representation of the day of the week
1014   z      9          The julian date, or day of the year (0-365)
1015   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1016   F      January    A full textual representation of the month
1017   m      01         Numeric representation of a month, with leading zeros
1018   M      Jan        Month name abbreviation, three letters
1019   n      1          Numeric representation of a month, without leading zeros
1020   t      31         Number of days in the given month
1021   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1022   Y      2007       A full numeric representation of a year, 4 digits
1023   y      07         A two digit representation of a year
1024   a      pm         Lowercase Ante meridiem and Post meridiem
1025   A      PM         Uppercase Ante meridiem and Post meridiem
1026   g      3          12-hour format of an hour without leading zeros
1027   G      15         24-hour format of an hour without leading zeros
1028   h      03         12-hour format of an hour with leading zeros
1029   H      15         24-hour format of an hour with leading zeros
1030   i      05         Minutes with leading zeros
1031   s      01         Seconds, with leading zeros
1032   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1033   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1034   T      CST        Timezone setting of the machine running the code
1035   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1036 </pre>
1037  *
1038  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1039  * <pre><code>
1040 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1041 document.write(dt.format('Y-m-d'));                         //2007-01-10
1042 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1043 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1044  </code></pre>
1045  *
1046  * Here are some standard date/time patterns that you might find helpful.  They
1047  * are not part of the source of Date.js, but to use them you can simply copy this
1048  * block of code into any script that is included after Date.js and they will also become
1049  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1050  * <pre><code>
1051 Date.patterns = {
1052     ISO8601Long:"Y-m-d H:i:s",
1053     ISO8601Short:"Y-m-d",
1054     ShortDate: "n/j/Y",
1055     LongDate: "l, F d, Y",
1056     FullDateTime: "l, F d, Y g:i:s A",
1057     MonthDay: "F d",
1058     ShortTime: "g:i A",
1059     LongTime: "g:i:s A",
1060     SortableDateTime: "Y-m-d\\TH:i:s",
1061     UniversalSortableDateTime: "Y-m-d H:i:sO",
1062     YearMonth: "F, Y"
1063 };
1064 </code></pre>
1065  *
1066  * Example usage:
1067  * <pre><code>
1068 var dt = new Date();
1069 document.write(dt.format(Date.patterns.ShortDate));
1070  </code></pre>
1071  */
1072
1073 /*
1074  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1075  * They generate precompiled functions from date formats instead of parsing and
1076  * processing the pattern every time you format a date.  These functions are available
1077  * on every Date object (any javascript function).
1078  *
1079  * The original article and download are here:
1080  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1081  *
1082  */
1083  
1084  
1085  // was in core
1086 /**
1087  Returns the number of milliseconds between this date and date
1088  @param {Date} date (optional) Defaults to now
1089  @return {Number} The diff in milliseconds
1090  @member Date getElapsed
1091  */
1092 Date.prototype.getElapsed = function(date) {
1093         return Math.abs((date || new Date()).getTime()-this.getTime());
1094 };
1095 // was in date file..
1096
1097
1098 // private
1099 Date.parseFunctions = {count:0};
1100 // private
1101 Date.parseRegexes = [];
1102 // private
1103 Date.formatFunctions = {count:0};
1104
1105 // private
1106 Date.prototype.dateFormat = function(format) {
1107     if (Date.formatFunctions[format] == null) {
1108         Date.createNewFormat(format);
1109     }
1110     var func = Date.formatFunctions[format];
1111     return this[func]();
1112 };
1113
1114
1115 /**
1116  * Formats a date given the supplied format string
1117  * @param {String} format The format string
1118  * @return {String} The formatted date
1119  * @method
1120  */
1121 Date.prototype.format = Date.prototype.dateFormat;
1122
1123 // private
1124 Date.createNewFormat = function(format) {
1125     var funcName = "format" + Date.formatFunctions.count++;
1126     Date.formatFunctions[format] = funcName;
1127     var code = "Date.prototype." + funcName + " = function(){return ";
1128     var special = false;
1129     var ch = '';
1130     for (var i = 0; i < format.length; ++i) {
1131         ch = format.charAt(i);
1132         if (!special && ch == "\\") {
1133             special = true;
1134         }
1135         else if (special) {
1136             special = false;
1137             code += "'" + String.escape(ch) + "' + ";
1138         }
1139         else {
1140             code += Date.getFormatCode(ch);
1141         }
1142     }
1143     /** eval:var:zzzzzzzzzzzzz */
1144     eval(code.substring(0, code.length - 3) + ";}");
1145 };
1146
1147 // private
1148 Date.getFormatCode = function(character) {
1149     switch (character) {
1150     case "d":
1151         return "String.leftPad(this.getDate(), 2, '0') + ";
1152     case "D":
1153         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1154     case "j":
1155         return "this.getDate() + ";
1156     case "l":
1157         return "Date.dayNames[this.getDay()] + ";
1158     case "S":
1159         return "this.getSuffix() + ";
1160     case "w":
1161         return "this.getDay() + ";
1162     case "z":
1163         return "this.getDayOfYear() + ";
1164     case "W":
1165         return "this.getWeekOfYear() + ";
1166     case "F":
1167         return "Date.monthNames[this.getMonth()] + ";
1168     case "m":
1169         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1170     case "M":
1171         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1172     case "n":
1173         return "(this.getMonth() + 1) + ";
1174     case "t":
1175         return "this.getDaysInMonth() + ";
1176     case "L":
1177         return "(this.isLeapYear() ? 1 : 0) + ";
1178     case "Y":
1179         return "this.getFullYear() + ";
1180     case "y":
1181         return "('' + this.getFullYear()).substring(2, 4) + ";
1182     case "a":
1183         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1184     case "A":
1185         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1186     case "g":
1187         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1188     case "G":
1189         return "this.getHours() + ";
1190     case "h":
1191         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1192     case "H":
1193         return "String.leftPad(this.getHours(), 2, '0') + ";
1194     case "i":
1195         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1196     case "s":
1197         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1198     case "O":
1199         return "this.getGMTOffset() + ";
1200     case "P":
1201         return "this.getGMTColonOffset() + ";
1202     case "T":
1203         return "this.getTimezone() + ";
1204     case "Z":
1205         return "(this.getTimezoneOffset() * -60) + ";
1206     default:
1207         return "'" + String.escape(character) + "' + ";
1208     }
1209 };
1210
1211 /**
1212  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1213  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1214  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1215  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1216  * string or the parse operation will fail.
1217  * Example Usage:
1218 <pre><code>
1219 //dt = Fri May 25 2007 (current date)
1220 var dt = new Date();
1221
1222 //dt = Thu May 25 2006 (today's month/day in 2006)
1223 dt = Date.parseDate("2006", "Y");
1224
1225 //dt = Sun Jan 15 2006 (all date parts specified)
1226 dt = Date.parseDate("2006-1-15", "Y-m-d");
1227
1228 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1229 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1230 </code></pre>
1231  * @param {String} input The unparsed date as a string
1232  * @param {String} format The format the date is in
1233  * @return {Date} The parsed date
1234  * @static
1235  */
1236 Date.parseDate = function(input, format) {
1237     if (Date.parseFunctions[format] == null) {
1238         Date.createParser(format);
1239     }
1240     var func = Date.parseFunctions[format];
1241     return Date[func](input);
1242 };
1243 /**
1244  * @private
1245  */
1246
1247 Date.createParser = function(format) {
1248     var funcName = "parse" + Date.parseFunctions.count++;
1249     var regexNum = Date.parseRegexes.length;
1250     var currentGroup = 1;
1251     Date.parseFunctions[format] = funcName;
1252
1253     var code = "Date." + funcName + " = function(input){\n"
1254         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1255         + "var d = new Date();\n"
1256         + "y = d.getFullYear();\n"
1257         + "m = d.getMonth();\n"
1258         + "d = d.getDate();\n"
1259         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1260         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1261         + "if (results && results.length > 0) {";
1262     var regex = "";
1263
1264     var special = false;
1265     var ch = '';
1266     for (var i = 0; i < format.length; ++i) {
1267         ch = format.charAt(i);
1268         if (!special && ch == "\\") {
1269             special = true;
1270         }
1271         else if (special) {
1272             special = false;
1273             regex += String.escape(ch);
1274         }
1275         else {
1276             var obj = Date.formatCodeToRegex(ch, currentGroup);
1277             currentGroup += obj.g;
1278             regex += obj.s;
1279             if (obj.g && obj.c) {
1280                 code += obj.c;
1281             }
1282         }
1283     }
1284
1285     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1286         + "{v = new Date(y, m, d, h, i, s);}\n"
1287         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1288         + "{v = new Date(y, m, d, h, i);}\n"
1289         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1290         + "{v = new Date(y, m, d, h);}\n"
1291         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1292         + "{v = new Date(y, m, d);}\n"
1293         + "else if (y >= 0 && m >= 0)\n"
1294         + "{v = new Date(y, m);}\n"
1295         + "else if (y >= 0)\n"
1296         + "{v = new Date(y);}\n"
1297         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1298         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1299         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1300         + ";}";
1301
1302     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1303     /** eval:var:zzzzzzzzzzzzz */
1304     eval(code);
1305 };
1306
1307 // private
1308 Date.formatCodeToRegex = function(character, currentGroup) {
1309     switch (character) {
1310     case "D":
1311         return {g:0,
1312         c:null,
1313         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1314     case "j":
1315         return {g:1,
1316             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1317             s:"(\\d{1,2})"}; // day of month without leading zeroes
1318     case "d":
1319         return {g:1,
1320             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1321             s:"(\\d{2})"}; // day of month with leading zeroes
1322     case "l":
1323         return {g:0,
1324             c:null,
1325             s:"(?:" + Date.dayNames.join("|") + ")"};
1326     case "S":
1327         return {g:0,
1328             c:null,
1329             s:"(?:st|nd|rd|th)"};
1330     case "w":
1331         return {g:0,
1332             c:null,
1333             s:"\\d"};
1334     case "z":
1335         return {g:0,
1336             c:null,
1337             s:"(?:\\d{1,3})"};
1338     case "W":
1339         return {g:0,
1340             c:null,
1341             s:"(?:\\d{2})"};
1342     case "F":
1343         return {g:1,
1344             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1345             s:"(" + Date.monthNames.join("|") + ")"};
1346     case "M":
1347         return {g:1,
1348             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1349             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1350     case "n":
1351         return {g:1,
1352             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1353             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1354     case "m":
1355         return {g:1,
1356             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1357             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1358     case "t":
1359         return {g:0,
1360             c:null,
1361             s:"\\d{1,2}"};
1362     case "L":
1363         return {g:0,
1364             c:null,
1365             s:"(?:1|0)"};
1366     case "Y":
1367         return {g:1,
1368             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1369             s:"(\\d{4})"};
1370     case "y":
1371         return {g:1,
1372             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1373                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1374             s:"(\\d{1,2})"};
1375     case "a":
1376         return {g:1,
1377             c:"if (results[" + currentGroup + "] == 'am') {\n"
1378                 + "if (h == 12) { h = 0; }\n"
1379                 + "} else { if (h < 12) { h += 12; }}",
1380             s:"(am|pm)"};
1381     case "A":
1382         return {g:1,
1383             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1384                 + "if (h == 12) { h = 0; }\n"
1385                 + "} else { if (h < 12) { h += 12; }}",
1386             s:"(AM|PM)"};
1387     case "g":
1388     case "G":
1389         return {g:1,
1390             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1391             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1392     case "h":
1393     case "H":
1394         return {g:1,
1395             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1396             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1397     case "i":
1398         return {g:1,
1399             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1400             s:"(\\d{2})"};
1401     case "s":
1402         return {g:1,
1403             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1404             s:"(\\d{2})"};
1405     case "O":
1406         return {g:1,
1407             c:[
1408                 "o = results[", currentGroup, "];\n",
1409                 "var sn = o.substring(0,1);\n", // get + / - sign
1410                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1411                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1412                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1413                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1414             ].join(""),
1415             s:"([+\-]\\d{2,4})"};
1416     
1417     
1418     case "P":
1419         return {g:1,
1420                 c:[
1421                    "o = results[", currentGroup, "];\n",
1422                    "var sn = o.substring(0,1);\n",
1423                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1424                    "var mn = o.substring(4,6) % 60;\n",
1425                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1426                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1427             ].join(""),
1428             s:"([+\-]\\d{4})"};
1429     case "T":
1430         return {g:0,
1431             c:null,
1432             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1433     case "Z":
1434         return {g:1,
1435             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1436                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1437             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1438     default:
1439         return {g:0,
1440             c:null,
1441             s:String.escape(character)};
1442     }
1443 };
1444
1445 /**
1446  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1447  * @return {String} The abbreviated timezone name (e.g. 'CST')
1448  */
1449 Date.prototype.getTimezone = function() {
1450     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1451 };
1452
1453 /**
1454  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1455  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1456  */
1457 Date.prototype.getGMTOffset = function() {
1458     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1459         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1460         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1461 };
1462
1463 /**
1464  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1465  * @return {String} 2-characters representing hours and 2-characters representing minutes
1466  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1467  */
1468 Date.prototype.getGMTColonOffset = function() {
1469         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1470                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1471                 + ":"
1472                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1473 }
1474
1475 /**
1476  * Get the numeric day number of the year, adjusted for leap year.
1477  * @return {Number} 0 through 364 (365 in leap years)
1478  */
1479 Date.prototype.getDayOfYear = function() {
1480     var num = 0;
1481     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1482     for (var i = 0; i < this.getMonth(); ++i) {
1483         num += Date.daysInMonth[i];
1484     }
1485     return num + this.getDate() - 1;
1486 };
1487
1488 /**
1489  * Get the string representation of the numeric week number of the year
1490  * (equivalent to the format specifier 'W').
1491  * @return {String} '00' through '52'
1492  */
1493 Date.prototype.getWeekOfYear = function() {
1494     // Skip to Thursday of this week
1495     var now = this.getDayOfYear() + (4 - this.getDay());
1496     // Find the first Thursday of the year
1497     var jan1 = new Date(this.getFullYear(), 0, 1);
1498     var then = (7 - jan1.getDay() + 4);
1499     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1500 };
1501
1502 /**
1503  * Whether or not the current date is in a leap year.
1504  * @return {Boolean} True if the current date is in a leap year, else false
1505  */
1506 Date.prototype.isLeapYear = function() {
1507     var year = this.getFullYear();
1508     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1509 };
1510
1511 /**
1512  * Get the first day of the current month, adjusted for leap year.  The returned value
1513  * is the numeric day index within the week (0-6) which can be used in conjunction with
1514  * the {@link #monthNames} array to retrieve the textual day name.
1515  * Example:
1516  *<pre><code>
1517 var dt = new Date('1/10/2007');
1518 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1519 </code></pre>
1520  * @return {Number} The day number (0-6)
1521  */
1522 Date.prototype.getFirstDayOfMonth = function() {
1523     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1524     return (day < 0) ? (day + 7) : day;
1525 };
1526
1527 /**
1528  * Get the last day of the current month, adjusted for leap year.  The returned value
1529  * is the numeric day index within the week (0-6) which can be used in conjunction with
1530  * the {@link #monthNames} array to retrieve the textual day name.
1531  * Example:
1532  *<pre><code>
1533 var dt = new Date('1/10/2007');
1534 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1535 </code></pre>
1536  * @return {Number} The day number (0-6)
1537  */
1538 Date.prototype.getLastDayOfMonth = function() {
1539     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1540     return (day < 0) ? (day + 7) : day;
1541 };
1542
1543
1544 /**
1545  * Get the first date of this date's month
1546  * @return {Date}
1547  */
1548 Date.prototype.getFirstDateOfMonth = function() {
1549     return new Date(this.getFullYear(), this.getMonth(), 1);
1550 };
1551
1552 /**
1553  * Get the last date of this date's month
1554  * @return {Date}
1555  */
1556 Date.prototype.getLastDateOfMonth = function() {
1557     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1558 };
1559 /**
1560  * Get the number of days in the current month, adjusted for leap year.
1561  * @return {Number} The number of days in the month
1562  */
1563 Date.prototype.getDaysInMonth = function() {
1564     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1565     return Date.daysInMonth[this.getMonth()];
1566 };
1567
1568 /**
1569  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1570  * @return {String} 'st, 'nd', 'rd' or 'th'
1571  */
1572 Date.prototype.getSuffix = function() {
1573     switch (this.getDate()) {
1574         case 1:
1575         case 21:
1576         case 31:
1577             return "st";
1578         case 2:
1579         case 22:
1580             return "nd";
1581         case 3:
1582         case 23:
1583             return "rd";
1584         default:
1585             return "th";
1586     }
1587 };
1588
1589 // private
1590 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1591
1592 /**
1593  * An array of textual month names.
1594  * Override these values for international dates, for example...
1595  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1596  * @type Array
1597  * @static
1598  */
1599 Date.monthNames =
1600    ["January",
1601     "February",
1602     "March",
1603     "April",
1604     "May",
1605     "June",
1606     "July",
1607     "August",
1608     "September",
1609     "October",
1610     "November",
1611     "December"];
1612
1613 /**
1614  * An array of textual day names.
1615  * Override these values for international dates, for example...
1616  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1617  * @type Array
1618  * @static
1619  */
1620 Date.dayNames =
1621    ["Sunday",
1622     "Monday",
1623     "Tuesday",
1624     "Wednesday",
1625     "Thursday",
1626     "Friday",
1627     "Saturday"];
1628
1629 // private
1630 Date.y2kYear = 50;
1631 // private
1632 Date.monthNumbers = {
1633     Jan:0,
1634     Feb:1,
1635     Mar:2,
1636     Apr:3,
1637     May:4,
1638     Jun:5,
1639     Jul:6,
1640     Aug:7,
1641     Sep:8,
1642     Oct:9,
1643     Nov:10,
1644     Dec:11};
1645
1646 /**
1647  * Creates and returns a new Date instance with the exact same date value as the called instance.
1648  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1649  * variable will also be changed.  When the intention is to create a new variable that will not
1650  * modify the original instance, you should create a clone.
1651  *
1652  * Example of correctly cloning a date:
1653  * <pre><code>
1654 //wrong way:
1655 var orig = new Date('10/1/2006');
1656 var copy = orig;
1657 copy.setDate(5);
1658 document.write(orig);  //returns 'Thu Oct 05 2006'!
1659
1660 //correct way:
1661 var orig = new Date('10/1/2006');
1662 var copy = orig.clone();
1663 copy.setDate(5);
1664 document.write(orig);  //returns 'Thu Oct 01 2006'
1665 </code></pre>
1666  * @return {Date} The new Date instance
1667  */
1668 Date.prototype.clone = function() {
1669         return new Date(this.getTime());
1670 };
1671
1672 /**
1673  * Clears any time information from this date
1674  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1675  @return {Date} this or the clone
1676  */
1677 Date.prototype.clearTime = function(clone){
1678     if(clone){
1679         return this.clone().clearTime();
1680     }
1681     this.setHours(0);
1682     this.setMinutes(0);
1683     this.setSeconds(0);
1684     this.setMilliseconds(0);
1685     return this;
1686 };
1687
1688 // private
1689 // safari setMonth is broken
1690 if(Roo.isSafari){
1691     Date.brokenSetMonth = Date.prototype.setMonth;
1692         Date.prototype.setMonth = function(num){
1693                 if(num <= -1){
1694                         var n = Math.ceil(-num);
1695                         var back_year = Math.ceil(n/12);
1696                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1697                         this.setFullYear(this.getFullYear() - back_year);
1698                         return Date.brokenSetMonth.call(this, month);
1699                 } else {
1700                         return Date.brokenSetMonth.apply(this, arguments);
1701                 }
1702         };
1703 }
1704
1705 /** Date interval constant 
1706 * @static 
1707 * @type String */
1708 Date.MILLI = "ms";
1709 /** Date interval constant 
1710 * @static 
1711 * @type String */
1712 Date.SECOND = "s";
1713 /** Date interval constant 
1714 * @static 
1715 * @type String */
1716 Date.MINUTE = "mi";
1717 /** Date interval constant 
1718 * @static 
1719 * @type String */
1720 Date.HOUR = "h";
1721 /** Date interval constant 
1722 * @static 
1723 * @type String */
1724 Date.DAY = "d";
1725 /** Date interval constant 
1726 * @static 
1727 * @type String */
1728 Date.MONTH = "mo";
1729 /** Date interval constant 
1730 * @static 
1731 * @type String */
1732 Date.YEAR = "y";
1733
1734 /**
1735  * Provides a convenient method of performing basic date arithmetic.  This method
1736  * does not modify the Date instance being called - it creates and returns
1737  * a new Date instance containing the resulting date value.
1738  *
1739  * Examples:
1740  * <pre><code>
1741 //Basic usage:
1742 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1743 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1744
1745 //Negative values will subtract correctly:
1746 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1747 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1748
1749 //You can even chain several calls together in one line!
1750 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1751 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1752  </code></pre>
1753  *
1754  * @param {String} interval   A valid date interval enum value
1755  * @param {Number} value      The amount to add to the current date
1756  * @return {Date} The new Date instance
1757  */
1758 Date.prototype.add = function(interval, value){
1759   var d = this.clone();
1760   if (!interval || value === 0) return d;
1761   switch(interval.toLowerCase()){
1762     case Date.MILLI:
1763       d.setMilliseconds(this.getMilliseconds() + value);
1764       break;
1765     case Date.SECOND:
1766       d.setSeconds(this.getSeconds() + value);
1767       break;
1768     case Date.MINUTE:
1769       d.setMinutes(this.getMinutes() + value);
1770       break;
1771     case Date.HOUR:
1772       d.setHours(this.getHours() + value);
1773       break;
1774     case Date.DAY:
1775       d.setDate(this.getDate() + value);
1776       break;
1777     case Date.MONTH:
1778       var day = this.getDate();
1779       if(day > 28){
1780           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1781       }
1782       d.setDate(day);
1783       d.setMonth(this.getMonth() + value);
1784       break;
1785     case Date.YEAR:
1786       d.setFullYear(this.getFullYear() + value);
1787       break;
1788   }
1789   return d;
1790 };
1791 /*
1792  * Based on:
1793  * Ext JS Library 1.1.1
1794  * Copyright(c) 2006-2007, Ext JS, LLC.
1795  *
1796  * Originally Released Under LGPL - original licence link has changed is not relivant.
1797  *
1798  * Fork - LGPL
1799  * <script type="text/javascript">
1800  */
1801
1802 /**
1803  * @class Roo.lib.Dom
1804  * @static
1805  * 
1806  * Dom utils (from YIU afaik)
1807  * 
1808  **/
1809 Roo.lib.Dom = {
1810     /**
1811      * Get the view width
1812      * @param {Boolean} full True will get the full document, otherwise it's the view width
1813      * @return {Number} The width
1814      */
1815      
1816     getViewWidth : function(full) {
1817         return full ? this.getDocumentWidth() : this.getViewportWidth();
1818     },
1819     /**
1820      * Get the view height
1821      * @param {Boolean} full True will get the full document, otherwise it's the view height
1822      * @return {Number} The height
1823      */
1824     getViewHeight : function(full) {
1825         return full ? this.getDocumentHeight() : this.getViewportHeight();
1826     },
1827
1828     getDocumentHeight: function() {
1829         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1830         return Math.max(scrollHeight, this.getViewportHeight());
1831     },
1832
1833     getDocumentWidth: function() {
1834         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1835         return Math.max(scrollWidth, this.getViewportWidth());
1836     },
1837
1838     getViewportHeight: function() {
1839         var height = self.innerHeight;
1840         var mode = document.compatMode;
1841
1842         if ((mode || Roo.isIE) && !Roo.isOpera) {
1843             height = (mode == "CSS1Compat") ?
1844                      document.documentElement.clientHeight :
1845                      document.body.clientHeight;
1846         }
1847
1848         return height;
1849     },
1850
1851     getViewportWidth: function() {
1852         var width = self.innerWidth;
1853         var mode = document.compatMode;
1854
1855         if (mode || Roo.isIE) {
1856             width = (mode == "CSS1Compat") ?
1857                     document.documentElement.clientWidth :
1858                     document.body.clientWidth;
1859         }
1860         return width;
1861     },
1862
1863     isAncestor : function(p, c) {
1864         p = Roo.getDom(p);
1865         c = Roo.getDom(c);
1866         if (!p || !c) {
1867             return false;
1868         }
1869
1870         if (p.contains && !Roo.isSafari) {
1871             return p.contains(c);
1872         } else if (p.compareDocumentPosition) {
1873             return !!(p.compareDocumentPosition(c) & 16);
1874         } else {
1875             var parent = c.parentNode;
1876             while (parent) {
1877                 if (parent == p) {
1878                     return true;
1879                 }
1880                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1881                     return false;
1882                 }
1883                 parent = parent.parentNode;
1884             }
1885             return false;
1886         }
1887     },
1888
1889     getRegion : function(el) {
1890         return Roo.lib.Region.getRegion(el);
1891     },
1892
1893     getY : function(el) {
1894         return this.getXY(el)[1];
1895     },
1896
1897     getX : function(el) {
1898         return this.getXY(el)[0];
1899     },
1900
1901     getXY : function(el) {
1902         var p, pe, b, scroll, bd = document.body;
1903         el = Roo.getDom(el);
1904         var fly = Roo.lib.AnimBase.fly;
1905         if (el.getBoundingClientRect) {
1906             b = el.getBoundingClientRect();
1907             scroll = fly(document).getScroll();
1908             return [b.left + scroll.left, b.top + scroll.top];
1909         }
1910         var x = 0, y = 0;
1911
1912         p = el;
1913
1914         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1915
1916         while (p) {
1917
1918             x += p.offsetLeft;
1919             y += p.offsetTop;
1920
1921             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1922                 hasAbsolute = true;
1923             }
1924
1925             if (Roo.isGecko) {
1926                 pe = fly(p);
1927
1928                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1929                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1930
1931
1932                 x += bl;
1933                 y += bt;
1934
1935
1936                 if (p != el && pe.getStyle('overflow') != 'visible') {
1937                     x += bl;
1938                     y += bt;
1939                 }
1940             }
1941             p = p.offsetParent;
1942         }
1943
1944         if (Roo.isSafari && hasAbsolute) {
1945             x -= bd.offsetLeft;
1946             y -= bd.offsetTop;
1947         }
1948
1949         if (Roo.isGecko && !hasAbsolute) {
1950             var dbd = fly(bd);
1951             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1952             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1953         }
1954
1955         p = el.parentNode;
1956         while (p && p != bd) {
1957             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1958                 x -= p.scrollLeft;
1959                 y -= p.scrollTop;
1960             }
1961             p = p.parentNode;
1962         }
1963         return [x, y];
1964     },
1965  
1966   
1967
1968
1969     setXY : function(el, xy) {
1970         el = Roo.fly(el, '_setXY');
1971         el.position();
1972         var pts = el.translatePoints(xy);
1973         if (xy[0] !== false) {
1974             el.dom.style.left = pts.left + "px";
1975         }
1976         if (xy[1] !== false) {
1977             el.dom.style.top = pts.top + "px";
1978         }
1979     },
1980
1981     setX : function(el, x) {
1982         this.setXY(el, [x, false]);
1983     },
1984
1985     setY : function(el, y) {
1986         this.setXY(el, [false, y]);
1987     }
1988 };
1989 /*
1990  * Portions of this file are based on pieces of Yahoo User Interface Library
1991  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1992  * YUI licensed under the BSD License:
1993  * http://developer.yahoo.net/yui/license.txt
1994  * <script type="text/javascript">
1995  *
1996  */
1997
1998 Roo.lib.Event = function() {
1999     var loadComplete = false;
2000     var listeners = [];
2001     var unloadListeners = [];
2002     var retryCount = 0;
2003     var onAvailStack = [];
2004     var counter = 0;
2005     var lastError = null;
2006
2007     return {
2008         POLL_RETRYS: 200,
2009         POLL_INTERVAL: 20,
2010         EL: 0,
2011         TYPE: 1,
2012         FN: 2,
2013         WFN: 3,
2014         OBJ: 3,
2015         ADJ_SCOPE: 4,
2016         _interval: null,
2017
2018         startInterval: function() {
2019             if (!this._interval) {
2020                 var self = this;
2021                 var callback = function() {
2022                     self._tryPreloadAttach();
2023                 };
2024                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2025
2026             }
2027         },
2028
2029         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2030             onAvailStack.push({ id:         p_id,
2031                 fn:         p_fn,
2032                 obj:        p_obj,
2033                 override:   p_override,
2034                 checkReady: false    });
2035
2036             retryCount = this.POLL_RETRYS;
2037             this.startInterval();
2038         },
2039
2040
2041         addListener: function(el, eventName, fn) {
2042             el = Roo.getDom(el);
2043             if (!el || !fn) {
2044                 return false;
2045             }
2046
2047             if ("unload" == eventName) {
2048                 unloadListeners[unloadListeners.length] =
2049                 [el, eventName, fn];
2050                 return true;
2051             }
2052
2053             var wrappedFn = function(e) {
2054                 return fn(Roo.lib.Event.getEvent(e));
2055             };
2056
2057             var li = [el, eventName, fn, wrappedFn];
2058
2059             var index = listeners.length;
2060             listeners[index] = li;
2061
2062             this.doAdd(el, eventName, wrappedFn, false);
2063             return true;
2064
2065         },
2066
2067
2068         removeListener: function(el, eventName, fn) {
2069             var i, len;
2070
2071             el = Roo.getDom(el);
2072
2073             if(!fn) {
2074                 return this.purgeElement(el, false, eventName);
2075             }
2076
2077
2078             if ("unload" == eventName) {
2079
2080                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2081                     var li = unloadListeners[i];
2082                     if (li &&
2083                         li[0] == el &&
2084                         li[1] == eventName &&
2085                         li[2] == fn) {
2086                         unloadListeners.splice(i, 1);
2087                         return true;
2088                     }
2089                 }
2090
2091                 return false;
2092             }
2093
2094             var cacheItem = null;
2095
2096
2097             var index = arguments[3];
2098
2099             if ("undefined" == typeof index) {
2100                 index = this._getCacheIndex(el, eventName, fn);
2101             }
2102
2103             if (index >= 0) {
2104                 cacheItem = listeners[index];
2105             }
2106
2107             if (!el || !cacheItem) {
2108                 return false;
2109             }
2110
2111             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2112
2113             delete listeners[index][this.WFN];
2114             delete listeners[index][this.FN];
2115             listeners.splice(index, 1);
2116
2117             return true;
2118
2119         },
2120
2121
2122         getTarget: function(ev, resolveTextNode) {
2123             ev = ev.browserEvent || ev;
2124             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2125             var t = ev.target || ev.srcElement;
2126             return this.resolveTextNode(t);
2127         },
2128
2129
2130         resolveTextNode: function(node) {
2131             if (Roo.isSafari && node && 3 == node.nodeType) {
2132                 return node.parentNode;
2133             } else {
2134                 return node;
2135             }
2136         },
2137
2138
2139         getPageX: function(ev) {
2140             ev = ev.browserEvent || ev;
2141             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2142             var x = ev.pageX;
2143             if (!x && 0 !== x) {
2144                 x = ev.clientX || 0;
2145
2146                 if (Roo.isIE) {
2147                     x += this.getScroll()[1];
2148                 }
2149             }
2150
2151             return x;
2152         },
2153
2154
2155         getPageY: function(ev) {
2156             ev = ev.browserEvent || ev;
2157             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2158             var y = ev.pageY;
2159             if (!y && 0 !== y) {
2160                 y = ev.clientY || 0;
2161
2162                 if (Roo.isIE) {
2163                     y += this.getScroll()[0];
2164                 }
2165             }
2166
2167
2168             return y;
2169         },
2170
2171
2172         getXY: function(ev) {
2173             ev = ev.browserEvent || ev;
2174             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2175             return [this.getPageX(ev), this.getPageY(ev)];
2176         },
2177
2178
2179         getRelatedTarget: function(ev) {
2180             ev = ev.browserEvent || ev;
2181             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2182             var t = ev.relatedTarget;
2183             if (!t) {
2184                 if (ev.type == "mouseout") {
2185                     t = ev.toElement;
2186                 } else if (ev.type == "mouseover") {
2187                     t = ev.fromElement;
2188                 }
2189             }
2190
2191             return this.resolveTextNode(t);
2192         },
2193
2194
2195         getTime: function(ev) {
2196             ev = ev.browserEvent || ev;
2197             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2198             if (!ev.time) {
2199                 var t = new Date().getTime();
2200                 try {
2201                     ev.time = t;
2202                 } catch(ex) {
2203                     this.lastError = ex;
2204                     return t;
2205                 }
2206             }
2207
2208             return ev.time;
2209         },
2210
2211
2212         stopEvent: function(ev) {
2213             this.stopPropagation(ev);
2214             this.preventDefault(ev);
2215         },
2216
2217
2218         stopPropagation: function(ev) {
2219             ev = ev.browserEvent || ev;
2220             if (ev.stopPropagation) {
2221                 ev.stopPropagation();
2222             } else {
2223                 ev.cancelBubble = true;
2224             }
2225         },
2226
2227
2228         preventDefault: function(ev) {
2229             ev = ev.browserEvent || ev;
2230             if(ev.preventDefault) {
2231                 ev.preventDefault();
2232             } else {
2233                 ev.returnValue = false;
2234             }
2235         },
2236
2237
2238         getEvent: function(e) {
2239             var ev = e || window.event;
2240             if (!ev) {
2241                 var c = this.getEvent.caller;
2242                 while (c) {
2243                     ev = c.arguments[0];
2244                     if (ev && Event == ev.constructor) {
2245                         break;
2246                     }
2247                     c = c.caller;
2248                 }
2249             }
2250             return ev;
2251         },
2252
2253
2254         getCharCode: function(ev) {
2255             ev = ev.browserEvent || ev;
2256             return ev.charCode || ev.keyCode || 0;
2257         },
2258
2259
2260         _getCacheIndex: function(el, eventName, fn) {
2261             for (var i = 0,len = listeners.length; i < len; ++i) {
2262                 var li = listeners[i];
2263                 if (li &&
2264                     li[this.FN] == fn &&
2265                     li[this.EL] == el &&
2266                     li[this.TYPE] == eventName) {
2267                     return i;
2268                 }
2269             }
2270
2271             return -1;
2272         },
2273
2274
2275         elCache: {},
2276
2277
2278         getEl: function(id) {
2279             return document.getElementById(id);
2280         },
2281
2282
2283         clearCache: function() {
2284         },
2285
2286
2287         _load: function(e) {
2288             loadComplete = true;
2289             var EU = Roo.lib.Event;
2290
2291
2292             if (Roo.isIE) {
2293                 EU.doRemove(window, "load", EU._load);
2294             }
2295         },
2296
2297
2298         _tryPreloadAttach: function() {
2299
2300             if (this.locked) {
2301                 return false;
2302             }
2303
2304             this.locked = true;
2305
2306
2307             var tryAgain = !loadComplete;
2308             if (!tryAgain) {
2309                 tryAgain = (retryCount > 0);
2310             }
2311
2312
2313             var notAvail = [];
2314             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2315                 var item = onAvailStack[i];
2316                 if (item) {
2317                     var el = this.getEl(item.id);
2318
2319                     if (el) {
2320                         if (!item.checkReady ||
2321                             loadComplete ||
2322                             el.nextSibling ||
2323                             (document && document.body)) {
2324
2325                             var scope = el;
2326                             if (item.override) {
2327                                 if (item.override === true) {
2328                                     scope = item.obj;
2329                                 } else {
2330                                     scope = item.override;
2331                                 }
2332                             }
2333                             item.fn.call(scope, item.obj);
2334                             onAvailStack[i] = null;
2335                         }
2336                     } else {
2337                         notAvail.push(item);
2338                     }
2339                 }
2340             }
2341
2342             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2343
2344             if (tryAgain) {
2345
2346                 this.startInterval();
2347             } else {
2348                 clearInterval(this._interval);
2349                 this._interval = null;
2350             }
2351
2352             this.locked = false;
2353
2354             return true;
2355
2356         },
2357
2358
2359         purgeElement: function(el, recurse, eventName) {
2360             var elListeners = this.getListeners(el, eventName);
2361             if (elListeners) {
2362                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2363                     var l = elListeners[i];
2364                     this.removeListener(el, l.type, l.fn);
2365                 }
2366             }
2367
2368             if (recurse && el && el.childNodes) {
2369                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2370                     this.purgeElement(el.childNodes[i], recurse, eventName);
2371                 }
2372             }
2373         },
2374
2375
2376         getListeners: function(el, eventName) {
2377             var results = [], searchLists;
2378             if (!eventName) {
2379                 searchLists = [listeners, unloadListeners];
2380             } else if (eventName == "unload") {
2381                 searchLists = [unloadListeners];
2382             } else {
2383                 searchLists = [listeners];
2384             }
2385
2386             for (var j = 0; j < searchLists.length; ++j) {
2387                 var searchList = searchLists[j];
2388                 if (searchList && searchList.length > 0) {
2389                     for (var i = 0,len = searchList.length; i < len; ++i) {
2390                         var l = searchList[i];
2391                         if (l && l[this.EL] === el &&
2392                             (!eventName || eventName === l[this.TYPE])) {
2393                             results.push({
2394                                 type:   l[this.TYPE],
2395                                 fn:     l[this.FN],
2396                                 obj:    l[this.OBJ],
2397                                 adjust: l[this.ADJ_SCOPE],
2398                                 index:  i
2399                             });
2400                         }
2401                     }
2402                 }
2403             }
2404
2405             return (results.length) ? results : null;
2406         },
2407
2408
2409         _unload: function(e) {
2410
2411             var EU = Roo.lib.Event, i, j, l, len, index;
2412
2413             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2414                 l = unloadListeners[i];
2415                 if (l) {
2416                     var scope = window;
2417                     if (l[EU.ADJ_SCOPE]) {
2418                         if (l[EU.ADJ_SCOPE] === true) {
2419                             scope = l[EU.OBJ];
2420                         } else {
2421                             scope = l[EU.ADJ_SCOPE];
2422                         }
2423                     }
2424                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2425                     unloadListeners[i] = null;
2426                     l = null;
2427                     scope = null;
2428                 }
2429             }
2430
2431             unloadListeners = null;
2432
2433             if (listeners && listeners.length > 0) {
2434                 j = listeners.length;
2435                 while (j) {
2436                     index = j - 1;
2437                     l = listeners[index];
2438                     if (l) {
2439                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2440                                 l[EU.FN], index);
2441                     }
2442                     j = j - 1;
2443                 }
2444                 l = null;
2445
2446                 EU.clearCache();
2447             }
2448
2449             EU.doRemove(window, "unload", EU._unload);
2450
2451         },
2452
2453
2454         getScroll: function() {
2455             var dd = document.documentElement, db = document.body;
2456             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2457                 return [dd.scrollTop, dd.scrollLeft];
2458             } else if (db) {
2459                 return [db.scrollTop, db.scrollLeft];
2460             } else {
2461                 return [0, 0];
2462             }
2463         },
2464
2465
2466         doAdd: function () {
2467             if (window.addEventListener) {
2468                 return function(el, eventName, fn, capture) {
2469                     el.addEventListener(eventName, fn, (capture));
2470                 };
2471             } else if (window.attachEvent) {
2472                 return function(el, eventName, fn, capture) {
2473                     el.attachEvent("on" + eventName, fn);
2474                 };
2475             } else {
2476                 return function() {
2477                 };
2478             }
2479         }(),
2480
2481
2482         doRemove: function() {
2483             if (window.removeEventListener) {
2484                 return function (el, eventName, fn, capture) {
2485                     el.removeEventListener(eventName, fn, (capture));
2486                 };
2487             } else if (window.detachEvent) {
2488                 return function (el, eventName, fn) {
2489                     el.detachEvent("on" + eventName, fn);
2490                 };
2491             } else {
2492                 return function() {
2493                 };
2494             }
2495         }()
2496     };
2497     
2498 }();
2499 (function() {     
2500    
2501     var E = Roo.lib.Event;
2502     E.on = E.addListener;
2503     E.un = E.removeListener;
2504
2505     if (document && document.body) {
2506         E._load();
2507     } else {
2508         E.doAdd(window, "load", E._load);
2509     }
2510     E.doAdd(window, "unload", E._unload);
2511     E._tryPreloadAttach();
2512 })();
2513
2514 /*
2515  * Portions of this file are based on pieces of Yahoo User Interface Library
2516  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2517  * YUI licensed under the BSD License:
2518  * http://developer.yahoo.net/yui/license.txt
2519  * <script type="text/javascript">
2520  *
2521  */
2522
2523 (function() {
2524     /**
2525      * @class Roo.lib.Ajax
2526      *
2527      */
2528     Roo.lib.Ajax = {
2529         /**
2530          * @static 
2531          */
2532         request : function(method, uri, cb, data, options) {
2533             if(options){
2534                 var hs = options.headers;
2535                 if(hs){
2536                     for(var h in hs){
2537                         if(hs.hasOwnProperty(h)){
2538                             this.initHeader(h, hs[h], false);
2539                         }
2540                     }
2541                 }
2542                 if(options.xmlData){
2543                     this.initHeader('Content-Type', 'text/xml', false);
2544                     method = 'POST';
2545                     data = options.xmlData;
2546                 }
2547             }
2548
2549             return this.asyncRequest(method, uri, cb, data);
2550         },
2551
2552         serializeForm : function(form) {
2553             if(typeof form == 'string') {
2554                 form = (document.getElementById(form) || document.forms[form]);
2555             }
2556
2557             var el, name, val, disabled, data = '', hasSubmit = false;
2558             for (var i = 0; i < form.elements.length; i++) {
2559                 el = form.elements[i];
2560                 disabled = form.elements[i].disabled;
2561                 name = form.elements[i].name;
2562                 val = form.elements[i].value;
2563
2564                 if (!disabled && name){
2565                     switch (el.type)
2566                             {
2567                         case 'select-one':
2568                         case 'select-multiple':
2569                             for (var j = 0; j < el.options.length; j++) {
2570                                 if (el.options[j].selected) {
2571                                     if (Roo.isIE) {
2572                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2573                                     }
2574                                     else {
2575                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2576                                     }
2577                                 }
2578                             }
2579                             break;
2580                         case 'radio':
2581                         case 'checkbox':
2582                             if (el.checked) {
2583                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2584                             }
2585                             break;
2586                         case 'file':
2587
2588                         case undefined:
2589
2590                         case 'reset':
2591
2592                         case 'button':
2593
2594                             break;
2595                         case 'submit':
2596                             if(hasSubmit == false) {
2597                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2598                                 hasSubmit = true;
2599                             }
2600                             break;
2601                         default:
2602                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2603                             break;
2604                     }
2605                 }
2606             }
2607             data = data.substr(0, data.length - 1);
2608             return data;
2609         },
2610
2611         headers:{},
2612
2613         hasHeaders:false,
2614
2615         useDefaultHeader:true,
2616
2617         defaultPostHeader:'application/x-www-form-urlencoded',
2618
2619         useDefaultXhrHeader:true,
2620
2621         defaultXhrHeader:'XMLHttpRequest',
2622
2623         hasDefaultHeaders:true,
2624
2625         defaultHeaders:{},
2626
2627         poll:{},
2628
2629         timeout:{},
2630
2631         pollInterval:50,
2632
2633         transactionId:0,
2634
2635         setProgId:function(id)
2636         {
2637             this.activeX.unshift(id);
2638         },
2639
2640         setDefaultPostHeader:function(b)
2641         {
2642             this.useDefaultHeader = b;
2643         },
2644
2645         setDefaultXhrHeader:function(b)
2646         {
2647             this.useDefaultXhrHeader = b;
2648         },
2649
2650         setPollingInterval:function(i)
2651         {
2652             if (typeof i == 'number' && isFinite(i)) {
2653                 this.pollInterval = i;
2654             }
2655         },
2656
2657         createXhrObject:function(transactionId)
2658         {
2659             var obj,http;
2660             try
2661             {
2662
2663                 http = new XMLHttpRequest();
2664
2665                 obj = { conn:http, tId:transactionId };
2666             }
2667             catch(e)
2668             {
2669                 for (var i = 0; i < this.activeX.length; ++i) {
2670                     try
2671                     {
2672
2673                         http = new ActiveXObject(this.activeX[i]);
2674
2675                         obj = { conn:http, tId:transactionId };
2676                         break;
2677                     }
2678                     catch(e) {
2679                     }
2680                 }
2681             }
2682             finally
2683             {
2684                 return obj;
2685             }
2686         },
2687
2688         getConnectionObject:function()
2689         {
2690             var o;
2691             var tId = this.transactionId;
2692
2693             try
2694             {
2695                 o = this.createXhrObject(tId);
2696                 if (o) {
2697                     this.transactionId++;
2698                 }
2699             }
2700             catch(e) {
2701             }
2702             finally
2703             {
2704                 return o;
2705             }
2706         },
2707
2708         asyncRequest:function(method, uri, callback, postData)
2709         {
2710             var o = this.getConnectionObject();
2711
2712             if (!o) {
2713                 return null;
2714             }
2715             else {
2716                 o.conn.open(method, uri, true);
2717
2718                 if (this.useDefaultXhrHeader) {
2719                     if (!this.defaultHeaders['X-Requested-With']) {
2720                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2721                     }
2722                 }
2723
2724                 if(postData && this.useDefaultHeader){
2725                     this.initHeader('Content-Type', this.defaultPostHeader);
2726                 }
2727
2728                  if (this.hasDefaultHeaders || this.hasHeaders) {
2729                     this.setHeader(o);
2730                 }
2731
2732                 this.handleReadyState(o, callback);
2733                 o.conn.send(postData || null);
2734
2735                 return o;
2736             }
2737         },
2738
2739         handleReadyState:function(o, callback)
2740         {
2741             var oConn = this;
2742
2743             if (callback && callback.timeout) {
2744                 
2745                 this.timeout[o.tId] = window.setTimeout(function() {
2746                     oConn.abort(o, callback, true);
2747                 }, callback.timeout);
2748             }
2749
2750             this.poll[o.tId] = window.setInterval(
2751                     function() {
2752                         if (o.conn && o.conn.readyState == 4) {
2753                             window.clearInterval(oConn.poll[o.tId]);
2754                             delete oConn.poll[o.tId];
2755
2756                             if(callback && callback.timeout) {
2757                                 window.clearTimeout(oConn.timeout[o.tId]);
2758                                 delete oConn.timeout[o.tId];
2759                             }
2760
2761                             oConn.handleTransactionResponse(o, callback);
2762                         }
2763                     }
2764                     , this.pollInterval);
2765         },
2766
2767         handleTransactionResponse:function(o, callback, isAbort)
2768         {
2769
2770             if (!callback) {
2771                 this.releaseObject(o);
2772                 return;
2773             }
2774
2775             var httpStatus, responseObject;
2776
2777             try
2778             {
2779                 if (o.conn.status !== undefined && o.conn.status != 0) {
2780                     httpStatus = o.conn.status;
2781                 }
2782                 else {
2783                     httpStatus = 13030;
2784                 }
2785             }
2786             catch(e) {
2787
2788
2789                 httpStatus = 13030;
2790             }
2791
2792             if (httpStatus >= 200 && httpStatus < 300) {
2793                 responseObject = this.createResponseObject(o, callback.argument);
2794                 if (callback.success) {
2795                     if (!callback.scope) {
2796                         callback.success(responseObject);
2797                     }
2798                     else {
2799
2800
2801                         callback.success.apply(callback.scope, [responseObject]);
2802                     }
2803                 }
2804             }
2805             else {
2806                 switch (httpStatus) {
2807
2808                     case 12002:
2809                     case 12029:
2810                     case 12030:
2811                     case 12031:
2812                     case 12152:
2813                     case 13030:
2814                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2815                         if (callback.failure) {
2816                             if (!callback.scope) {
2817                                 callback.failure(responseObject);
2818                             }
2819                             else {
2820                                 callback.failure.apply(callback.scope, [responseObject]);
2821                             }
2822                         }
2823                         break;
2824                     default:
2825                         responseObject = this.createResponseObject(o, callback.argument);
2826                         if (callback.failure) {
2827                             if (!callback.scope) {
2828                                 callback.failure(responseObject);
2829                             }
2830                             else {
2831                                 callback.failure.apply(callback.scope, [responseObject]);
2832                             }
2833                         }
2834                 }
2835             }
2836
2837             this.releaseObject(o);
2838             responseObject = null;
2839         },
2840
2841         createResponseObject:function(o, callbackArg)
2842         {
2843             var obj = {};
2844             var headerObj = {};
2845
2846             try
2847             {
2848                 var headerStr = o.conn.getAllResponseHeaders();
2849                 var header = headerStr.split('\n');
2850                 for (var i = 0; i < header.length; i++) {
2851                     var delimitPos = header[i].indexOf(':');
2852                     if (delimitPos != -1) {
2853                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2854                     }
2855                 }
2856             }
2857             catch(e) {
2858             }
2859
2860             obj.tId = o.tId;
2861             obj.status = o.conn.status;
2862             obj.statusText = o.conn.statusText;
2863             obj.getResponseHeader = headerObj;
2864             obj.getAllResponseHeaders = headerStr;
2865             obj.responseText = o.conn.responseText;
2866             obj.responseXML = o.conn.responseXML;
2867
2868             if (typeof callbackArg !== undefined) {
2869                 obj.argument = callbackArg;
2870             }
2871
2872             return obj;
2873         },
2874
2875         createExceptionObject:function(tId, callbackArg, isAbort)
2876         {
2877             var COMM_CODE = 0;
2878             var COMM_ERROR = 'communication failure';
2879             var ABORT_CODE = -1;
2880             var ABORT_ERROR = 'transaction aborted';
2881
2882             var obj = {};
2883
2884             obj.tId = tId;
2885             if (isAbort) {
2886                 obj.status = ABORT_CODE;
2887                 obj.statusText = ABORT_ERROR;
2888             }
2889             else {
2890                 obj.status = COMM_CODE;
2891                 obj.statusText = COMM_ERROR;
2892             }
2893
2894             if (callbackArg) {
2895                 obj.argument = callbackArg;
2896             }
2897
2898             return obj;
2899         },
2900
2901         initHeader:function(label, value, isDefault)
2902         {
2903             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2904
2905             if (headerObj[label] === undefined) {
2906                 headerObj[label] = value;
2907             }
2908             else {
2909
2910
2911                 headerObj[label] = value + "," + headerObj[label];
2912             }
2913
2914             if (isDefault) {
2915                 this.hasDefaultHeaders = true;
2916             }
2917             else {
2918                 this.hasHeaders = true;
2919             }
2920         },
2921
2922
2923         setHeader:function(o)
2924         {
2925             if (this.hasDefaultHeaders) {
2926                 for (var prop in this.defaultHeaders) {
2927                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2928                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2929                     }
2930                 }
2931             }
2932
2933             if (this.hasHeaders) {
2934                 for (var prop in this.headers) {
2935                     if (this.headers.hasOwnProperty(prop)) {
2936                         o.conn.setRequestHeader(prop, this.headers[prop]);
2937                     }
2938                 }
2939                 this.headers = {};
2940                 this.hasHeaders = false;
2941             }
2942         },
2943
2944         resetDefaultHeaders:function() {
2945             delete this.defaultHeaders;
2946             this.defaultHeaders = {};
2947             this.hasDefaultHeaders = false;
2948         },
2949
2950         abort:function(o, callback, isTimeout)
2951         {
2952             if(this.isCallInProgress(o)) {
2953                 o.conn.abort();
2954                 window.clearInterval(this.poll[o.tId]);
2955                 delete this.poll[o.tId];
2956                 if (isTimeout) {
2957                     delete this.timeout[o.tId];
2958                 }
2959
2960                 this.handleTransactionResponse(o, callback, true);
2961
2962                 return true;
2963             }
2964             else {
2965                 return false;
2966             }
2967         },
2968
2969
2970         isCallInProgress:function(o)
2971         {
2972             if (o && o.conn) {
2973                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2974             }
2975             else {
2976
2977                 return false;
2978             }
2979         },
2980
2981
2982         releaseObject:function(o)
2983         {
2984
2985             o.conn = null;
2986
2987             o = null;
2988         },
2989
2990         activeX:[
2991         'MSXML2.XMLHTTP.3.0',
2992         'MSXML2.XMLHTTP',
2993         'Microsoft.XMLHTTP'
2994         ]
2995
2996
2997     };
2998 })();/*
2999  * Portions of this file are based on pieces of Yahoo User Interface Library
3000  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3001  * YUI licensed under the BSD License:
3002  * http://developer.yahoo.net/yui/license.txt
3003  * <script type="text/javascript">
3004  *
3005  */
3006
3007 Roo.lib.Region = function(t, r, b, l) {
3008     this.top = t;
3009     this[1] = t;
3010     this.right = r;
3011     this.bottom = b;
3012     this.left = l;
3013     this[0] = l;
3014 };
3015
3016
3017 Roo.lib.Region.prototype = {
3018     contains : function(region) {
3019         return ( region.left >= this.left &&
3020                  region.right <= this.right &&
3021                  region.top >= this.top &&
3022                  region.bottom <= this.bottom    );
3023
3024     },
3025
3026     getArea : function() {
3027         return ( (this.bottom - this.top) * (this.right - this.left) );
3028     },
3029
3030     intersect : function(region) {
3031         var t = Math.max(this.top, region.top);
3032         var r = Math.min(this.right, region.right);
3033         var b = Math.min(this.bottom, region.bottom);
3034         var l = Math.max(this.left, region.left);
3035
3036         if (b >= t && r >= l) {
3037             return new Roo.lib.Region(t, r, b, l);
3038         } else {
3039             return null;
3040         }
3041     },
3042     union : function(region) {
3043         var t = Math.min(this.top, region.top);
3044         var r = Math.max(this.right, region.right);
3045         var b = Math.max(this.bottom, region.bottom);
3046         var l = Math.min(this.left, region.left);
3047
3048         return new Roo.lib.Region(t, r, b, l);
3049     },
3050
3051     adjust : function(t, l, b, r) {
3052         this.top += t;
3053         this.left += l;
3054         this.right += r;
3055         this.bottom += b;
3056         return this;
3057     }
3058 };
3059
3060 Roo.lib.Region.getRegion = function(el) {
3061     var p = Roo.lib.Dom.getXY(el);
3062
3063     var t = p[1];
3064     var r = p[0] + el.offsetWidth;
3065     var b = p[1] + el.offsetHeight;
3066     var l = p[0];
3067
3068     return new Roo.lib.Region(t, r, b, l);
3069 };
3070 /*
3071  * Portions of this file are based on pieces of Yahoo User Interface Library
3072  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3073  * YUI licensed under the BSD License:
3074  * http://developer.yahoo.net/yui/license.txt
3075  * <script type="text/javascript">
3076  *
3077  */
3078 //@@dep Roo.lib.Region
3079
3080
3081 Roo.lib.Point = function(x, y) {
3082     if (x instanceof Array) {
3083         y = x[1];
3084         x = x[0];
3085     }
3086     this.x = this.right = this.left = this[0] = x;
3087     this.y = this.top = this.bottom = this[1] = y;
3088 };
3089
3090 Roo.lib.Point.prototype = new Roo.lib.Region();
3091 /*
3092  * Portions of this file are based on pieces of Yahoo User Interface Library
3093  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3094  * YUI licensed under the BSD License:
3095  * http://developer.yahoo.net/yui/license.txt
3096  * <script type="text/javascript">
3097  *
3098  */
3099  
3100 (function() {   
3101
3102     Roo.lib.Anim = {
3103         scroll : function(el, args, duration, easing, cb, scope) {
3104             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3105         },
3106
3107         motion : function(el, args, duration, easing, cb, scope) {
3108             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3109         },
3110
3111         color : function(el, args, duration, easing, cb, scope) {
3112             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3113         },
3114
3115         run : function(el, args, duration, easing, cb, scope, type) {
3116             type = type || Roo.lib.AnimBase;
3117             if (typeof easing == "string") {
3118                 easing = Roo.lib.Easing[easing];
3119             }
3120             var anim = new type(el, args, duration, easing);
3121             anim.animateX(function() {
3122                 Roo.callback(cb, scope);
3123             });
3124             return anim;
3125         }
3126     };
3127 })();/*
3128  * Portions of this file are based on pieces of Yahoo User Interface Library
3129  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3130  * YUI licensed under the BSD License:
3131  * http://developer.yahoo.net/yui/license.txt
3132  * <script type="text/javascript">
3133  *
3134  */
3135
3136 (function() {    
3137     var libFlyweight;
3138     
3139     function fly(el) {
3140         if (!libFlyweight) {
3141             libFlyweight = new Roo.Element.Flyweight();
3142         }
3143         libFlyweight.dom = el;
3144         return libFlyweight;
3145     }
3146
3147     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3148     
3149    
3150     
3151     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3152         if (el) {
3153             this.init(el, attributes, duration, method);
3154         }
3155     };
3156
3157     Roo.lib.AnimBase.fly = fly;
3158     
3159     
3160     
3161     Roo.lib.AnimBase.prototype = {
3162
3163         toString: function() {
3164             var el = this.getEl();
3165             var id = el.id || el.tagName;
3166             return ("Anim " + id);
3167         },
3168
3169         patterns: {
3170             noNegatives:        /width|height|opacity|padding/i,
3171             offsetAttribute:  /^((width|height)|(top|left))$/,
3172             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3173             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3174         },
3175
3176
3177         doMethod: function(attr, start, end) {
3178             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3179         },
3180
3181
3182         setAttribute: function(attr, val, unit) {
3183             if (this.patterns.noNegatives.test(attr)) {
3184                 val = (val > 0) ? val : 0;
3185             }
3186
3187             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3188         },
3189
3190
3191         getAttribute: function(attr) {
3192             var el = this.getEl();
3193             var val = fly(el).getStyle(attr);
3194
3195             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3196                 return parseFloat(val);
3197             }
3198
3199             var a = this.patterns.offsetAttribute.exec(attr) || [];
3200             var pos = !!( a[3] );
3201             var box = !!( a[2] );
3202
3203
3204             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3205                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3206             } else {
3207                 val = 0;
3208             }
3209
3210             return val;
3211         },
3212
3213
3214         getDefaultUnit: function(attr) {
3215             if (this.patterns.defaultUnit.test(attr)) {
3216                 return 'px';
3217             }
3218
3219             return '';
3220         },
3221
3222         animateX : function(callback, scope) {
3223             var f = function() {
3224                 this.onComplete.removeListener(f);
3225                 if (typeof callback == "function") {
3226                     callback.call(scope || this, this);
3227                 }
3228             };
3229             this.onComplete.addListener(f, this);
3230             this.animate();
3231         },
3232
3233
3234         setRuntimeAttribute: function(attr) {
3235             var start;
3236             var end;
3237             var attributes = this.attributes;
3238
3239             this.runtimeAttributes[attr] = {};
3240
3241             var isset = function(prop) {
3242                 return (typeof prop !== 'undefined');
3243             };
3244
3245             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3246                 return false;
3247             }
3248
3249             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3250
3251
3252             if (isset(attributes[attr]['to'])) {
3253                 end = attributes[attr]['to'];
3254             } else if (isset(attributes[attr]['by'])) {
3255                 if (start.constructor == Array) {
3256                     end = [];
3257                     for (var i = 0, len = start.length; i < len; ++i) {
3258                         end[i] = start[i] + attributes[attr]['by'][i];
3259                     }
3260                 } else {
3261                     end = start + attributes[attr]['by'];
3262                 }
3263             }
3264
3265             this.runtimeAttributes[attr].start = start;
3266             this.runtimeAttributes[attr].end = end;
3267
3268
3269             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3270         },
3271
3272
3273         init: function(el, attributes, duration, method) {
3274
3275             var isAnimated = false;
3276
3277
3278             var startTime = null;
3279
3280
3281             var actualFrames = 0;
3282
3283
3284             el = Roo.getDom(el);
3285
3286
3287             this.attributes = attributes || {};
3288
3289
3290             this.duration = duration || 1;
3291
3292
3293             this.method = method || Roo.lib.Easing.easeNone;
3294
3295
3296             this.useSeconds = true;
3297
3298
3299             this.currentFrame = 0;
3300
3301
3302             this.totalFrames = Roo.lib.AnimMgr.fps;
3303
3304
3305             this.getEl = function() {
3306                 return el;
3307             };
3308
3309
3310             this.isAnimated = function() {
3311                 return isAnimated;
3312             };
3313
3314
3315             this.getStartTime = function() {
3316                 return startTime;
3317             };
3318
3319             this.runtimeAttributes = {};
3320
3321
3322             this.animate = function() {
3323                 if (this.isAnimated()) {
3324                     return false;
3325                 }
3326
3327                 this.currentFrame = 0;
3328
3329                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3330
3331                 Roo.lib.AnimMgr.registerElement(this);
3332             };
3333
3334
3335             this.stop = function(finish) {
3336                 if (finish) {
3337                     this.currentFrame = this.totalFrames;
3338                     this._onTween.fire();
3339                 }
3340                 Roo.lib.AnimMgr.stop(this);
3341             };
3342
3343             var onStart = function() {
3344                 this.onStart.fire();
3345
3346                 this.runtimeAttributes = {};
3347                 for (var attr in this.attributes) {
3348                     this.setRuntimeAttribute(attr);
3349                 }
3350
3351                 isAnimated = true;
3352                 actualFrames = 0;
3353                 startTime = new Date();
3354             };
3355
3356
3357             var onTween = function() {
3358                 var data = {
3359                     duration: new Date() - this.getStartTime(),
3360                     currentFrame: this.currentFrame
3361                 };
3362
3363                 data.toString = function() {
3364                     return (
3365                             'duration: ' + data.duration +
3366                             ', currentFrame: ' + data.currentFrame
3367                             );
3368                 };
3369
3370                 this.onTween.fire(data);
3371
3372                 var runtimeAttributes = this.runtimeAttributes;
3373
3374                 for (var attr in runtimeAttributes) {
3375                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3376                 }
3377
3378                 actualFrames += 1;
3379             };
3380
3381             var onComplete = function() {
3382                 var actual_duration = (new Date() - startTime) / 1000 ;
3383
3384                 var data = {
3385                     duration: actual_duration,
3386                     frames: actualFrames,
3387                     fps: actualFrames / actual_duration
3388                 };
3389
3390                 data.toString = function() {
3391                     return (
3392                             'duration: ' + data.duration +
3393                             ', frames: ' + data.frames +
3394                             ', fps: ' + data.fps
3395                             );
3396                 };
3397
3398                 isAnimated = false;
3399                 actualFrames = 0;
3400                 this.onComplete.fire(data);
3401             };
3402
3403
3404             this._onStart = new Roo.util.Event(this);
3405             this.onStart = new Roo.util.Event(this);
3406             this.onTween = new Roo.util.Event(this);
3407             this._onTween = new Roo.util.Event(this);
3408             this.onComplete = new Roo.util.Event(this);
3409             this._onComplete = new Roo.util.Event(this);
3410             this._onStart.addListener(onStart);
3411             this._onTween.addListener(onTween);
3412             this._onComplete.addListener(onComplete);
3413         }
3414     };
3415 })();
3416 /*
3417  * Portions of this file are based on pieces of Yahoo User Interface Library
3418  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3419  * YUI licensed under the BSD License:
3420  * http://developer.yahoo.net/yui/license.txt
3421  * <script type="text/javascript">
3422  *
3423  */
3424
3425 Roo.lib.AnimMgr = new function() {
3426
3427     var thread = null;
3428
3429
3430     var queue = [];
3431
3432
3433     var tweenCount = 0;
3434
3435
3436     this.fps = 1000;
3437
3438
3439     this.delay = 1;
3440
3441
3442     this.registerElement = function(tween) {
3443         queue[queue.length] = tween;
3444         tweenCount += 1;
3445         tween._onStart.fire();
3446         this.start();
3447     };
3448
3449
3450     this.unRegister = function(tween, index) {
3451         tween._onComplete.fire();
3452         index = index || getIndex(tween);
3453         if (index != -1) {
3454             queue.splice(index, 1);
3455         }
3456
3457         tweenCount -= 1;
3458         if (tweenCount <= 0) {
3459             this.stop();
3460         }
3461     };
3462
3463
3464     this.start = function() {
3465         if (thread === null) {
3466             thread = setInterval(this.run, this.delay);
3467         }
3468     };
3469
3470
3471     this.stop = function(tween) {
3472         if (!tween) {
3473             clearInterval(thread);
3474
3475             for (var i = 0, len = queue.length; i < len; ++i) {
3476                 if (queue[0].isAnimated()) {
3477                     this.unRegister(queue[0], 0);
3478                 }
3479             }
3480
3481             queue = [];
3482             thread = null;
3483             tweenCount = 0;
3484         }
3485         else {
3486             this.unRegister(tween);
3487         }
3488     };
3489
3490
3491     this.run = function() {
3492         for (var i = 0, len = queue.length; i < len; ++i) {
3493             var tween = queue[i];
3494             if (!tween || !tween.isAnimated()) {
3495                 continue;
3496             }
3497
3498             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3499             {
3500                 tween.currentFrame += 1;
3501
3502                 if (tween.useSeconds) {
3503                     correctFrame(tween);
3504                 }
3505                 tween._onTween.fire();
3506             }
3507             else {
3508                 Roo.lib.AnimMgr.stop(tween, i);
3509             }
3510         }
3511     };
3512
3513     var getIndex = function(anim) {
3514         for (var i = 0, len = queue.length; i < len; ++i) {
3515             if (queue[i] == anim) {
3516                 return i;
3517             }
3518         }
3519         return -1;
3520     };
3521
3522
3523     var correctFrame = function(tween) {
3524         var frames = tween.totalFrames;
3525         var frame = tween.currentFrame;
3526         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3527         var elapsed = (new Date() - tween.getStartTime());
3528         var tweak = 0;
3529
3530         if (elapsed < tween.duration * 1000) {
3531             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3532         } else {
3533             tweak = frames - (frame + 1);
3534         }
3535         if (tweak > 0 && isFinite(tweak)) {
3536             if (tween.currentFrame + tweak >= frames) {
3537                 tweak = frames - (frame + 1);
3538             }
3539
3540             tween.currentFrame += tweak;
3541         }
3542     };
3543 };
3544
3545     /*
3546  * Portions of this file are based on pieces of Yahoo User Interface Library
3547  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3548  * YUI licensed under the BSD License:
3549  * http://developer.yahoo.net/yui/license.txt
3550  * <script type="text/javascript">
3551  *
3552  */
3553 Roo.lib.Bezier = new function() {
3554
3555         this.getPosition = function(points, t) {
3556             var n = points.length;
3557             var tmp = [];
3558
3559             for (var i = 0; i < n; ++i) {
3560                 tmp[i] = [points[i][0], points[i][1]];
3561             }
3562
3563             for (var j = 1; j < n; ++j) {
3564                 for (i = 0; i < n - j; ++i) {
3565                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3566                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3567                 }
3568             }
3569
3570             return [ tmp[0][0], tmp[0][1] ];
3571
3572         };
3573     };/*
3574  * Portions of this file are based on pieces of Yahoo User Interface Library
3575  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3576  * YUI licensed under the BSD License:
3577  * http://developer.yahoo.net/yui/license.txt
3578  * <script type="text/javascript">
3579  *
3580  */
3581 (function() {
3582
3583     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3584         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3585     };
3586
3587     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3588
3589     var fly = Roo.lib.AnimBase.fly;
3590     var Y = Roo.lib;
3591     var superclass = Y.ColorAnim.superclass;
3592     var proto = Y.ColorAnim.prototype;
3593
3594     proto.toString = function() {
3595         var el = this.getEl();
3596         var id = el.id || el.tagName;
3597         return ("ColorAnim " + id);
3598     };
3599
3600     proto.patterns.color = /color$/i;
3601     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3602     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3603     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3604     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3605
3606
3607     proto.parseColor = function(s) {
3608         if (s.length == 3) {
3609             return s;
3610         }
3611
3612         var c = this.patterns.hex.exec(s);
3613         if (c && c.length == 4) {
3614             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3615         }
3616
3617         c = this.patterns.rgb.exec(s);
3618         if (c && c.length == 4) {
3619             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3620         }
3621
3622         c = this.patterns.hex3.exec(s);
3623         if (c && c.length == 4) {
3624             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3625         }
3626
3627         return null;
3628     };
3629     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3630     proto.getAttribute = function(attr) {
3631         var el = this.getEl();
3632         if (this.patterns.color.test(attr)) {
3633             var val = fly(el).getStyle(attr);
3634
3635             if (this.patterns.transparent.test(val)) {
3636                 var parent = el.parentNode;
3637                 val = fly(parent).getStyle(attr);
3638
3639                 while (parent && this.patterns.transparent.test(val)) {
3640                     parent = parent.parentNode;
3641                     val = fly(parent).getStyle(attr);
3642                     if (parent.tagName.toUpperCase() == 'HTML') {
3643                         val = '#fff';
3644                     }
3645                 }
3646             }
3647         } else {
3648             val = superclass.getAttribute.call(this, attr);
3649         }
3650
3651         return val;
3652     };
3653     proto.getAttribute = function(attr) {
3654         var el = this.getEl();
3655         if (this.patterns.color.test(attr)) {
3656             var val = fly(el).getStyle(attr);
3657
3658             if (this.patterns.transparent.test(val)) {
3659                 var parent = el.parentNode;
3660                 val = fly(parent).getStyle(attr);
3661
3662                 while (parent && this.patterns.transparent.test(val)) {
3663                     parent = parent.parentNode;
3664                     val = fly(parent).getStyle(attr);
3665                     if (parent.tagName.toUpperCase() == 'HTML') {
3666                         val = '#fff';
3667                     }
3668                 }
3669             }
3670         } else {
3671             val = superclass.getAttribute.call(this, attr);
3672         }
3673
3674         return val;
3675     };
3676
3677     proto.doMethod = function(attr, start, end) {
3678         var val;
3679
3680         if (this.patterns.color.test(attr)) {
3681             val = [];
3682             for (var i = 0, len = start.length; i < len; ++i) {
3683                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3684             }
3685
3686             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3687         }
3688         else {
3689             val = superclass.doMethod.call(this, attr, start, end);
3690         }
3691
3692         return val;
3693     };
3694
3695     proto.setRuntimeAttribute = function(attr) {
3696         superclass.setRuntimeAttribute.call(this, attr);
3697
3698         if (this.patterns.color.test(attr)) {
3699             var attributes = this.attributes;
3700             var start = this.parseColor(this.runtimeAttributes[attr].start);
3701             var end = this.parseColor(this.runtimeAttributes[attr].end);
3702
3703             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3704                 end = this.parseColor(attributes[attr].by);
3705
3706                 for (var i = 0, len = start.length; i < len; ++i) {
3707                     end[i] = start[i] + end[i];
3708                 }
3709             }
3710
3711             this.runtimeAttributes[attr].start = start;
3712             this.runtimeAttributes[attr].end = end;
3713         }
3714     };
3715 })();
3716
3717 /*
3718  * Portions of this file are based on pieces of Yahoo User Interface Library
3719  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3720  * YUI licensed under the BSD License:
3721  * http://developer.yahoo.net/yui/license.txt
3722  * <script type="text/javascript">
3723  *
3724  */
3725 Roo.lib.Easing = {
3726
3727
3728     easeNone: function (t, b, c, d) {
3729         return c * t / d + b;
3730     },
3731
3732
3733     easeIn: function (t, b, c, d) {
3734         return c * (t /= d) * t + b;
3735     },
3736
3737
3738     easeOut: function (t, b, c, d) {
3739         return -c * (t /= d) * (t - 2) + b;
3740     },
3741
3742
3743     easeBoth: function (t, b, c, d) {
3744         if ((t /= d / 2) < 1) {
3745             return c / 2 * t * t + b;
3746         }
3747
3748         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3749     },
3750
3751
3752     easeInStrong: function (t, b, c, d) {
3753         return c * (t /= d) * t * t * t + b;
3754     },
3755
3756
3757     easeOutStrong: function (t, b, c, d) {
3758         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3759     },
3760
3761
3762     easeBothStrong: function (t, b, c, d) {
3763         if ((t /= d / 2) < 1) {
3764             return c / 2 * t * t * t * t + b;
3765         }
3766
3767         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3768     },
3769
3770
3771
3772     elasticIn: function (t, b, c, d, a, p) {
3773         if (t == 0) {
3774             return b;
3775         }
3776         if ((t /= d) == 1) {
3777             return b + c;
3778         }
3779         if (!p) {
3780             p = d * .3;
3781         }
3782
3783         if (!a || a < Math.abs(c)) {
3784             a = c;
3785             var s = p / 4;
3786         }
3787         else {
3788             var s = p / (2 * Math.PI) * Math.asin(c / a);
3789         }
3790
3791         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3792     },
3793
3794
3795     elasticOut: function (t, b, c, d, a, p) {
3796         if (t == 0) {
3797             return b;
3798         }
3799         if ((t /= d) == 1) {
3800             return b + c;
3801         }
3802         if (!p) {
3803             p = d * .3;
3804         }
3805
3806         if (!a || a < Math.abs(c)) {
3807             a = c;
3808             var s = p / 4;
3809         }
3810         else {
3811             var s = p / (2 * Math.PI) * Math.asin(c / a);
3812         }
3813
3814         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3815     },
3816
3817
3818     elasticBoth: function (t, b, c, d, a, p) {
3819         if (t == 0) {
3820             return b;
3821         }
3822
3823         if ((t /= d / 2) == 2) {
3824             return b + c;
3825         }
3826
3827         if (!p) {
3828             p = d * (.3 * 1.5);
3829         }
3830
3831         if (!a || a < Math.abs(c)) {
3832             a = c;
3833             var s = p / 4;
3834         }
3835         else {
3836             var s = p / (2 * Math.PI) * Math.asin(c / a);
3837         }
3838
3839         if (t < 1) {
3840             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3841                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3842         }
3843         return a * Math.pow(2, -10 * (t -= 1)) *
3844                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3845     },
3846
3847
3848
3849     backIn: function (t, b, c, d, s) {
3850         if (typeof s == 'undefined') {
3851             s = 1.70158;
3852         }
3853         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3854     },
3855
3856
3857     backOut: function (t, b, c, d, s) {
3858         if (typeof s == 'undefined') {
3859             s = 1.70158;
3860         }
3861         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3862     },
3863
3864
3865     backBoth: function (t, b, c, d, s) {
3866         if (typeof s == 'undefined') {
3867             s = 1.70158;
3868         }
3869
3870         if ((t /= d / 2 ) < 1) {
3871             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3872         }
3873         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3874     },
3875
3876
3877     bounceIn: function (t, b, c, d) {
3878         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3879     },
3880
3881
3882     bounceOut: function (t, b, c, d) {
3883         if ((t /= d) < (1 / 2.75)) {
3884             return c * (7.5625 * t * t) + b;
3885         } else if (t < (2 / 2.75)) {
3886             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3887         } else if (t < (2.5 / 2.75)) {
3888             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3889         }
3890         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3891     },
3892
3893
3894     bounceBoth: function (t, b, c, d) {
3895         if (t < d / 2) {
3896             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3897         }
3898         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3899     }
3900 };/*
3901  * Portions of this file are based on pieces of Yahoo User Interface Library
3902  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3903  * YUI licensed under the BSD License:
3904  * http://developer.yahoo.net/yui/license.txt
3905  * <script type="text/javascript">
3906  *
3907  */
3908     (function() {
3909         Roo.lib.Motion = function(el, attributes, duration, method) {
3910             if (el) {
3911                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3912             }
3913         };
3914
3915         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3916
3917
3918         var Y = Roo.lib;
3919         var superclass = Y.Motion.superclass;
3920         var proto = Y.Motion.prototype;
3921
3922         proto.toString = function() {
3923             var el = this.getEl();
3924             var id = el.id || el.tagName;
3925             return ("Motion " + id);
3926         };
3927
3928         proto.patterns.points = /^points$/i;
3929
3930         proto.setAttribute = function(attr, val, unit) {
3931             if (this.patterns.points.test(attr)) {
3932                 unit = unit || 'px';
3933                 superclass.setAttribute.call(this, 'left', val[0], unit);
3934                 superclass.setAttribute.call(this, 'top', val[1], unit);
3935             } else {
3936                 superclass.setAttribute.call(this, attr, val, unit);
3937             }
3938         };
3939
3940         proto.getAttribute = function(attr) {
3941             if (this.patterns.points.test(attr)) {
3942                 var val = [
3943                         superclass.getAttribute.call(this, 'left'),
3944                         superclass.getAttribute.call(this, 'top')
3945                         ];
3946             } else {
3947                 val = superclass.getAttribute.call(this, attr);
3948             }
3949
3950             return val;
3951         };
3952
3953         proto.doMethod = function(attr, start, end) {
3954             var val = null;
3955
3956             if (this.patterns.points.test(attr)) {
3957                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3958                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3959             } else {
3960                 val = superclass.doMethod.call(this, attr, start, end);
3961             }
3962             return val;
3963         };
3964
3965         proto.setRuntimeAttribute = function(attr) {
3966             if (this.patterns.points.test(attr)) {
3967                 var el = this.getEl();
3968                 var attributes = this.attributes;
3969                 var start;
3970                 var control = attributes['points']['control'] || [];
3971                 var end;
3972                 var i, len;
3973
3974                 if (control.length > 0 && !(control[0] instanceof Array)) {
3975                     control = [control];
3976                 } else {
3977                     var tmp = [];
3978                     for (i = 0,len = control.length; i < len; ++i) {
3979                         tmp[i] = control[i];
3980                     }
3981                     control = tmp;
3982                 }
3983
3984                 Roo.fly(el).position();
3985
3986                 if (isset(attributes['points']['from'])) {
3987                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3988                 }
3989                 else {
3990                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3991                 }
3992
3993                 start = this.getAttribute('points');
3994
3995
3996                 if (isset(attributes['points']['to'])) {
3997                     end = translateValues.call(this, attributes['points']['to'], start);
3998
3999                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4000                     for (i = 0,len = control.length; i < len; ++i) {
4001                         control[i] = translateValues.call(this, control[i], start);
4002                     }
4003
4004
4005                 } else if (isset(attributes['points']['by'])) {
4006                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4007
4008                     for (i = 0,len = control.length; i < len; ++i) {
4009                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4010                     }
4011                 }
4012
4013                 this.runtimeAttributes[attr] = [start];
4014
4015                 if (control.length > 0) {
4016                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4017                 }
4018
4019                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4020             }
4021             else {
4022                 superclass.setRuntimeAttribute.call(this, attr);
4023             }
4024         };
4025
4026         var translateValues = function(val, start) {
4027             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4028             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4029
4030             return val;
4031         };
4032
4033         var isset = function(prop) {
4034             return (typeof prop !== 'undefined');
4035         };
4036     })();
4037 /*
4038  * Portions of this file are based on pieces of Yahoo User Interface Library
4039  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4040  * YUI licensed under the BSD License:
4041  * http://developer.yahoo.net/yui/license.txt
4042  * <script type="text/javascript">
4043  *
4044  */
4045     (function() {
4046         Roo.lib.Scroll = function(el, attributes, duration, method) {
4047             if (el) {
4048                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4049             }
4050         };
4051
4052         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4053
4054
4055         var Y = Roo.lib;
4056         var superclass = Y.Scroll.superclass;
4057         var proto = Y.Scroll.prototype;
4058
4059         proto.toString = function() {
4060             var el = this.getEl();
4061             var id = el.id || el.tagName;
4062             return ("Scroll " + id);
4063         };
4064
4065         proto.doMethod = function(attr, start, end) {
4066             var val = null;
4067
4068             if (attr == 'scroll') {
4069                 val = [
4070                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4071                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4072                         ];
4073
4074             } else {
4075                 val = superclass.doMethod.call(this, attr, start, end);
4076             }
4077             return val;
4078         };
4079
4080         proto.getAttribute = function(attr) {
4081             var val = null;
4082             var el = this.getEl();
4083
4084             if (attr == 'scroll') {
4085                 val = [ el.scrollLeft, el.scrollTop ];
4086             } else {
4087                 val = superclass.getAttribute.call(this, attr);
4088             }
4089
4090             return val;
4091         };
4092
4093         proto.setAttribute = function(attr, val, unit) {
4094             var el = this.getEl();
4095
4096             if (attr == 'scroll') {
4097                 el.scrollLeft = val[0];
4098                 el.scrollTop = val[1];
4099             } else {
4100                 superclass.setAttribute.call(this, attr, val, unit);
4101             }
4102         };
4103     })();
4104 /*
4105  * Based on:
4106  * Ext JS Library 1.1.1
4107  * Copyright(c) 2006-2007, Ext JS, LLC.
4108  *
4109  * Originally Released Under LGPL - original licence link has changed is not relivant.
4110  *
4111  * Fork - LGPL
4112  * <script type="text/javascript">
4113  */
4114
4115
4116 // nasty IE9 hack - what a pile of crap that is..
4117
4118  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4119     Range.prototype.createContextualFragment = function (html) {
4120         var doc = window.document;
4121         var container = doc.createElement("div");
4122         container.innerHTML = html;
4123         var frag = doc.createDocumentFragment(), n;
4124         while ((n = container.firstChild)) {
4125             frag.appendChild(n);
4126         }
4127         return frag;
4128     };
4129 }
4130
4131 /**
4132  * @class Roo.DomHelper
4133  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4134  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4135  * @singleton
4136  */
4137 Roo.DomHelper = function(){
4138     var tempTableEl = null;
4139     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4140     var tableRe = /^table|tbody|tr|td$/i;
4141     var xmlns = {};
4142     // build as innerHTML where available
4143     /** @ignore */
4144     var createHtml = function(o){
4145         if(typeof o == 'string'){
4146             return o;
4147         }
4148         var b = "";
4149         if(!o.tag){
4150             o.tag = "div";
4151         }
4152         b += "<" + o.tag;
4153         for(var attr in o){
4154             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4155             if(attr == "style"){
4156                 var s = o["style"];
4157                 if(typeof s == "function"){
4158                     s = s.call();
4159                 }
4160                 if(typeof s == "string"){
4161                     b += ' style="' + s + '"';
4162                 }else if(typeof s == "object"){
4163                     b += ' style="';
4164                     for(var key in s){
4165                         if(typeof s[key] != "function"){
4166                             b += key + ":" + s[key] + ";";
4167                         }
4168                     }
4169                     b += '"';
4170                 }
4171             }else{
4172                 if(attr == "cls"){
4173                     b += ' class="' + o["cls"] + '"';
4174                 }else if(attr == "htmlFor"){
4175                     b += ' for="' + o["htmlFor"] + '"';
4176                 }else{
4177                     b += " " + attr + '="' + o[attr] + '"';
4178                 }
4179             }
4180         }
4181         if(emptyTags.test(o.tag)){
4182             b += "/>";
4183         }else{
4184             b += ">";
4185             var cn = o.children || o.cn;
4186             if(cn){
4187                 //http://bugs.kde.org/show_bug.cgi?id=71506
4188                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4189                     for(var i = 0, len = cn.length; i < len; i++) {
4190                         b += createHtml(cn[i], b);
4191                     }
4192                 }else{
4193                     b += createHtml(cn, b);
4194                 }
4195             }
4196             if(o.html){
4197                 b += o.html;
4198             }
4199             b += "</" + o.tag + ">";
4200         }
4201         return b;
4202     };
4203
4204     // build as dom
4205     /** @ignore */
4206     var createDom = function(o, parentNode){
4207          
4208         // defininition craeted..
4209         var ns = false;
4210         if (o.ns && o.ns != 'html') {
4211                
4212             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4213                 xmlns[o.ns] = o.xmlns;
4214                 ns = o.xmlns;
4215             }
4216             if (typeof(xmlns[o.ns]) == 'undefined') {
4217                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4218             }
4219             ns = xmlns[o.ns];
4220         }
4221         
4222         
4223         if (typeof(o) == 'string') {
4224             return parentNode.appendChild(document.createTextNode(o));
4225         }
4226         o.tag = o.tag || div;
4227         if (o.ns && Roo.isIE) {
4228             ns = false;
4229             o.tag = o.ns + ':' + o.tag;
4230             
4231         }
4232         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4233         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4234         for(var attr in o){
4235             
4236             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4237                     attr == "style" || typeof o[attr] == "function") continue;
4238                     
4239             if(attr=="cls" && Roo.isIE){
4240                 el.className = o["cls"];
4241             }else{
4242                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4243                 else { 
4244                     el[attr] = o[attr];
4245                 }
4246             }
4247         }
4248         Roo.DomHelper.applyStyles(el, o.style);
4249         var cn = o.children || o.cn;
4250         if(cn){
4251             //http://bugs.kde.org/show_bug.cgi?id=71506
4252              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4253                 for(var i = 0, len = cn.length; i < len; i++) {
4254                     createDom(cn[i], el);
4255                 }
4256             }else{
4257                 createDom(cn, el);
4258             }
4259         }
4260         if(o.html){
4261             el.innerHTML = o.html;
4262         }
4263         if(parentNode){
4264            parentNode.appendChild(el);
4265         }
4266         return el;
4267     };
4268
4269     var ieTable = function(depth, s, h, e){
4270         tempTableEl.innerHTML = [s, h, e].join('');
4271         var i = -1, el = tempTableEl;
4272         while(++i < depth){
4273             el = el.firstChild;
4274         }
4275         return el;
4276     };
4277
4278     // kill repeat to save bytes
4279     var ts = '<table>',
4280         te = '</table>',
4281         tbs = ts+'<tbody>',
4282         tbe = '</tbody>'+te,
4283         trs = tbs + '<tr>',
4284         tre = '</tr>'+tbe;
4285
4286     /**
4287      * @ignore
4288      * Nasty code for IE's broken table implementation
4289      */
4290     var insertIntoTable = function(tag, where, el, html){
4291         if(!tempTableEl){
4292             tempTableEl = document.createElement('div');
4293         }
4294         var node;
4295         var before = null;
4296         if(tag == 'td'){
4297             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4298                 return;
4299             }
4300             if(where == 'beforebegin'){
4301                 before = el;
4302                 el = el.parentNode;
4303             } else{
4304                 before = el.nextSibling;
4305                 el = el.parentNode;
4306             }
4307             node = ieTable(4, trs, html, tre);
4308         }
4309         else if(tag == 'tr'){
4310             if(where == 'beforebegin'){
4311                 before = el;
4312                 el = el.parentNode;
4313                 node = ieTable(3, tbs, html, tbe);
4314             } else if(where == 'afterend'){
4315                 before = el.nextSibling;
4316                 el = el.parentNode;
4317                 node = ieTable(3, tbs, html, tbe);
4318             } else{ // INTO a TR
4319                 if(where == 'afterbegin'){
4320                     before = el.firstChild;
4321                 }
4322                 node = ieTable(4, trs, html, tre);
4323             }
4324         } else if(tag == 'tbody'){
4325             if(where == 'beforebegin'){
4326                 before = el;
4327                 el = el.parentNode;
4328                 node = ieTable(2, ts, html, te);
4329             } else if(where == 'afterend'){
4330                 before = el.nextSibling;
4331                 el = el.parentNode;
4332                 node = ieTable(2, ts, html, te);
4333             } else{
4334                 if(where == 'afterbegin'){
4335                     before = el.firstChild;
4336                 }
4337                 node = ieTable(3, tbs, html, tbe);
4338             }
4339         } else{ // TABLE
4340             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4341                 return;
4342             }
4343             if(where == 'afterbegin'){
4344                 before = el.firstChild;
4345             }
4346             node = ieTable(2, ts, html, te);
4347         }
4348         el.insertBefore(node, before);
4349         return node;
4350     };
4351
4352     return {
4353     /** True to force the use of DOM instead of html fragments @type Boolean */
4354     useDom : false,
4355
4356     /**
4357      * Returns the markup for the passed Element(s) config
4358      * @param {Object} o The Dom object spec (and children)
4359      * @return {String}
4360      */
4361     markup : function(o){
4362         return createHtml(o);
4363     },
4364
4365     /**
4366      * Applies a style specification to an element
4367      * @param {String/HTMLElement} el The element to apply styles to
4368      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4369      * a function which returns such a specification.
4370      */
4371     applyStyles : function(el, styles){
4372         if(styles){
4373            el = Roo.fly(el);
4374            if(typeof styles == "string"){
4375                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4376                var matches;
4377                while ((matches = re.exec(styles)) != null){
4378                    el.setStyle(matches[1], matches[2]);
4379                }
4380            }else if (typeof styles == "object"){
4381                for (var style in styles){
4382                   el.setStyle(style, styles[style]);
4383                }
4384            }else if (typeof styles == "function"){
4385                 Roo.DomHelper.applyStyles(el, styles.call());
4386            }
4387         }
4388     },
4389
4390     /**
4391      * Inserts an HTML fragment into the Dom
4392      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4393      * @param {HTMLElement} el The context element
4394      * @param {String} html The HTML fragmenet
4395      * @return {HTMLElement} The new node
4396      */
4397     insertHtml : function(where, el, html){
4398         where = where.toLowerCase();
4399         if(el.insertAdjacentHTML){
4400             if(tableRe.test(el.tagName)){
4401                 var rs;
4402                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4403                     return rs;
4404                 }
4405             }
4406             switch(where){
4407                 case "beforebegin":
4408                     el.insertAdjacentHTML('BeforeBegin', html);
4409                     return el.previousSibling;
4410                 case "afterbegin":
4411                     el.insertAdjacentHTML('AfterBegin', html);
4412                     return el.firstChild;
4413                 case "beforeend":
4414                     el.insertAdjacentHTML('BeforeEnd', html);
4415                     return el.lastChild;
4416                 case "afterend":
4417                     el.insertAdjacentHTML('AfterEnd', html);
4418                     return el.nextSibling;
4419             }
4420             throw 'Illegal insertion point -> "' + where + '"';
4421         }
4422         var range = el.ownerDocument.createRange();
4423         var frag;
4424         switch(where){
4425              case "beforebegin":
4426                 range.setStartBefore(el);
4427                 frag = range.createContextualFragment(html);
4428                 el.parentNode.insertBefore(frag, el);
4429                 return el.previousSibling;
4430              case "afterbegin":
4431                 if(el.firstChild){
4432                     range.setStartBefore(el.firstChild);
4433                     frag = range.createContextualFragment(html);
4434                     el.insertBefore(frag, el.firstChild);
4435                     return el.firstChild;
4436                 }else{
4437                     el.innerHTML = html;
4438                     return el.firstChild;
4439                 }
4440             case "beforeend":
4441                 if(el.lastChild){
4442                     range.setStartAfter(el.lastChild);
4443                     frag = range.createContextualFragment(html);
4444                     el.appendChild(frag);
4445                     return el.lastChild;
4446                 }else{
4447                     el.innerHTML = html;
4448                     return el.lastChild;
4449                 }
4450             case "afterend":
4451                 range.setStartAfter(el);
4452                 frag = range.createContextualFragment(html);
4453                 el.parentNode.insertBefore(frag, el.nextSibling);
4454                 return el.nextSibling;
4455             }
4456             throw 'Illegal insertion point -> "' + where + '"';
4457     },
4458
4459     /**
4460      * Creates new Dom element(s) and inserts them before el
4461      * @param {String/HTMLElement/Element} el The context element
4462      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4463      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4464      * @return {HTMLElement/Roo.Element} The new node
4465      */
4466     insertBefore : function(el, o, returnElement){
4467         return this.doInsert(el, o, returnElement, "beforeBegin");
4468     },
4469
4470     /**
4471      * Creates new Dom element(s) and inserts them after el
4472      * @param {String/HTMLElement/Element} el The context element
4473      * @param {Object} o The Dom object spec (and children)
4474      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4475      * @return {HTMLElement/Roo.Element} The new node
4476      */
4477     insertAfter : function(el, o, returnElement){
4478         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4479     },
4480
4481     /**
4482      * Creates new Dom element(s) and inserts them as the first child of el
4483      * @param {String/HTMLElement/Element} el The context element
4484      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4485      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4486      * @return {HTMLElement/Roo.Element} The new node
4487      */
4488     insertFirst : function(el, o, returnElement){
4489         return this.doInsert(el, o, returnElement, "afterBegin");
4490     },
4491
4492     // private
4493     doInsert : function(el, o, returnElement, pos, sibling){
4494         el = Roo.getDom(el);
4495         var newNode;
4496         if(this.useDom || o.ns){
4497             newNode = createDom(o, null);
4498             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4499         }else{
4500             var html = createHtml(o);
4501             newNode = this.insertHtml(pos, el, html);
4502         }
4503         return returnElement ? Roo.get(newNode, true) : newNode;
4504     },
4505
4506     /**
4507      * Creates new Dom element(s) and appends them to el
4508      * @param {String/HTMLElement/Element} el The context element
4509      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4510      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4511      * @return {HTMLElement/Roo.Element} The new node
4512      */
4513     append : function(el, o, returnElement){
4514         el = Roo.getDom(el);
4515         var newNode;
4516         if(this.useDom || o.ns){
4517             newNode = createDom(o, null);
4518             el.appendChild(newNode);
4519         }else{
4520             var html = createHtml(o);
4521             newNode = this.insertHtml("beforeEnd", el, html);
4522         }
4523         return returnElement ? Roo.get(newNode, true) : newNode;
4524     },
4525
4526     /**
4527      * Creates new Dom element(s) and overwrites the contents of el with them
4528      * @param {String/HTMLElement/Element} el The context element
4529      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4530      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4531      * @return {HTMLElement/Roo.Element} The new node
4532      */
4533     overwrite : function(el, o, returnElement){
4534         el = Roo.getDom(el);
4535         if (o.ns) {
4536           
4537             while (el.childNodes.length) {
4538                 el.removeChild(el.firstChild);
4539             }
4540             createDom(o, el);
4541         } else {
4542             el.innerHTML = createHtml(o);   
4543         }
4544         
4545         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4546     },
4547
4548     /**
4549      * Creates a new Roo.DomHelper.Template from the Dom object spec
4550      * @param {Object} o The Dom object spec (and children)
4551      * @return {Roo.DomHelper.Template} The new template
4552      */
4553     createTemplate : function(o){
4554         var html = createHtml(o);
4555         return new Roo.Template(html);
4556     }
4557     };
4558 }();
4559 /*
4560  * Based on:
4561  * Ext JS Library 1.1.1
4562  * Copyright(c) 2006-2007, Ext JS, LLC.
4563  *
4564  * Originally Released Under LGPL - original licence link has changed is not relivant.
4565  *
4566  * Fork - LGPL
4567  * <script type="text/javascript">
4568  */
4569  
4570 /**
4571 * @class Roo.Template
4572 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4573 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4574 * Usage:
4575 <pre><code>
4576 var t = new Roo.Template({
4577     html :  '&lt;div name="{id}"&gt;' + 
4578         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4579         '&lt;/div&gt;',
4580     myformat: function (value, allValues) {
4581         return 'XX' + value;
4582     }
4583 });
4584 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4585 </code></pre>
4586 * For more information see this blog post with examples:
4587 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4588      - Create Elements using DOM, HTML fragments and Templates</a>. 
4589 * @constructor
4590 * @param {Object} cfg - Configuration object.
4591 */
4592 Roo.Template = function(cfg){
4593     // BC!
4594     if(cfg instanceof Array){
4595         cfg = cfg.join("");
4596     }else if(arguments.length > 1){
4597         cfg = Array.prototype.join.call(arguments, "");
4598     }
4599     
4600     
4601     if (typeof(cfg) == 'object') {
4602         Roo.apply(this,cfg)
4603     } else {
4604         // bc
4605         this.html = cfg;
4606     }
4607     if (this.url) {
4608         this.load();
4609     }
4610     
4611 };
4612 Roo.Template.prototype = {
4613     
4614     /**
4615      * @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..
4616      *                    it should be fixed so that template is observable...
4617      */
4618     url : false,
4619     /**
4620      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4621      */
4622     html : '',
4623     /**
4624      * Returns an HTML fragment of this template with the specified values applied.
4625      * @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'})
4626      * @return {String} The HTML fragment
4627      */
4628     applyTemplate : function(values){
4629         try {
4630            
4631             if(this.compiled){
4632                 return this.compiled(values);
4633             }
4634             var useF = this.disableFormats !== true;
4635             var fm = Roo.util.Format, tpl = this;
4636             var fn = function(m, name, format, args){
4637                 if(format && useF){
4638                     if(format.substr(0, 5) == "this."){
4639                         return tpl.call(format.substr(5), values[name], values);
4640                     }else{
4641                         if(args){
4642                             // quoted values are required for strings in compiled templates, 
4643                             // but for non compiled we need to strip them
4644                             // quoted reversed for jsmin
4645                             var re = /^\s*['"](.*)["']\s*$/;
4646                             args = args.split(',');
4647                             for(var i = 0, len = args.length; i < len; i++){
4648                                 args[i] = args[i].replace(re, "$1");
4649                             }
4650                             args = [values[name]].concat(args);
4651                         }else{
4652                             args = [values[name]];
4653                         }
4654                         return fm[format].apply(fm, args);
4655                     }
4656                 }else{
4657                     return values[name] !== undefined ? values[name] : "";
4658                 }
4659             };
4660             return this.html.replace(this.re, fn);
4661         } catch (e) {
4662             Roo.log(e);
4663             throw e;
4664         }
4665          
4666     },
4667     
4668     loading : false,
4669       
4670     load : function ()
4671     {
4672          
4673         if (this.loading) {
4674             return;
4675         }
4676         var _t = this;
4677         
4678         this.loading = true;
4679         this.compiled = false;
4680         
4681         var cx = new Roo.data.Connection();
4682         cx.request({
4683             url : this.url,
4684             method : 'GET',
4685             success : function (response) {
4686                 _t.loading = false;
4687                 _t.html = response.responseText;
4688                 _t.url = false;
4689                 _t.compile();
4690              },
4691             failure : function(response) {
4692                 Roo.log("Template failed to load from " + _t.url);
4693                 _t.loading = false;
4694             }
4695         });
4696     },
4697
4698     /**
4699      * Sets the HTML used as the template and optionally compiles it.
4700      * @param {String} html
4701      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4702      * @return {Roo.Template} this
4703      */
4704     set : function(html, compile){
4705         this.html = html;
4706         this.compiled = null;
4707         if(compile){
4708             this.compile();
4709         }
4710         return this;
4711     },
4712     
4713     /**
4714      * True to disable format functions (defaults to false)
4715      * @type Boolean
4716      */
4717     disableFormats : false,
4718     
4719     /**
4720     * The regular expression used to match template variables 
4721     * @type RegExp
4722     * @property 
4723     */
4724     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4725     
4726     /**
4727      * Compiles the template into an internal function, eliminating the RegEx overhead.
4728      * @return {Roo.Template} this
4729      */
4730     compile : function(){
4731         var fm = Roo.util.Format;
4732         var useF = this.disableFormats !== true;
4733         var sep = Roo.isGecko ? "+" : ",";
4734         var fn = function(m, name, format, args){
4735             if(format && useF){
4736                 args = args ? ',' + args : "";
4737                 if(format.substr(0, 5) != "this."){
4738                     format = "fm." + format + '(';
4739                 }else{
4740                     format = 'this.call("'+ format.substr(5) + '", ';
4741                     args = ", values";
4742                 }
4743             }else{
4744                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4745             }
4746             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4747         };
4748         var body;
4749         // branched to use + in gecko and [].join() in others
4750         if(Roo.isGecko){
4751             body = "this.compiled = function(values){ return '" +
4752                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4753                     "';};";
4754         }else{
4755             body = ["this.compiled = function(values){ return ['"];
4756             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4757             body.push("'].join('');};");
4758             body = body.join('');
4759         }
4760         /**
4761          * eval:var:values
4762          * eval:var:fm
4763          */
4764         eval(body);
4765         return this;
4766     },
4767     
4768     // private function used to call members
4769     call : function(fnName, value, allValues){
4770         return this[fnName](value, allValues);
4771     },
4772     
4773     /**
4774      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4775      * @param {String/HTMLElement/Roo.Element} el The context element
4776      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4777      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4778      * @return {HTMLElement/Roo.Element} The new node or Element
4779      */
4780     insertFirst: function(el, values, returnElement){
4781         return this.doInsert('afterBegin', el, values, returnElement);
4782     },
4783
4784     /**
4785      * Applies the supplied values to the template and inserts the new node(s) before el.
4786      * @param {String/HTMLElement/Roo.Element} el The context element
4787      * @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'})
4788      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4789      * @return {HTMLElement/Roo.Element} The new node or Element
4790      */
4791     insertBefore: function(el, values, returnElement){
4792         return this.doInsert('beforeBegin', el, values, returnElement);
4793     },
4794
4795     /**
4796      * Applies the supplied values to the template and inserts the new node(s) after el.
4797      * @param {String/HTMLElement/Roo.Element} el The context element
4798      * @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'})
4799      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4800      * @return {HTMLElement/Roo.Element} The new node or Element
4801      */
4802     insertAfter : function(el, values, returnElement){
4803         return this.doInsert('afterEnd', el, values, returnElement);
4804     },
4805     
4806     /**
4807      * Applies the supplied values to the template and appends the new node(s) to el.
4808      * @param {String/HTMLElement/Roo.Element} el The context element
4809      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4810      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4811      * @return {HTMLElement/Roo.Element} The new node or Element
4812      */
4813     append : function(el, values, returnElement){
4814         return this.doInsert('beforeEnd', el, values, returnElement);
4815     },
4816
4817     doInsert : function(where, el, values, returnEl){
4818         el = Roo.getDom(el);
4819         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4820         return returnEl ? Roo.get(newNode, true) : newNode;
4821     },
4822
4823     /**
4824      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4825      * @param {String/HTMLElement/Roo.Element} el The context element
4826      * @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'})
4827      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4828      * @return {HTMLElement/Roo.Element} The new node or Element
4829      */
4830     overwrite : function(el, values, returnElement){
4831         el = Roo.getDom(el);
4832         el.innerHTML = this.applyTemplate(values);
4833         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4834     }
4835 };
4836 /**
4837  * Alias for {@link #applyTemplate}
4838  * @method
4839  */
4840 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4841
4842 // backwards compat
4843 Roo.DomHelper.Template = Roo.Template;
4844
4845 /**
4846  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4847  * @param {String/HTMLElement} el A DOM element or its id
4848  * @returns {Roo.Template} The created template
4849  * @static
4850  */
4851 Roo.Template.from = function(el){
4852     el = Roo.getDom(el);
4853     return new Roo.Template(el.value || el.innerHTML);
4854 };/*
4855  * Based on:
4856  * Ext JS Library 1.1.1
4857  * Copyright(c) 2006-2007, Ext JS, LLC.
4858  *
4859  * Originally Released Under LGPL - original licence link has changed is not relivant.
4860  *
4861  * Fork - LGPL
4862  * <script type="text/javascript">
4863  */
4864  
4865
4866 /*
4867  * This is code is also distributed under MIT license for use
4868  * with jQuery and prototype JavaScript libraries.
4869  */
4870 /**
4871  * @class Roo.DomQuery
4872 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).
4873 <p>
4874 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>
4875
4876 <p>
4877 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.
4878 </p>
4879 <h4>Element Selectors:</h4>
4880 <ul class="list">
4881     <li> <b>*</b> any element</li>
4882     <li> <b>E</b> an element with the tag E</li>
4883     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4884     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4885     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4886     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4887 </ul>
4888 <h4>Attribute Selectors:</h4>
4889 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4890 <ul class="list">
4891     <li> <b>E[foo]</b> has an attribute "foo"</li>
4892     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4893     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4894     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4895     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4896     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4897     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4898 </ul>
4899 <h4>Pseudo Classes:</h4>
4900 <ul class="list">
4901     <li> <b>E:first-child</b> E is the first child of its parent</li>
4902     <li> <b>E:last-child</b> E is the last child of its parent</li>
4903     <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>
4904     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4905     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4906     <li> <b>E:only-child</b> E is the only child of its parent</li>
4907     <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>
4908     <li> <b>E:first</b> the first E in the resultset</li>
4909     <li> <b>E:last</b> the last E in the resultset</li>
4910     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4911     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4912     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4913     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4914     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4915     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4916     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4917     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4918     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4919 </ul>
4920 <h4>CSS Value Selectors:</h4>
4921 <ul class="list">
4922     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4923     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4924     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4925     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4926     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4927     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4928 </ul>
4929  * @singleton
4930  */
4931 Roo.DomQuery = function(){
4932     var cache = {}, simpleCache = {}, valueCache = {};
4933     var nonSpace = /\S/;
4934     var trimRe = /^\s+|\s+$/g;
4935     var tplRe = /\{(\d+)\}/g;
4936     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4937     var tagTokenRe = /^(#)?([\w-\*]+)/;
4938     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4939
4940     function child(p, index){
4941         var i = 0;
4942         var n = p.firstChild;
4943         while(n){
4944             if(n.nodeType == 1){
4945                if(++i == index){
4946                    return n;
4947                }
4948             }
4949             n = n.nextSibling;
4950         }
4951         return null;
4952     };
4953
4954     function next(n){
4955         while((n = n.nextSibling) && n.nodeType != 1);
4956         return n;
4957     };
4958
4959     function prev(n){
4960         while((n = n.previousSibling) && n.nodeType != 1);
4961         return n;
4962     };
4963
4964     function children(d){
4965         var n = d.firstChild, ni = -1;
4966             while(n){
4967                 var nx = n.nextSibling;
4968                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4969                     d.removeChild(n);
4970                 }else{
4971                     n.nodeIndex = ++ni;
4972                 }
4973                 n = nx;
4974             }
4975             return this;
4976         };
4977
4978     function byClassName(c, a, v){
4979         if(!v){
4980             return c;
4981         }
4982         var r = [], ri = -1, cn;
4983         for(var i = 0, ci; ci = c[i]; i++){
4984             if((' '+ci.className+' ').indexOf(v) != -1){
4985                 r[++ri] = ci;
4986             }
4987         }
4988         return r;
4989     };
4990
4991     function attrValue(n, attr){
4992         if(!n.tagName && typeof n.length != "undefined"){
4993             n = n[0];
4994         }
4995         if(!n){
4996             return null;
4997         }
4998         if(attr == "for"){
4999             return n.htmlFor;
5000         }
5001         if(attr == "class" || attr == "className"){
5002             return n.className;
5003         }
5004         return n.getAttribute(attr) || n[attr];
5005
5006     };
5007
5008     function getNodes(ns, mode, tagName){
5009         var result = [], ri = -1, cs;
5010         if(!ns){
5011             return result;
5012         }
5013         tagName = tagName || "*";
5014         if(typeof ns.getElementsByTagName != "undefined"){
5015             ns = [ns];
5016         }
5017         if(!mode){
5018             for(var i = 0, ni; ni = ns[i]; i++){
5019                 cs = ni.getElementsByTagName(tagName);
5020                 for(var j = 0, ci; ci = cs[j]; j++){
5021                     result[++ri] = ci;
5022                 }
5023             }
5024         }else if(mode == "/" || mode == ">"){
5025             var utag = tagName.toUpperCase();
5026             for(var i = 0, ni, cn; ni = ns[i]; i++){
5027                 cn = ni.children || ni.childNodes;
5028                 for(var j = 0, cj; cj = cn[j]; j++){
5029                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5030                         result[++ri] = cj;
5031                     }
5032                 }
5033             }
5034         }else if(mode == "+"){
5035             var utag = tagName.toUpperCase();
5036             for(var i = 0, n; n = ns[i]; i++){
5037                 while((n = n.nextSibling) && n.nodeType != 1);
5038                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5039                     result[++ri] = n;
5040                 }
5041             }
5042         }else if(mode == "~"){
5043             for(var i = 0, n; n = ns[i]; i++){
5044                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5045                 if(n){
5046                     result[++ri] = n;
5047                 }
5048             }
5049         }
5050         return result;
5051     };
5052
5053     function concat(a, b){
5054         if(b.slice){
5055             return a.concat(b);
5056         }
5057         for(var i = 0, l = b.length; i < l; i++){
5058             a[a.length] = b[i];
5059         }
5060         return a;
5061     }
5062
5063     function byTag(cs, tagName){
5064         if(cs.tagName || cs == document){
5065             cs = [cs];
5066         }
5067         if(!tagName){
5068             return cs;
5069         }
5070         var r = [], ri = -1;
5071         tagName = tagName.toLowerCase();
5072         for(var i = 0, ci; ci = cs[i]; i++){
5073             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5074                 r[++ri] = ci;
5075             }
5076         }
5077         return r;
5078     };
5079
5080     function byId(cs, attr, id){
5081         if(cs.tagName || cs == document){
5082             cs = [cs];
5083         }
5084         if(!id){
5085             return cs;
5086         }
5087         var r = [], ri = -1;
5088         for(var i = 0,ci; ci = cs[i]; i++){
5089             if(ci && ci.id == id){
5090                 r[++ri] = ci;
5091                 return r;
5092             }
5093         }
5094         return r;
5095     };
5096
5097     function byAttribute(cs, attr, value, op, custom){
5098         var r = [], ri = -1, st = custom=="{";
5099         var f = Roo.DomQuery.operators[op];
5100         for(var i = 0, ci; ci = cs[i]; i++){
5101             var a;
5102             if(st){
5103                 a = Roo.DomQuery.getStyle(ci, attr);
5104             }
5105             else if(attr == "class" || attr == "className"){
5106                 a = ci.className;
5107             }else if(attr == "for"){
5108                 a = ci.htmlFor;
5109             }else if(attr == "href"){
5110                 a = ci.getAttribute("href", 2);
5111             }else{
5112                 a = ci.getAttribute(attr);
5113             }
5114             if((f && f(a, value)) || (!f && a)){
5115                 r[++ri] = ci;
5116             }
5117         }
5118         return r;
5119     };
5120
5121     function byPseudo(cs, name, value){
5122         return Roo.DomQuery.pseudos[name](cs, value);
5123     };
5124
5125     // This is for IE MSXML which does not support expandos.
5126     // IE runs the same speed using setAttribute, however FF slows way down
5127     // and Safari completely fails so they need to continue to use expandos.
5128     var isIE = window.ActiveXObject ? true : false;
5129
5130     // this eval is stop the compressor from
5131     // renaming the variable to something shorter
5132     
5133     /** eval:var:batch */
5134     var batch = 30803; 
5135
5136     var key = 30803;
5137
5138     function nodupIEXml(cs){
5139         var d = ++key;
5140         cs[0].setAttribute("_nodup", d);
5141         var r = [cs[0]];
5142         for(var i = 1, len = cs.length; i < len; i++){
5143             var c = cs[i];
5144             if(!c.getAttribute("_nodup") != d){
5145                 c.setAttribute("_nodup", d);
5146                 r[r.length] = c;
5147             }
5148         }
5149         for(var i = 0, len = cs.length; i < len; i++){
5150             cs[i].removeAttribute("_nodup");
5151         }
5152         return r;
5153     }
5154
5155     function nodup(cs){
5156         if(!cs){
5157             return [];
5158         }
5159         var len = cs.length, c, i, r = cs, cj, ri = -1;
5160         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5161             return cs;
5162         }
5163         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5164             return nodupIEXml(cs);
5165         }
5166         var d = ++key;
5167         cs[0]._nodup = d;
5168         for(i = 1; c = cs[i]; i++){
5169             if(c._nodup != d){
5170                 c._nodup = d;
5171             }else{
5172                 r = [];
5173                 for(var j = 0; j < i; j++){
5174                     r[++ri] = cs[j];
5175                 }
5176                 for(j = i+1; cj = cs[j]; j++){
5177                     if(cj._nodup != d){
5178                         cj._nodup = d;
5179                         r[++ri] = cj;
5180                     }
5181                 }
5182                 return r;
5183             }
5184         }
5185         return r;
5186     }
5187
5188     function quickDiffIEXml(c1, c2){
5189         var d = ++key;
5190         for(var i = 0, len = c1.length; i < len; i++){
5191             c1[i].setAttribute("_qdiff", d);
5192         }
5193         var r = [];
5194         for(var i = 0, len = c2.length; i < len; i++){
5195             if(c2[i].getAttribute("_qdiff") != d){
5196                 r[r.length] = c2[i];
5197             }
5198         }
5199         for(var i = 0, len = c1.length; i < len; i++){
5200            c1[i].removeAttribute("_qdiff");
5201         }
5202         return r;
5203     }
5204
5205     function quickDiff(c1, c2){
5206         var len1 = c1.length;
5207         if(!len1){
5208             return c2;
5209         }
5210         if(isIE && c1[0].selectSingleNode){
5211             return quickDiffIEXml(c1, c2);
5212         }
5213         var d = ++key;
5214         for(var i = 0; i < len1; i++){
5215             c1[i]._qdiff = d;
5216         }
5217         var r = [];
5218         for(var i = 0, len = c2.length; i < len; i++){
5219             if(c2[i]._qdiff != d){
5220                 r[r.length] = c2[i];
5221             }
5222         }
5223         return r;
5224     }
5225
5226     function quickId(ns, mode, root, id){
5227         if(ns == root){
5228            var d = root.ownerDocument || root;
5229            return d.getElementById(id);
5230         }
5231         ns = getNodes(ns, mode, "*");
5232         return byId(ns, null, id);
5233     }
5234
5235     return {
5236         getStyle : function(el, name){
5237             return Roo.fly(el).getStyle(name);
5238         },
5239         /**
5240          * Compiles a selector/xpath query into a reusable function. The returned function
5241          * takes one parameter "root" (optional), which is the context node from where the query should start.
5242          * @param {String} selector The selector/xpath query
5243          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5244          * @return {Function}
5245          */
5246         compile : function(path, type){
5247             type = type || "select";
5248             
5249             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5250             var q = path, mode, lq;
5251             var tk = Roo.DomQuery.matchers;
5252             var tklen = tk.length;
5253             var mm;
5254
5255             // accept leading mode switch
5256             var lmode = q.match(modeRe);
5257             if(lmode && lmode[1]){
5258                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5259                 q = q.replace(lmode[1], "");
5260             }
5261             // strip leading slashes
5262             while(path.substr(0, 1)=="/"){
5263                 path = path.substr(1);
5264             }
5265
5266             while(q && lq != q){
5267                 lq = q;
5268                 var tm = q.match(tagTokenRe);
5269                 if(type == "select"){
5270                     if(tm){
5271                         if(tm[1] == "#"){
5272                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5273                         }else{
5274                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5275                         }
5276                         q = q.replace(tm[0], "");
5277                     }else if(q.substr(0, 1) != '@'){
5278                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5279                     }
5280                 }else{
5281                     if(tm){
5282                         if(tm[1] == "#"){
5283                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5284                         }else{
5285                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5286                         }
5287                         q = q.replace(tm[0], "");
5288                     }
5289                 }
5290                 while(!(mm = q.match(modeRe))){
5291                     var matched = false;
5292                     for(var j = 0; j < tklen; j++){
5293                         var t = tk[j];
5294                         var m = q.match(t.re);
5295                         if(m){
5296                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5297                                                     return m[i];
5298                                                 });
5299                             q = q.replace(m[0], "");
5300                             matched = true;
5301                             break;
5302                         }
5303                     }
5304                     // prevent infinite loop on bad selector
5305                     if(!matched){
5306                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5307                     }
5308                 }
5309                 if(mm[1]){
5310                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5311                     q = q.replace(mm[1], "");
5312                 }
5313             }
5314             fn[fn.length] = "return nodup(n);\n}";
5315             
5316              /** 
5317               * list of variables that need from compression as they are used by eval.
5318              *  eval:var:batch 
5319              *  eval:var:nodup
5320              *  eval:var:byTag
5321              *  eval:var:ById
5322              *  eval:var:getNodes
5323              *  eval:var:quickId
5324              *  eval:var:mode
5325              *  eval:var:root
5326              *  eval:var:n
5327              *  eval:var:byClassName
5328              *  eval:var:byPseudo
5329              *  eval:var:byAttribute
5330              *  eval:var:attrValue
5331              * 
5332              **/ 
5333             eval(fn.join(""));
5334             return f;
5335         },
5336
5337         /**
5338          * Selects a group of elements.
5339          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5340          * @param {Node} root (optional) The start of the query (defaults to document).
5341          * @return {Array}
5342          */
5343         select : function(path, root, type){
5344             if(!root || root == document){
5345                 root = document;
5346             }
5347             if(typeof root == "string"){
5348                 root = document.getElementById(root);
5349             }
5350             var paths = path.split(",");
5351             var results = [];
5352             for(var i = 0, len = paths.length; i < len; i++){
5353                 var p = paths[i].replace(trimRe, "");
5354                 if(!cache[p]){
5355                     cache[p] = Roo.DomQuery.compile(p);
5356                     if(!cache[p]){
5357                         throw p + " is not a valid selector";
5358                     }
5359                 }
5360                 var result = cache[p](root);
5361                 if(result && result != document){
5362                     results = results.concat(result);
5363                 }
5364             }
5365             if(paths.length > 1){
5366                 return nodup(results);
5367             }
5368             return results;
5369         },
5370
5371         /**
5372          * Selects a single element.
5373          * @param {String} selector The selector/xpath query
5374          * @param {Node} root (optional) The start of the query (defaults to document).
5375          * @return {Element}
5376          */
5377         selectNode : function(path, root){
5378             return Roo.DomQuery.select(path, root)[0];
5379         },
5380
5381         /**
5382          * Selects the value of a node, optionally replacing null with the defaultValue.
5383          * @param {String} selector The selector/xpath query
5384          * @param {Node} root (optional) The start of the query (defaults to document).
5385          * @param {String} defaultValue
5386          */
5387         selectValue : function(path, root, defaultValue){
5388             path = path.replace(trimRe, "");
5389             if(!valueCache[path]){
5390                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5391             }
5392             var n = valueCache[path](root);
5393             n = n[0] ? n[0] : n;
5394             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5395             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5396         },
5397
5398         /**
5399          * Selects the value of a node, parsing integers and floats.
5400          * @param {String} selector The selector/xpath query
5401          * @param {Node} root (optional) The start of the query (defaults to document).
5402          * @param {Number} defaultValue
5403          * @return {Number}
5404          */
5405         selectNumber : function(path, root, defaultValue){
5406             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5407             return parseFloat(v);
5408         },
5409
5410         /**
5411          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5412          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5413          * @param {String} selector The simple selector to test
5414          * @return {Boolean}
5415          */
5416         is : function(el, ss){
5417             if(typeof el == "string"){
5418                 el = document.getElementById(el);
5419             }
5420             var isArray = (el instanceof Array);
5421             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5422             return isArray ? (result.length == el.length) : (result.length > 0);
5423         },
5424
5425         /**
5426          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5427          * @param {Array} el An array of elements to filter
5428          * @param {String} selector The simple selector to test
5429          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5430          * the selector instead of the ones that match
5431          * @return {Array}
5432          */
5433         filter : function(els, ss, nonMatches){
5434             ss = ss.replace(trimRe, "");
5435             if(!simpleCache[ss]){
5436                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5437             }
5438             var result = simpleCache[ss](els);
5439             return nonMatches ? quickDiff(result, els) : result;
5440         },
5441
5442         /**
5443          * Collection of matching regular expressions and code snippets.
5444          */
5445         matchers : [{
5446                 re: /^\.([\w-]+)/,
5447                 select: 'n = byClassName(n, null, " {1} ");'
5448             }, {
5449                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5450                 select: 'n = byPseudo(n, "{1}", "{2}");'
5451             },{
5452                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5453                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5454             }, {
5455                 re: /^#([\w-]+)/,
5456                 select: 'n = byId(n, null, "{1}");'
5457             },{
5458                 re: /^@([\w-]+)/,
5459                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5460             }
5461         ],
5462
5463         /**
5464          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5465          * 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;.
5466          */
5467         operators : {
5468             "=" : function(a, v){
5469                 return a == v;
5470             },
5471             "!=" : function(a, v){
5472                 return a != v;
5473             },
5474             "^=" : function(a, v){
5475                 return a && a.substr(0, v.length) == v;
5476             },
5477             "$=" : function(a, v){
5478                 return a && a.substr(a.length-v.length) == v;
5479             },
5480             "*=" : function(a, v){
5481                 return a && a.indexOf(v) !== -1;
5482             },
5483             "%=" : function(a, v){
5484                 return (a % v) == 0;
5485             },
5486             "|=" : function(a, v){
5487                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5488             },
5489             "~=" : function(a, v){
5490                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5491             }
5492         },
5493
5494         /**
5495          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5496          * and the argument (if any) supplied in the selector.
5497          */
5498         pseudos : {
5499             "first-child" : function(c){
5500                 var r = [], ri = -1, n;
5501                 for(var i = 0, ci; ci = n = c[i]; i++){
5502                     while((n = n.previousSibling) && n.nodeType != 1);
5503                     if(!n){
5504                         r[++ri] = ci;
5505                     }
5506                 }
5507                 return r;
5508             },
5509
5510             "last-child" : function(c){
5511                 var r = [], ri = -1, n;
5512                 for(var i = 0, ci; ci = n = c[i]; i++){
5513                     while((n = n.nextSibling) && n.nodeType != 1);
5514                     if(!n){
5515                         r[++ri] = ci;
5516                     }
5517                 }
5518                 return r;
5519             },
5520
5521             "nth-child" : function(c, a) {
5522                 var r = [], ri = -1;
5523                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5524                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5525                 for(var i = 0, n; n = c[i]; i++){
5526                     var pn = n.parentNode;
5527                     if (batch != pn._batch) {
5528                         var j = 0;
5529                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5530                             if(cn.nodeType == 1){
5531                                cn.nodeIndex = ++j;
5532                             }
5533                         }
5534                         pn._batch = batch;
5535                     }
5536                     if (f == 1) {
5537                         if (l == 0 || n.nodeIndex == l){
5538                             r[++ri] = n;
5539                         }
5540                     } else if ((n.nodeIndex + l) % f == 0){
5541                         r[++ri] = n;
5542                     }
5543                 }
5544
5545                 return r;
5546             },
5547
5548             "only-child" : function(c){
5549                 var r = [], ri = -1;;
5550                 for(var i = 0, ci; ci = c[i]; i++){
5551                     if(!prev(ci) && !next(ci)){
5552                         r[++ri] = ci;
5553                     }
5554                 }
5555                 return r;
5556             },
5557
5558             "empty" : function(c){
5559                 var r = [], ri = -1;
5560                 for(var i = 0, ci; ci = c[i]; i++){
5561                     var cns = ci.childNodes, j = 0, cn, empty = true;
5562                     while(cn = cns[j]){
5563                         ++j;
5564                         if(cn.nodeType == 1 || cn.nodeType == 3){
5565                             empty = false;
5566                             break;
5567                         }
5568                     }
5569                     if(empty){
5570                         r[++ri] = ci;
5571                     }
5572                 }
5573                 return r;
5574             },
5575
5576             "contains" : function(c, v){
5577                 var r = [], ri = -1;
5578                 for(var i = 0, ci; ci = c[i]; i++){
5579                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5580                         r[++ri] = ci;
5581                     }
5582                 }
5583                 return r;
5584             },
5585
5586             "nodeValue" : function(c, v){
5587                 var r = [], ri = -1;
5588                 for(var i = 0, ci; ci = c[i]; i++){
5589                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5590                         r[++ri] = ci;
5591                     }
5592                 }
5593                 return r;
5594             },
5595
5596             "checked" : function(c){
5597                 var r = [], ri = -1;
5598                 for(var i = 0, ci; ci = c[i]; i++){
5599                     if(ci.checked == true){
5600                         r[++ri] = ci;
5601                     }
5602                 }
5603                 return r;
5604             },
5605
5606             "not" : function(c, ss){
5607                 return Roo.DomQuery.filter(c, ss, true);
5608             },
5609
5610             "odd" : function(c){
5611                 return this["nth-child"](c, "odd");
5612             },
5613
5614             "even" : function(c){
5615                 return this["nth-child"](c, "even");
5616             },
5617
5618             "nth" : function(c, a){
5619                 return c[a-1] || [];
5620             },
5621
5622             "first" : function(c){
5623                 return c[0] || [];
5624             },
5625
5626             "last" : function(c){
5627                 return c[c.length-1] || [];
5628             },
5629
5630             "has" : function(c, ss){
5631                 var s = Roo.DomQuery.select;
5632                 var r = [], ri = -1;
5633                 for(var i = 0, ci; ci = c[i]; i++){
5634                     if(s(ss, ci).length > 0){
5635                         r[++ri] = ci;
5636                     }
5637                 }
5638                 return r;
5639             },
5640
5641             "next" : function(c, ss){
5642                 var is = Roo.DomQuery.is;
5643                 var r = [], ri = -1;
5644                 for(var i = 0, ci; ci = c[i]; i++){
5645                     var n = next(ci);
5646                     if(n && is(n, ss)){
5647                         r[++ri] = ci;
5648                     }
5649                 }
5650                 return r;
5651             },
5652
5653             "prev" : function(c, ss){
5654                 var is = Roo.DomQuery.is;
5655                 var r = [], ri = -1;
5656                 for(var i = 0, ci; ci = c[i]; i++){
5657                     var n = prev(ci);
5658                     if(n && is(n, ss)){
5659                         r[++ri] = ci;
5660                     }
5661                 }
5662                 return r;
5663             }
5664         }
5665     };
5666 }();
5667
5668 /**
5669  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5670  * @param {String} path The selector/xpath query
5671  * @param {Node} root (optional) The start of the query (defaults to document).
5672  * @return {Array}
5673  * @member Roo
5674  * @method query
5675  */
5676 Roo.query = Roo.DomQuery.select;
5677 /*
5678  * Based on:
5679  * Ext JS Library 1.1.1
5680  * Copyright(c) 2006-2007, Ext JS, LLC.
5681  *
5682  * Originally Released Under LGPL - original licence link has changed is not relivant.
5683  *
5684  * Fork - LGPL
5685  * <script type="text/javascript">
5686  */
5687
5688 /**
5689  * @class Roo.util.Observable
5690  * Base class that provides a common interface for publishing events. Subclasses are expected to
5691  * to have a property "events" with all the events defined.<br>
5692  * For example:
5693  * <pre><code>
5694  Employee = function(name){
5695     this.name = name;
5696     this.addEvents({
5697         "fired" : true,
5698         "quit" : true
5699     });
5700  }
5701  Roo.extend(Employee, Roo.util.Observable);
5702 </code></pre>
5703  * @param {Object} config properties to use (incuding events / listeners)
5704  */
5705
5706 Roo.util.Observable = function(cfg){
5707     
5708     cfg = cfg|| {};
5709     this.addEvents(cfg.events || {});
5710     if (cfg.events) {
5711         delete cfg.events; // make sure
5712     }
5713      
5714     Roo.apply(this, cfg);
5715     
5716     if(this.listeners){
5717         this.on(this.listeners);
5718         delete this.listeners;
5719     }
5720 };
5721 Roo.util.Observable.prototype = {
5722     /** 
5723  * @cfg {Object} listeners  list of events and functions to call for this object, 
5724  * For example :
5725  * <pre><code>
5726     listeners :  { 
5727        'click' : function(e) {
5728            ..... 
5729         } ,
5730         .... 
5731     } 
5732   </code></pre>
5733  */
5734     
5735     
5736     /**
5737      * Fires the specified event with the passed parameters (minus the event name).
5738      * @param {String} eventName
5739      * @param {Object...} args Variable number of parameters are passed to handlers
5740      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5741      */
5742     fireEvent : function(){
5743         var ce = this.events[arguments[0].toLowerCase()];
5744         if(typeof ce == "object"){
5745             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5746         }else{
5747             return true;
5748         }
5749     },
5750
5751     // private
5752     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5753
5754     /**
5755      * Appends an event handler to this component
5756      * @param {String}   eventName The type of event to listen for
5757      * @param {Function} handler The method the event invokes
5758      * @param {Object}   scope (optional) The scope in which to execute the handler
5759      * function. The handler function's "this" context.
5760      * @param {Object}   options (optional) An object containing handler configuration
5761      * properties. This may contain any of the following properties:<ul>
5762      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5763      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5764      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5765      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5766      * by the specified number of milliseconds. If the event fires again within that time, the original
5767      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5768      * </ul><br>
5769      * <p>
5770      * <b>Combining Options</b><br>
5771      * Using the options argument, it is possible to combine different types of listeners:<br>
5772      * <br>
5773      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5774                 <pre><code>
5775                 el.on('click', this.onClick, this, {
5776                         single: true,
5777                 delay: 100,
5778                 forumId: 4
5779                 });
5780                 </code></pre>
5781      * <p>
5782      * <b>Attaching multiple handlers in 1 call</b><br>
5783      * The method also allows for a single argument to be passed which is a config object containing properties
5784      * which specify multiple handlers.
5785      * <pre><code>
5786                 el.on({
5787                         'click': {
5788                         fn: this.onClick,
5789                         scope: this,
5790                         delay: 100
5791                 }, 
5792                 'mouseover': {
5793                         fn: this.onMouseOver,
5794                         scope: this
5795                 },
5796                 'mouseout': {
5797                         fn: this.onMouseOut,
5798                         scope: this
5799                 }
5800                 });
5801                 </code></pre>
5802      * <p>
5803      * Or a shorthand syntax which passes the same scope object to all handlers:
5804         <pre><code>
5805                 el.on({
5806                         'click': this.onClick,
5807                 'mouseover': this.onMouseOver,
5808                 'mouseout': this.onMouseOut,
5809                 scope: this
5810                 });
5811                 </code></pre>
5812      */
5813     addListener : function(eventName, fn, scope, o){
5814         if(typeof eventName == "object"){
5815             o = eventName;
5816             for(var e in o){
5817                 if(this.filterOptRe.test(e)){
5818                     continue;
5819                 }
5820                 if(typeof o[e] == "function"){
5821                     // shared options
5822                     this.addListener(e, o[e], o.scope,  o);
5823                 }else{
5824                     // individual options
5825                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5826                 }
5827             }
5828             return;
5829         }
5830         o = (!o || typeof o == "boolean") ? {} : o;
5831         eventName = eventName.toLowerCase();
5832         var ce = this.events[eventName] || true;
5833         if(typeof ce == "boolean"){
5834             ce = new Roo.util.Event(this, eventName);
5835             this.events[eventName] = ce;
5836         }
5837         ce.addListener(fn, scope, o);
5838     },
5839
5840     /**
5841      * Removes a listener
5842      * @param {String}   eventName     The type of event to listen for
5843      * @param {Function} handler        The handler to remove
5844      * @param {Object}   scope  (optional) The scope (this object) for the handler
5845      */
5846     removeListener : function(eventName, fn, scope){
5847         var ce = this.events[eventName.toLowerCase()];
5848         if(typeof ce == "object"){
5849             ce.removeListener(fn, scope);
5850         }
5851     },
5852
5853     /**
5854      * Removes all listeners for this object
5855      */
5856     purgeListeners : function(){
5857         for(var evt in this.events){
5858             if(typeof this.events[evt] == "object"){
5859                  this.events[evt].clearListeners();
5860             }
5861         }
5862     },
5863
5864     relayEvents : function(o, events){
5865         var createHandler = function(ename){
5866             return function(){
5867                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5868             };
5869         };
5870         for(var i = 0, len = events.length; i < len; i++){
5871             var ename = events[i];
5872             if(!this.events[ename]){ this.events[ename] = true; };
5873             o.on(ename, createHandler(ename), this);
5874         }
5875     },
5876
5877     /**
5878      * Used to define events on this Observable
5879      * @param {Object} object The object with the events defined
5880      */
5881     addEvents : function(o){
5882         if(!this.events){
5883             this.events = {};
5884         }
5885         Roo.applyIf(this.events, o);
5886     },
5887
5888     /**
5889      * Checks to see if this object has any listeners for a specified event
5890      * @param {String} eventName The name of the event to check for
5891      * @return {Boolean} True if the event is being listened for, else false
5892      */
5893     hasListener : function(eventName){
5894         var e = this.events[eventName];
5895         return typeof e == "object" && e.listeners.length > 0;
5896     }
5897 };
5898 /**
5899  * Appends an event handler to this element (shorthand for addListener)
5900  * @param {String}   eventName     The type of event to listen for
5901  * @param {Function} handler        The method the event invokes
5902  * @param {Object}   scope (optional) The scope in which to execute the handler
5903  * function. The handler function's "this" context.
5904  * @param {Object}   options  (optional)
5905  * @method
5906  */
5907 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5908 /**
5909  * Removes a listener (shorthand for removeListener)
5910  * @param {String}   eventName     The type of event to listen for
5911  * @param {Function} handler        The handler to remove
5912  * @param {Object}   scope  (optional) The scope (this object) for the handler
5913  * @method
5914  */
5915 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5916
5917 /**
5918  * Starts capture on the specified Observable. All events will be passed
5919  * to the supplied function with the event name + standard signature of the event
5920  * <b>before</b> the event is fired. If the supplied function returns false,
5921  * the event will not fire.
5922  * @param {Observable} o The Observable to capture
5923  * @param {Function} fn The function to call
5924  * @param {Object} scope (optional) The scope (this object) for the fn
5925  * @static
5926  */
5927 Roo.util.Observable.capture = function(o, fn, scope){
5928     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5929 };
5930
5931 /**
5932  * Removes <b>all</b> added captures from the Observable.
5933  * @param {Observable} o The Observable to release
5934  * @static
5935  */
5936 Roo.util.Observable.releaseCapture = function(o){
5937     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5938 };
5939
5940 (function(){
5941
5942     var createBuffered = function(h, o, scope){
5943         var task = new Roo.util.DelayedTask();
5944         return function(){
5945             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5946         };
5947     };
5948
5949     var createSingle = function(h, e, fn, scope){
5950         return function(){
5951             e.removeListener(fn, scope);
5952             return h.apply(scope, arguments);
5953         };
5954     };
5955
5956     var createDelayed = function(h, o, scope){
5957         return function(){
5958             var args = Array.prototype.slice.call(arguments, 0);
5959             setTimeout(function(){
5960                 h.apply(scope, args);
5961             }, o.delay || 10);
5962         };
5963     };
5964
5965     Roo.util.Event = function(obj, name){
5966         this.name = name;
5967         this.obj = obj;
5968         this.listeners = [];
5969     };
5970
5971     Roo.util.Event.prototype = {
5972         addListener : function(fn, scope, options){
5973             var o = options || {};
5974             scope = scope || this.obj;
5975             if(!this.isListening(fn, scope)){
5976                 var l = {fn: fn, scope: scope, options: o};
5977                 var h = fn;
5978                 if(o.delay){
5979                     h = createDelayed(h, o, scope);
5980                 }
5981                 if(o.single){
5982                     h = createSingle(h, this, fn, scope);
5983                 }
5984                 if(o.buffer){
5985                     h = createBuffered(h, o, scope);
5986                 }
5987                 l.fireFn = h;
5988                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5989                     this.listeners.push(l);
5990                 }else{
5991                     this.listeners = this.listeners.slice(0);
5992                     this.listeners.push(l);
5993                 }
5994             }
5995         },
5996
5997         findListener : function(fn, scope){
5998             scope = scope || this.obj;
5999             var ls = this.listeners;
6000             for(var i = 0, len = ls.length; i < len; i++){
6001                 var l = ls[i];
6002                 if(l.fn == fn && l.scope == scope){
6003                     return i;
6004                 }
6005             }
6006             return -1;
6007         },
6008
6009         isListening : function(fn, scope){
6010             return this.findListener(fn, scope) != -1;
6011         },
6012
6013         removeListener : function(fn, scope){
6014             var index;
6015             if((index = this.findListener(fn, scope)) != -1){
6016                 if(!this.firing){
6017                     this.listeners.splice(index, 1);
6018                 }else{
6019                     this.listeners = this.listeners.slice(0);
6020                     this.listeners.splice(index, 1);
6021                 }
6022                 return true;
6023             }
6024             return false;
6025         },
6026
6027         clearListeners : function(){
6028             this.listeners = [];
6029         },
6030
6031         fire : function(){
6032             var ls = this.listeners, scope, len = ls.length;
6033             if(len > 0){
6034                 this.firing = true;
6035                 var args = Array.prototype.slice.call(arguments, 0);
6036                 for(var i = 0; i < len; i++){
6037                     var l = ls[i];
6038                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6039                         this.firing = false;
6040                         return false;
6041                     }
6042                 }
6043                 this.firing = false;
6044             }
6045             return true;
6046         }
6047     };
6048 })();/*
6049  * Based on:
6050  * Ext JS Library 1.1.1
6051  * Copyright(c) 2006-2007, Ext JS, LLC.
6052  *
6053  * Originally Released Under LGPL - original licence link has changed is not relivant.
6054  *
6055  * Fork - LGPL
6056  * <script type="text/javascript">
6057  */
6058
6059 /**
6060  * @class Roo.EventManager
6061  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6062  * several useful events directly.
6063  * See {@link Roo.EventObject} for more details on normalized event objects.
6064  * @singleton
6065  */
6066 Roo.EventManager = function(){
6067     var docReadyEvent, docReadyProcId, docReadyState = false;
6068     var resizeEvent, resizeTask, textEvent, textSize;
6069     var E = Roo.lib.Event;
6070     var D = Roo.lib.Dom;
6071
6072     
6073     
6074
6075     var fireDocReady = function(){
6076         if(!docReadyState){
6077             docReadyState = true;
6078             Roo.isReady = true;
6079             if(docReadyProcId){
6080                 clearInterval(docReadyProcId);
6081             }
6082             if(Roo.isGecko || Roo.isOpera) {
6083                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6084             }
6085             if(Roo.isIE){
6086                 var defer = document.getElementById("ie-deferred-loader");
6087                 if(defer){
6088                     defer.onreadystatechange = null;
6089                     defer.parentNode.removeChild(defer);
6090                 }
6091             }
6092             if(docReadyEvent){
6093                 docReadyEvent.fire();
6094                 docReadyEvent.clearListeners();
6095             }
6096         }
6097     };
6098     
6099     var initDocReady = function(){
6100         docReadyEvent = new Roo.util.Event();
6101         if(Roo.isGecko || Roo.isOpera) {
6102             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6103         }else if(Roo.isIE){
6104             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6105             var defer = document.getElementById("ie-deferred-loader");
6106             defer.onreadystatechange = function(){
6107                 if(this.readyState == "complete"){
6108                     fireDocReady();
6109                 }
6110             };
6111         }else if(Roo.isSafari){ 
6112             docReadyProcId = setInterval(function(){
6113                 var rs = document.readyState;
6114                 if(rs == "complete") {
6115                     fireDocReady();     
6116                  }
6117             }, 10);
6118         }
6119         // no matter what, make sure it fires on load
6120         E.on(window, "load", fireDocReady);
6121     };
6122
6123     var createBuffered = function(h, o){
6124         var task = new Roo.util.DelayedTask(h);
6125         return function(e){
6126             // create new event object impl so new events don't wipe out properties
6127             e = new Roo.EventObjectImpl(e);
6128             task.delay(o.buffer, h, null, [e]);
6129         };
6130     };
6131
6132     var createSingle = function(h, el, ename, fn){
6133         return function(e){
6134             Roo.EventManager.removeListener(el, ename, fn);
6135             h(e);
6136         };
6137     };
6138
6139     var createDelayed = function(h, o){
6140         return function(e){
6141             // create new event object impl so new events don't wipe out properties
6142             e = new Roo.EventObjectImpl(e);
6143             setTimeout(function(){
6144                 h(e);
6145             }, o.delay || 10);
6146         };
6147     };
6148     var transitionEndVal = false;
6149     
6150     var transitionEnd = function()
6151     {
6152         if (transitionEndVal) {
6153             return transitionEndVal;
6154         }
6155         var el = document.createElement('div');
6156
6157         var transEndEventNames = {
6158             WebkitTransition : 'webkitTransitionEnd',
6159             MozTransition    : 'transitionend',
6160             OTransition      : 'oTransitionEnd otransitionend',
6161             transition       : 'transitionend'
6162         };
6163     
6164         for (var name in transEndEventNames) {
6165             if (el.style[name] !== undefined) {
6166                 transitionEndVal = transEndEventNames[name];
6167                 return  transitionEndVal ;
6168             }
6169         }
6170     }
6171     
6172
6173     var listen = function(element, ename, opt, fn, scope){
6174         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6175         fn = fn || o.fn; scope = scope || o.scope;
6176         var el = Roo.getDom(element);
6177         
6178         
6179         if(!el){
6180             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6181         }
6182         
6183         if (ename == 'transitionend') {
6184             ename = transitionEnd();
6185         }
6186         var h = function(e){
6187             e = Roo.EventObject.setEvent(e);
6188             var t;
6189             if(o.delegate){
6190                 t = e.getTarget(o.delegate, el);
6191                 if(!t){
6192                     return;
6193                 }
6194             }else{
6195                 t = e.target;
6196             }
6197             if(o.stopEvent === true){
6198                 e.stopEvent();
6199             }
6200             if(o.preventDefault === true){
6201                e.preventDefault();
6202             }
6203             if(o.stopPropagation === true){
6204                 e.stopPropagation();
6205             }
6206
6207             if(o.normalized === false){
6208                 e = e.browserEvent;
6209             }
6210
6211             fn.call(scope || el, e, t, o);
6212         };
6213         if(o.delay){
6214             h = createDelayed(h, o);
6215         }
6216         if(o.single){
6217             h = createSingle(h, el, ename, fn);
6218         }
6219         if(o.buffer){
6220             h = createBuffered(h, o);
6221         }
6222         fn._handlers = fn._handlers || [];
6223         
6224         
6225         fn._handlers.push([Roo.id(el), ename, h]);
6226         
6227         
6228          
6229         E.on(el, ename, h);
6230         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6231             el.addEventListener("DOMMouseScroll", h, false);
6232             E.on(window, 'unload', function(){
6233                 el.removeEventListener("DOMMouseScroll", h, false);
6234             });
6235         }
6236         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6237             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6238         }
6239         return h;
6240     };
6241
6242     var stopListening = function(el, ename, fn){
6243         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6244         if(hds){
6245             for(var i = 0, len = hds.length; i < len; i++){
6246                 var h = hds[i];
6247                 if(h[0] == id && h[1] == ename){
6248                     hd = h[2];
6249                     hds.splice(i, 1);
6250                     break;
6251                 }
6252             }
6253         }
6254         E.un(el, ename, hd);
6255         el = Roo.getDom(el);
6256         if(ename == "mousewheel" && el.addEventListener){
6257             el.removeEventListener("DOMMouseScroll", hd, false);
6258         }
6259         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6260             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6261         }
6262     };
6263
6264     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6265     
6266     var pub = {
6267         
6268         
6269         /** 
6270          * Fix for doc tools
6271          * @scope Roo.EventManager
6272          */
6273         
6274         
6275         /** 
6276          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6277          * object with a Roo.EventObject
6278          * @param {Function} fn        The method the event invokes
6279          * @param {Object}   scope    An object that becomes the scope of the handler
6280          * @param {boolean}  override If true, the obj passed in becomes
6281          *                             the execution scope of the listener
6282          * @return {Function} The wrapped function
6283          * @deprecated
6284          */
6285         wrap : function(fn, scope, override){
6286             return function(e){
6287                 Roo.EventObject.setEvent(e);
6288                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6289             };
6290         },
6291         
6292         /**
6293      * Appends an event handler to an element (shorthand for addListener)
6294      * @param {String/HTMLElement}   element        The html element or id to assign the
6295      * @param {String}   eventName The type of event to listen for
6296      * @param {Function} handler The method the event invokes
6297      * @param {Object}   scope (optional) The scope in which to execute the handler
6298      * function. The handler function's "this" context.
6299      * @param {Object}   options (optional) An object containing handler configuration
6300      * properties. This may contain any of the following properties:<ul>
6301      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6302      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6303      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6304      * <li>preventDefault {Boolean} True to prevent the default action</li>
6305      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6306      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6307      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6308      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6309      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6310      * by the specified number of milliseconds. If the event fires again within that time, the original
6311      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6312      * </ul><br>
6313      * <p>
6314      * <b>Combining Options</b><br>
6315      * Using the options argument, it is possible to combine different types of listeners:<br>
6316      * <br>
6317      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6318      * Code:<pre><code>
6319 el.on('click', this.onClick, this, {
6320     single: true,
6321     delay: 100,
6322     stopEvent : true,
6323     forumId: 4
6324 });</code></pre>
6325      * <p>
6326      * <b>Attaching multiple handlers in 1 call</b><br>
6327       * The method also allows for a single argument to be passed which is a config object containing properties
6328      * which specify multiple handlers.
6329      * <p>
6330      * Code:<pre><code>
6331 el.on({
6332     'click' : {
6333         fn: this.onClick
6334         scope: this,
6335         delay: 100
6336     },
6337     'mouseover' : {
6338         fn: this.onMouseOver
6339         scope: this
6340     },
6341     'mouseout' : {
6342         fn: this.onMouseOut
6343         scope: this
6344     }
6345 });</code></pre>
6346      * <p>
6347      * Or a shorthand syntax:<br>
6348      * Code:<pre><code>
6349 el.on({
6350     'click' : this.onClick,
6351     'mouseover' : this.onMouseOver,
6352     'mouseout' : this.onMouseOut
6353     scope: this
6354 });</code></pre>
6355      */
6356         addListener : function(element, eventName, fn, scope, options){
6357             if(typeof eventName == "object"){
6358                 var o = eventName;
6359                 for(var e in o){
6360                     if(propRe.test(e)){
6361                         continue;
6362                     }
6363                     if(typeof o[e] == "function"){
6364                         // shared options
6365                         listen(element, e, o, o[e], o.scope);
6366                     }else{
6367                         // individual options
6368                         listen(element, e, o[e]);
6369                     }
6370                 }
6371                 return;
6372             }
6373             return listen(element, eventName, options, fn, scope);
6374         },
6375         
6376         /**
6377          * Removes an event handler
6378          *
6379          * @param {String/HTMLElement}   element        The id or html element to remove the 
6380          *                             event from
6381          * @param {String}   eventName     The type of event
6382          * @param {Function} fn
6383          * @return {Boolean} True if a listener was actually removed
6384          */
6385         removeListener : function(element, eventName, fn){
6386             return stopListening(element, eventName, fn);
6387         },
6388         
6389         /**
6390          * Fires when the document is ready (before onload and before images are loaded). Can be 
6391          * accessed shorthanded Roo.onReady().
6392          * @param {Function} fn        The method the event invokes
6393          * @param {Object}   scope    An  object that becomes the scope of the handler
6394          * @param {boolean}  options
6395          */
6396         onDocumentReady : function(fn, scope, options){
6397             if(docReadyState){ // if it already fired
6398                 docReadyEvent.addListener(fn, scope, options);
6399                 docReadyEvent.fire();
6400                 docReadyEvent.clearListeners();
6401                 return;
6402             }
6403             if(!docReadyEvent){
6404                 initDocReady();
6405             }
6406             docReadyEvent.addListener(fn, scope, options);
6407         },
6408         
6409         /**
6410          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6411          * @param {Function} fn        The method the event invokes
6412          * @param {Object}   scope    An object that becomes the scope of the handler
6413          * @param {boolean}  options
6414          */
6415         onWindowResize : function(fn, scope, options){
6416             if(!resizeEvent){
6417                 resizeEvent = new Roo.util.Event();
6418                 resizeTask = new Roo.util.DelayedTask(function(){
6419                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6420                 });
6421                 E.on(window, "resize", function(){
6422                     if(Roo.isIE){
6423                         resizeTask.delay(50);
6424                     }else{
6425                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6426                     }
6427                 });
6428             }
6429             resizeEvent.addListener(fn, scope, options);
6430         },
6431
6432         /**
6433          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6434          * @param {Function} fn        The method the event invokes
6435          * @param {Object}   scope    An object that becomes the scope of the handler
6436          * @param {boolean}  options
6437          */
6438         onTextResize : function(fn, scope, options){
6439             if(!textEvent){
6440                 textEvent = new Roo.util.Event();
6441                 var textEl = new Roo.Element(document.createElement('div'));
6442                 textEl.dom.className = 'x-text-resize';
6443                 textEl.dom.innerHTML = 'X';
6444                 textEl.appendTo(document.body);
6445                 textSize = textEl.dom.offsetHeight;
6446                 setInterval(function(){
6447                     if(textEl.dom.offsetHeight != textSize){
6448                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6449                     }
6450                 }, this.textResizeInterval);
6451             }
6452             textEvent.addListener(fn, scope, options);
6453         },
6454
6455         /**
6456          * Removes the passed window resize listener.
6457          * @param {Function} fn        The method the event invokes
6458          * @param {Object}   scope    The scope of handler
6459          */
6460         removeResizeListener : function(fn, scope){
6461             if(resizeEvent){
6462                 resizeEvent.removeListener(fn, scope);
6463             }
6464         },
6465
6466         // private
6467         fireResize : function(){
6468             if(resizeEvent){
6469                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6470             }   
6471         },
6472         /**
6473          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6474          */
6475         ieDeferSrc : false,
6476         /**
6477          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6478          */
6479         textResizeInterval : 50
6480     };
6481     
6482     /**
6483      * Fix for doc tools
6484      * @scopeAlias pub=Roo.EventManager
6485      */
6486     
6487      /**
6488      * Appends an event handler to an element (shorthand for addListener)
6489      * @param {String/HTMLElement}   element        The html element or id to assign the
6490      * @param {String}   eventName The type of event to listen for
6491      * @param {Function} handler The method the event invokes
6492      * @param {Object}   scope (optional) The scope in which to execute the handler
6493      * function. The handler function's "this" context.
6494      * @param {Object}   options (optional) An object containing handler configuration
6495      * properties. This may contain any of the following properties:<ul>
6496      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6497      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6498      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6499      * <li>preventDefault {Boolean} True to prevent the default action</li>
6500      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6501      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6502      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6503      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6504      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6505      * by the specified number of milliseconds. If the event fires again within that time, the original
6506      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6507      * </ul><br>
6508      * <p>
6509      * <b>Combining Options</b><br>
6510      * Using the options argument, it is possible to combine different types of listeners:<br>
6511      * <br>
6512      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6513      * Code:<pre><code>
6514 el.on('click', this.onClick, this, {
6515     single: true,
6516     delay: 100,
6517     stopEvent : true,
6518     forumId: 4
6519 });</code></pre>
6520      * <p>
6521      * <b>Attaching multiple handlers in 1 call</b><br>
6522       * The method also allows for a single argument to be passed which is a config object containing properties
6523      * which specify multiple handlers.
6524      * <p>
6525      * Code:<pre><code>
6526 el.on({
6527     'click' : {
6528         fn: this.onClick
6529         scope: this,
6530         delay: 100
6531     },
6532     'mouseover' : {
6533         fn: this.onMouseOver
6534         scope: this
6535     },
6536     'mouseout' : {
6537         fn: this.onMouseOut
6538         scope: this
6539     }
6540 });</code></pre>
6541      * <p>
6542      * Or a shorthand syntax:<br>
6543      * Code:<pre><code>
6544 el.on({
6545     'click' : this.onClick,
6546     'mouseover' : this.onMouseOver,
6547     'mouseout' : this.onMouseOut
6548     scope: this
6549 });</code></pre>
6550      */
6551     pub.on = pub.addListener;
6552     pub.un = pub.removeListener;
6553
6554     pub.stoppedMouseDownEvent = new Roo.util.Event();
6555     return pub;
6556 }();
6557 /**
6558   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6559   * @param {Function} fn        The method the event invokes
6560   * @param {Object}   scope    An  object that becomes the scope of the handler
6561   * @param {boolean}  override If true, the obj passed in becomes
6562   *                             the execution scope of the listener
6563   * @member Roo
6564   * @method onReady
6565  */
6566 Roo.onReady = Roo.EventManager.onDocumentReady;
6567
6568 Roo.onReady(function(){
6569     var bd = Roo.get(document.body);
6570     if(!bd){ return; }
6571
6572     var cls = [
6573             Roo.isIE ? "roo-ie"
6574             : Roo.isGecko ? "roo-gecko"
6575             : Roo.isOpera ? "roo-opera"
6576             : Roo.isSafari ? "roo-safari" : ""];
6577
6578     if(Roo.isMac){
6579         cls.push("roo-mac");
6580     }
6581     if(Roo.isLinux){
6582         cls.push("roo-linux");
6583     }
6584     if(Roo.isIOS){
6585         cls.push("roo-ios");
6586     }
6587     if(Roo.isBorderBox){
6588         cls.push('roo-border-box');
6589     }
6590     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6591         var p = bd.dom.parentNode;
6592         if(p){
6593             p.className += ' roo-strict';
6594         }
6595     }
6596     bd.addClass(cls.join(' '));
6597 });
6598
6599 /**
6600  * @class Roo.EventObject
6601  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6602  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6603  * Example:
6604  * <pre><code>
6605  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6606     e.preventDefault();
6607     var target = e.getTarget();
6608     ...
6609  }
6610  var myDiv = Roo.get("myDiv");
6611  myDiv.on("click", handleClick);
6612  //or
6613  Roo.EventManager.on("myDiv", 'click', handleClick);
6614  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6615  </code></pre>
6616  * @singleton
6617  */
6618 Roo.EventObject = function(){
6619     
6620     var E = Roo.lib.Event;
6621     
6622     // safari keypress events for special keys return bad keycodes
6623     var safariKeys = {
6624         63234 : 37, // left
6625         63235 : 39, // right
6626         63232 : 38, // up
6627         63233 : 40, // down
6628         63276 : 33, // page up
6629         63277 : 34, // page down
6630         63272 : 46, // delete
6631         63273 : 36, // home
6632         63275 : 35  // end
6633     };
6634
6635     // normalize button clicks
6636     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6637                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6638
6639     Roo.EventObjectImpl = function(e){
6640         if(e){
6641             this.setEvent(e.browserEvent || e);
6642         }
6643     };
6644     Roo.EventObjectImpl.prototype = {
6645         /**
6646          * Used to fix doc tools.
6647          * @scope Roo.EventObject.prototype
6648          */
6649             
6650
6651         
6652         
6653         /** The normal browser event */
6654         browserEvent : null,
6655         /** The button pressed in a mouse event */
6656         button : -1,
6657         /** True if the shift key was down during the event */
6658         shiftKey : false,
6659         /** True if the control key was down during the event */
6660         ctrlKey : false,
6661         /** True if the alt key was down during the event */
6662         altKey : false,
6663
6664         /** Key constant 
6665         * @type Number */
6666         BACKSPACE : 8,
6667         /** Key constant 
6668         * @type Number */
6669         TAB : 9,
6670         /** Key constant 
6671         * @type Number */
6672         RETURN : 13,
6673         /** Key constant 
6674         * @type Number */
6675         ENTER : 13,
6676         /** Key constant 
6677         * @type Number */
6678         SHIFT : 16,
6679         /** Key constant 
6680         * @type Number */
6681         CONTROL : 17,
6682         /** Key constant 
6683         * @type Number */
6684         ESC : 27,
6685         /** Key constant 
6686         * @type Number */
6687         SPACE : 32,
6688         /** Key constant 
6689         * @type Number */
6690         PAGEUP : 33,
6691         /** Key constant 
6692         * @type Number */
6693         PAGEDOWN : 34,
6694         /** Key constant 
6695         * @type Number */
6696         END : 35,
6697         /** Key constant 
6698         * @type Number */
6699         HOME : 36,
6700         /** Key constant 
6701         * @type Number */
6702         LEFT : 37,
6703         /** Key constant 
6704         * @type Number */
6705         UP : 38,
6706         /** Key constant 
6707         * @type Number */
6708         RIGHT : 39,
6709         /** Key constant 
6710         * @type Number */
6711         DOWN : 40,
6712         /** Key constant 
6713         * @type Number */
6714         DELETE : 46,
6715         /** Key constant 
6716         * @type Number */
6717         F5 : 116,
6718
6719            /** @private */
6720         setEvent : function(e){
6721             if(e == this || (e && e.browserEvent)){ // already wrapped
6722                 return e;
6723             }
6724             this.browserEvent = e;
6725             if(e){
6726                 // normalize buttons
6727                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6728                 if(e.type == 'click' && this.button == -1){
6729                     this.button = 0;
6730                 }
6731                 this.type = e.type;
6732                 this.shiftKey = e.shiftKey;
6733                 // mac metaKey behaves like ctrlKey
6734                 this.ctrlKey = e.ctrlKey || e.metaKey;
6735                 this.altKey = e.altKey;
6736                 // in getKey these will be normalized for the mac
6737                 this.keyCode = e.keyCode;
6738                 // keyup warnings on firefox.
6739                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6740                 // cache the target for the delayed and or buffered events
6741                 this.target = E.getTarget(e);
6742                 // same for XY
6743                 this.xy = E.getXY(e);
6744             }else{
6745                 this.button = -1;
6746                 this.shiftKey = false;
6747                 this.ctrlKey = false;
6748                 this.altKey = false;
6749                 this.keyCode = 0;
6750                 this.charCode =0;
6751                 this.target = null;
6752                 this.xy = [0, 0];
6753             }
6754             return this;
6755         },
6756
6757         /**
6758          * Stop the event (preventDefault and stopPropagation)
6759          */
6760         stopEvent : function(){
6761             if(this.browserEvent){
6762                 if(this.browserEvent.type == 'mousedown'){
6763                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6764                 }
6765                 E.stopEvent(this.browserEvent);
6766             }
6767         },
6768
6769         /**
6770          * Prevents the browsers default handling of the event.
6771          */
6772         preventDefault : function(){
6773             if(this.browserEvent){
6774                 E.preventDefault(this.browserEvent);
6775             }
6776         },
6777
6778         /** @private */
6779         isNavKeyPress : function(){
6780             var k = this.keyCode;
6781             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6782             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6783         },
6784
6785         isSpecialKey : function(){
6786             var k = this.keyCode;
6787             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6788             (k == 16) || (k == 17) ||
6789             (k >= 18 && k <= 20) ||
6790             (k >= 33 && k <= 35) ||
6791             (k >= 36 && k <= 39) ||
6792             (k >= 44 && k <= 45);
6793         },
6794         /**
6795          * Cancels bubbling of the event.
6796          */
6797         stopPropagation : function(){
6798             if(this.browserEvent){
6799                 if(this.type == 'mousedown'){
6800                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6801                 }
6802                 E.stopPropagation(this.browserEvent);
6803             }
6804         },
6805
6806         /**
6807          * Gets the key code for the event.
6808          * @return {Number}
6809          */
6810         getCharCode : function(){
6811             return this.charCode || this.keyCode;
6812         },
6813
6814         /**
6815          * Returns a normalized keyCode for the event.
6816          * @return {Number} The key code
6817          */
6818         getKey : function(){
6819             var k = this.keyCode || this.charCode;
6820             return Roo.isSafari ? (safariKeys[k] || k) : k;
6821         },
6822
6823         /**
6824          * Gets the x coordinate of the event.
6825          * @return {Number}
6826          */
6827         getPageX : function(){
6828             return this.xy[0];
6829         },
6830
6831         /**
6832          * Gets the y coordinate of the event.
6833          * @return {Number}
6834          */
6835         getPageY : function(){
6836             return this.xy[1];
6837         },
6838
6839         /**
6840          * Gets the time of the event.
6841          * @return {Number}
6842          */
6843         getTime : function(){
6844             if(this.browserEvent){
6845                 return E.getTime(this.browserEvent);
6846             }
6847             return null;
6848         },
6849
6850         /**
6851          * Gets the page coordinates of the event.
6852          * @return {Array} The xy values like [x, y]
6853          */
6854         getXY : function(){
6855             return this.xy;
6856         },
6857
6858         /**
6859          * Gets the target for the event.
6860          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6861          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6862                 search as a number or element (defaults to 10 || document.body)
6863          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6864          * @return {HTMLelement}
6865          */
6866         getTarget : function(selector, maxDepth, returnEl){
6867             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6868         },
6869         /**
6870          * Gets the related target.
6871          * @return {HTMLElement}
6872          */
6873         getRelatedTarget : function(){
6874             if(this.browserEvent){
6875                 return E.getRelatedTarget(this.browserEvent);
6876             }
6877             return null;
6878         },
6879
6880         /**
6881          * Normalizes mouse wheel delta across browsers
6882          * @return {Number} The delta
6883          */
6884         getWheelDelta : function(){
6885             var e = this.browserEvent;
6886             var delta = 0;
6887             if(e.wheelDelta){ /* IE/Opera. */
6888                 delta = e.wheelDelta/120;
6889             }else if(e.detail){ /* Mozilla case. */
6890                 delta = -e.detail/3;
6891             }
6892             return delta;
6893         },
6894
6895         /**
6896          * Returns true if the control, meta, shift or alt key was pressed during this event.
6897          * @return {Boolean}
6898          */
6899         hasModifier : function(){
6900             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6901         },
6902
6903         /**
6904          * Returns true if the target of this event equals el or is a child of el
6905          * @param {String/HTMLElement/Element} el
6906          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6907          * @return {Boolean}
6908          */
6909         within : function(el, related){
6910             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6911             return t && Roo.fly(el).contains(t);
6912         },
6913
6914         getPoint : function(){
6915             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6916         }
6917     };
6918
6919     return new Roo.EventObjectImpl();
6920 }();
6921             
6922     /*
6923  * Based on:
6924  * Ext JS Library 1.1.1
6925  * Copyright(c) 2006-2007, Ext JS, LLC.
6926  *
6927  * Originally Released Under LGPL - original licence link has changed is not relivant.
6928  *
6929  * Fork - LGPL
6930  * <script type="text/javascript">
6931  */
6932
6933  
6934 // was in Composite Element!??!?!
6935  
6936 (function(){
6937     var D = Roo.lib.Dom;
6938     var E = Roo.lib.Event;
6939     var A = Roo.lib.Anim;
6940
6941     // local style camelizing for speed
6942     var propCache = {};
6943     var camelRe = /(-[a-z])/gi;
6944     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6945     var view = document.defaultView;
6946
6947 /**
6948  * @class Roo.Element
6949  * Represents an Element in the DOM.<br><br>
6950  * Usage:<br>
6951 <pre><code>
6952 var el = Roo.get("my-div");
6953
6954 // or with getEl
6955 var el = getEl("my-div");
6956
6957 // or with a DOM element
6958 var el = Roo.get(myDivElement);
6959 </code></pre>
6960  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6961  * each call instead of constructing a new one.<br><br>
6962  * <b>Animations</b><br />
6963  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6964  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6965 <pre>
6966 Option    Default   Description
6967 --------- --------  ---------------------------------------------
6968 duration  .35       The duration of the animation in seconds
6969 easing    easeOut   The YUI easing method
6970 callback  none      A function to execute when the anim completes
6971 scope     this      The scope (this) of the callback function
6972 </pre>
6973 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6974 * manipulate the animation. Here's an example:
6975 <pre><code>
6976 var el = Roo.get("my-div");
6977
6978 // no animation
6979 el.setWidth(100);
6980
6981 // default animation
6982 el.setWidth(100, true);
6983
6984 // animation with some options set
6985 el.setWidth(100, {
6986     duration: 1,
6987     callback: this.foo,
6988     scope: this
6989 });
6990
6991 // using the "anim" property to get the Anim object
6992 var opt = {
6993     duration: 1,
6994     callback: this.foo,
6995     scope: this
6996 };
6997 el.setWidth(100, opt);
6998 ...
6999 if(opt.anim.isAnimated()){
7000     opt.anim.stop();
7001 }
7002 </code></pre>
7003 * <b> Composite (Collections of) Elements</b><br />
7004  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7005  * @constructor Create a new Element directly.
7006  * @param {String/HTMLElement} element
7007  * @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).
7008  */
7009     Roo.Element = function(element, forceNew){
7010         var dom = typeof element == "string" ?
7011                 document.getElementById(element) : element;
7012         if(!dom){ // invalid id/element
7013             return null;
7014         }
7015         var id = dom.id;
7016         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7017             return Roo.Element.cache[id];
7018         }
7019
7020         /**
7021          * The DOM element
7022          * @type HTMLElement
7023          */
7024         this.dom = dom;
7025
7026         /**
7027          * The DOM element ID
7028          * @type String
7029          */
7030         this.id = id || Roo.id(dom);
7031     };
7032
7033     var El = Roo.Element;
7034
7035     El.prototype = {
7036         /**
7037          * The element's default display mode  (defaults to "")
7038          * @type String
7039          */
7040         originalDisplay : "",
7041
7042         visibilityMode : 1,
7043         /**
7044          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7045          * @type String
7046          */
7047         defaultUnit : "px",
7048         
7049         pxReg : '/^\d+(?:\.\d*)?px$/i',
7050         /**
7051          * Sets the element's visibility mode. When setVisible() is called it
7052          * will use this to determine whether to set the visibility or the display property.
7053          * @param visMode Element.VISIBILITY or Element.DISPLAY
7054          * @return {Roo.Element} this
7055          */
7056         setVisibilityMode : function(visMode){
7057             this.visibilityMode = visMode;
7058             return this;
7059         },
7060         /**
7061          * Convenience method for setVisibilityMode(Element.DISPLAY)
7062          * @param {String} display (optional) What to set display to when visible
7063          * @return {Roo.Element} this
7064          */
7065         enableDisplayMode : function(display){
7066             this.setVisibilityMode(El.DISPLAY);
7067             if(typeof display != "undefined") this.originalDisplay = display;
7068             return this;
7069         },
7070
7071         /**
7072          * 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)
7073          * @param {String} selector The simple selector to test
7074          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7075                 search as a number or element (defaults to 10 || document.body)
7076          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7077          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7078          */
7079         findParent : function(simpleSelector, maxDepth, returnEl){
7080             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7081             maxDepth = maxDepth || 50;
7082             if(typeof maxDepth != "number"){
7083                 stopEl = Roo.getDom(maxDepth);
7084                 maxDepth = 10;
7085             }
7086             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7087                 if(dq.is(p, simpleSelector)){
7088                     return returnEl ? Roo.get(p) : p;
7089                 }
7090                 depth++;
7091                 p = p.parentNode;
7092             }
7093             return null;
7094         },
7095
7096
7097         /**
7098          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7099          * @param {String} selector The simple selector to test
7100          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7101                 search as a number or element (defaults to 10 || document.body)
7102          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7103          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7104          */
7105         findParentNode : function(simpleSelector, maxDepth, returnEl){
7106             var p = Roo.fly(this.dom.parentNode, '_internal');
7107             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7108         },
7109
7110         /**
7111          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7112          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7113          * @param {String} selector The simple selector to test
7114          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7115                 search as a number or element (defaults to 10 || document.body)
7116          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7117          */
7118         up : function(simpleSelector, maxDepth){
7119             return this.findParentNode(simpleSelector, maxDepth, true);
7120         },
7121
7122
7123
7124         /**
7125          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7126          * @param {String} selector The simple selector to test
7127          * @return {Boolean} True if this element matches the selector, else false
7128          */
7129         is : function(simpleSelector){
7130             return Roo.DomQuery.is(this.dom, simpleSelector);
7131         },
7132
7133         /**
7134          * Perform animation on this element.
7135          * @param {Object} args The YUI animation control args
7136          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7137          * @param {Function} onComplete (optional) Function to call when animation completes
7138          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7139          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7140          * @return {Roo.Element} this
7141          */
7142         animate : function(args, duration, onComplete, easing, animType){
7143             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7144             return this;
7145         },
7146
7147         /*
7148          * @private Internal animation call
7149          */
7150         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7151             animType = animType || 'run';
7152             opt = opt || {};
7153             var anim = Roo.lib.Anim[animType](
7154                 this.dom, args,
7155                 (opt.duration || defaultDur) || .35,
7156                 (opt.easing || defaultEase) || 'easeOut',
7157                 function(){
7158                     Roo.callback(cb, this);
7159                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7160                 },
7161                 this
7162             );
7163             opt.anim = anim;
7164             return anim;
7165         },
7166
7167         // private legacy anim prep
7168         preanim : function(a, i){
7169             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7170         },
7171
7172         /**
7173          * Removes worthless text nodes
7174          * @param {Boolean} forceReclean (optional) By default the element
7175          * keeps track if it has been cleaned already so
7176          * you can call this over and over. However, if you update the element and
7177          * need to force a reclean, you can pass true.
7178          */
7179         clean : function(forceReclean){
7180             if(this.isCleaned && forceReclean !== true){
7181                 return this;
7182             }
7183             var ns = /\S/;
7184             var d = this.dom, n = d.firstChild, ni = -1;
7185             while(n){
7186                 var nx = n.nextSibling;
7187                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7188                     d.removeChild(n);
7189                 }else{
7190                     n.nodeIndex = ++ni;
7191                 }
7192                 n = nx;
7193             }
7194             this.isCleaned = true;
7195             return this;
7196         },
7197
7198         // private
7199         calcOffsetsTo : function(el){
7200             el = Roo.get(el);
7201             var d = el.dom;
7202             var restorePos = false;
7203             if(el.getStyle('position') == 'static'){
7204                 el.position('relative');
7205                 restorePos = true;
7206             }
7207             var x = 0, y =0;
7208             var op = this.dom;
7209             while(op && op != d && op.tagName != 'HTML'){
7210                 x+= op.offsetLeft;
7211                 y+= op.offsetTop;
7212                 op = op.offsetParent;
7213             }
7214             if(restorePos){
7215                 el.position('static');
7216             }
7217             return [x, y];
7218         },
7219
7220         /**
7221          * Scrolls this element into view within the passed container.
7222          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7223          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7224          * @return {Roo.Element} this
7225          */
7226         scrollIntoView : function(container, hscroll){
7227             var c = Roo.getDom(container) || document.body;
7228             var el = this.dom;
7229
7230             var o = this.calcOffsetsTo(c),
7231                 l = o[0],
7232                 t = o[1],
7233                 b = t+el.offsetHeight,
7234                 r = l+el.offsetWidth;
7235
7236             var ch = c.clientHeight;
7237             var ct = parseInt(c.scrollTop, 10);
7238             var cl = parseInt(c.scrollLeft, 10);
7239             var cb = ct + ch;
7240             var cr = cl + c.clientWidth;
7241
7242             if(t < ct){
7243                 c.scrollTop = t;
7244             }else if(b > cb){
7245                 c.scrollTop = b-ch;
7246             }
7247
7248             if(hscroll !== false){
7249                 if(l < cl){
7250                     c.scrollLeft = l;
7251                 }else if(r > cr){
7252                     c.scrollLeft = r-c.clientWidth;
7253                 }
7254             }
7255             return this;
7256         },
7257
7258         // private
7259         scrollChildIntoView : function(child, hscroll){
7260             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7261         },
7262
7263         /**
7264          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7265          * the new height may not be available immediately.
7266          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7267          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7268          * @param {Function} onComplete (optional) Function to call when animation completes
7269          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7270          * @return {Roo.Element} this
7271          */
7272         autoHeight : function(animate, duration, onComplete, easing){
7273             var oldHeight = this.getHeight();
7274             this.clip();
7275             this.setHeight(1); // force clipping
7276             setTimeout(function(){
7277                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7278                 if(!animate){
7279                     this.setHeight(height);
7280                     this.unclip();
7281                     if(typeof onComplete == "function"){
7282                         onComplete();
7283                     }
7284                 }else{
7285                     this.setHeight(oldHeight); // restore original height
7286                     this.setHeight(height, animate, duration, function(){
7287                         this.unclip();
7288                         if(typeof onComplete == "function") onComplete();
7289                     }.createDelegate(this), easing);
7290                 }
7291             }.createDelegate(this), 0);
7292             return this;
7293         },
7294
7295         /**
7296          * Returns true if this element is an ancestor of the passed element
7297          * @param {HTMLElement/String} el The element to check
7298          * @return {Boolean} True if this element is an ancestor of el, else false
7299          */
7300         contains : function(el){
7301             if(!el){return false;}
7302             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7303         },
7304
7305         /**
7306          * Checks whether the element is currently visible using both visibility and display properties.
7307          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7308          * @return {Boolean} True if the element is currently visible, else false
7309          */
7310         isVisible : function(deep) {
7311             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7312             if(deep !== true || !vis){
7313                 return vis;
7314             }
7315             var p = this.dom.parentNode;
7316             while(p && p.tagName.toLowerCase() != "body"){
7317                 if(!Roo.fly(p, '_isVisible').isVisible()){
7318                     return false;
7319                 }
7320                 p = p.parentNode;
7321             }
7322             return true;
7323         },
7324
7325         /**
7326          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7327          * @param {String} selector The CSS selector
7328          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7329          * @return {CompositeElement/CompositeElementLite} The composite element
7330          */
7331         select : function(selector, unique){
7332             return El.select(selector, unique, this.dom);
7333         },
7334
7335         /**
7336          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7337          * @param {String} selector The CSS selector
7338          * @return {Array} An array of the matched nodes
7339          */
7340         query : function(selector, unique){
7341             return Roo.DomQuery.select(selector, this.dom);
7342         },
7343
7344         /**
7345          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7346          * @param {String} selector The CSS selector
7347          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7348          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7349          */
7350         child : function(selector, returnDom){
7351             var n = Roo.DomQuery.selectNode(selector, this.dom);
7352             return returnDom ? n : Roo.get(n);
7353         },
7354
7355         /**
7356          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7357          * @param {String} selector The CSS selector
7358          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7359          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7360          */
7361         down : function(selector, returnDom){
7362             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7363             return returnDom ? n : Roo.get(n);
7364         },
7365
7366         /**
7367          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7368          * @param {String} group The group the DD object is member of
7369          * @param {Object} config The DD config object
7370          * @param {Object} overrides An object containing methods to override/implement on the DD object
7371          * @return {Roo.dd.DD} The DD object
7372          */
7373         initDD : function(group, config, overrides){
7374             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7375             return Roo.apply(dd, overrides);
7376         },
7377
7378         /**
7379          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7380          * @param {String} group The group the DDProxy object is member of
7381          * @param {Object} config The DDProxy config object
7382          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7383          * @return {Roo.dd.DDProxy} The DDProxy object
7384          */
7385         initDDProxy : function(group, config, overrides){
7386             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7387             return Roo.apply(dd, overrides);
7388         },
7389
7390         /**
7391          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7392          * @param {String} group The group the DDTarget object is member of
7393          * @param {Object} config The DDTarget config object
7394          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7395          * @return {Roo.dd.DDTarget} The DDTarget object
7396          */
7397         initDDTarget : function(group, config, overrides){
7398             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7399             return Roo.apply(dd, overrides);
7400         },
7401
7402         /**
7403          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7404          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7405          * @param {Boolean} visible Whether the element is visible
7406          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7407          * @return {Roo.Element} this
7408          */
7409          setVisible : function(visible, animate){
7410             if(!animate || !A){
7411                 if(this.visibilityMode == El.DISPLAY){
7412                     this.setDisplayed(visible);
7413                 }else{
7414                     this.fixDisplay();
7415                     this.dom.style.visibility = visible ? "visible" : "hidden";
7416                 }
7417             }else{
7418                 // closure for composites
7419                 var dom = this.dom;
7420                 var visMode = this.visibilityMode;
7421                 if(visible){
7422                     this.setOpacity(.01);
7423                     this.setVisible(true);
7424                 }
7425                 this.anim({opacity: { to: (visible?1:0) }},
7426                       this.preanim(arguments, 1),
7427                       null, .35, 'easeIn', function(){
7428                          if(!visible){
7429                              if(visMode == El.DISPLAY){
7430                                  dom.style.display = "none";
7431                              }else{
7432                                  dom.style.visibility = "hidden";
7433                              }
7434                              Roo.get(dom).setOpacity(1);
7435                          }
7436                      });
7437             }
7438             return this;
7439         },
7440
7441         /**
7442          * Returns true if display is not "none"
7443          * @return {Boolean}
7444          */
7445         isDisplayed : function() {
7446             return this.getStyle("display") != "none";
7447         },
7448
7449         /**
7450          * Toggles the element's visibility or display, depending on visibility mode.
7451          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7452          * @return {Roo.Element} this
7453          */
7454         toggle : function(animate){
7455             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7456             return this;
7457         },
7458
7459         /**
7460          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7461          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7462          * @return {Roo.Element} this
7463          */
7464         setDisplayed : function(value) {
7465             if(typeof value == "boolean"){
7466                value = value ? this.originalDisplay : "none";
7467             }
7468             this.setStyle("display", value);
7469             return this;
7470         },
7471
7472         /**
7473          * Tries to focus the element. Any exceptions are caught and ignored.
7474          * @return {Roo.Element} this
7475          */
7476         focus : function() {
7477             try{
7478                 this.dom.focus();
7479             }catch(e){}
7480             return this;
7481         },
7482
7483         /**
7484          * Tries to blur the element. Any exceptions are caught and ignored.
7485          * @return {Roo.Element} this
7486          */
7487         blur : function() {
7488             try{
7489                 this.dom.blur();
7490             }catch(e){}
7491             return this;
7492         },
7493
7494         /**
7495          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7496          * @param {String/Array} className The CSS class to add, or an array of classes
7497          * @return {Roo.Element} this
7498          */
7499         addClass : function(className){
7500             if(className instanceof Array){
7501                 for(var i = 0, len = className.length; i < len; i++) {
7502                     this.addClass(className[i]);
7503                 }
7504             }else{
7505                 if(className && !this.hasClass(className)){
7506                     this.dom.className = this.dom.className + " " + className;
7507                 }
7508             }
7509             return this;
7510         },
7511
7512         /**
7513          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7514          * @param {String/Array} className The CSS class to add, or an array of classes
7515          * @return {Roo.Element} this
7516          */
7517         radioClass : function(className){
7518             var siblings = this.dom.parentNode.childNodes;
7519             for(var i = 0; i < siblings.length; i++) {
7520                 var s = siblings[i];
7521                 if(s.nodeType == 1){
7522                     Roo.get(s).removeClass(className);
7523                 }
7524             }
7525             this.addClass(className);
7526             return this;
7527         },
7528
7529         /**
7530          * Removes one or more CSS classes from the element.
7531          * @param {String/Array} className The CSS class to remove, or an array of classes
7532          * @return {Roo.Element} this
7533          */
7534         removeClass : function(className){
7535             if(!className || !this.dom.className){
7536                 return this;
7537             }
7538             if(className instanceof Array){
7539                 for(var i = 0, len = className.length; i < len; i++) {
7540                     this.removeClass(className[i]);
7541                 }
7542             }else{
7543                 if(this.hasClass(className)){
7544                     var re = this.classReCache[className];
7545                     if (!re) {
7546                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7547                        this.classReCache[className] = re;
7548                     }
7549                     this.dom.className =
7550                         this.dom.className.replace(re, " ");
7551                 }
7552             }
7553             return this;
7554         },
7555
7556         // private
7557         classReCache: {},
7558
7559         /**
7560          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7561          * @param {String} className The CSS class to toggle
7562          * @return {Roo.Element} this
7563          */
7564         toggleClass : function(className){
7565             if(this.hasClass(className)){
7566                 this.removeClass(className);
7567             }else{
7568                 this.addClass(className);
7569             }
7570             return this;
7571         },
7572
7573         /**
7574          * Checks if the specified CSS class exists on this element's DOM node.
7575          * @param {String} className The CSS class to check for
7576          * @return {Boolean} True if the class exists, else false
7577          */
7578         hasClass : function(className){
7579             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7580         },
7581
7582         /**
7583          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7584          * @param {String} oldClassName The CSS class to replace
7585          * @param {String} newClassName The replacement CSS class
7586          * @return {Roo.Element} this
7587          */
7588         replaceClass : function(oldClassName, newClassName){
7589             this.removeClass(oldClassName);
7590             this.addClass(newClassName);
7591             return this;
7592         },
7593
7594         /**
7595          * Returns an object with properties matching the styles requested.
7596          * For example, el.getStyles('color', 'font-size', 'width') might return
7597          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7598          * @param {String} style1 A style name
7599          * @param {String} style2 A style name
7600          * @param {String} etc.
7601          * @return {Object} The style object
7602          */
7603         getStyles : function(){
7604             var a = arguments, len = a.length, r = {};
7605             for(var i = 0; i < len; i++){
7606                 r[a[i]] = this.getStyle(a[i]);
7607             }
7608             return r;
7609         },
7610
7611         /**
7612          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7613          * @param {String} property The style property whose value is returned.
7614          * @return {String} The current value of the style property for this element.
7615          */
7616         getStyle : function(){
7617             return view && view.getComputedStyle ?
7618                 function(prop){
7619                     var el = this.dom, v, cs, camel;
7620                     if(prop == 'float'){
7621                         prop = "cssFloat";
7622                     }
7623                     if(el.style && (v = el.style[prop])){
7624                         return v;
7625                     }
7626                     if(cs = view.getComputedStyle(el, "")){
7627                         if(!(camel = propCache[prop])){
7628                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7629                         }
7630                         return cs[camel];
7631                     }
7632                     return null;
7633                 } :
7634                 function(prop){
7635                     var el = this.dom, v, cs, camel;
7636                     if(prop == 'opacity'){
7637                         if(typeof el.style.filter == 'string'){
7638                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7639                             if(m){
7640                                 var fv = parseFloat(m[1]);
7641                                 if(!isNaN(fv)){
7642                                     return fv ? fv / 100 : 0;
7643                                 }
7644                             }
7645                         }
7646                         return 1;
7647                     }else if(prop == 'float'){
7648                         prop = "styleFloat";
7649                     }
7650                     if(!(camel = propCache[prop])){
7651                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7652                     }
7653                     if(v = el.style[camel]){
7654                         return v;
7655                     }
7656                     if(cs = el.currentStyle){
7657                         return cs[camel];
7658                     }
7659                     return null;
7660                 };
7661         }(),
7662
7663         /**
7664          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7665          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7666          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7667          * @return {Roo.Element} this
7668          */
7669         setStyle : function(prop, value){
7670             if(typeof prop == "string"){
7671                 
7672                 if (prop == 'float') {
7673                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7674                     return this;
7675                 }
7676                 
7677                 var camel;
7678                 if(!(camel = propCache[prop])){
7679                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7680                 }
7681                 
7682                 if(camel == 'opacity') {
7683                     this.setOpacity(value);
7684                 }else{
7685                     this.dom.style[camel] = value;
7686                 }
7687             }else{
7688                 for(var style in prop){
7689                     if(typeof prop[style] != "function"){
7690                        this.setStyle(style, prop[style]);
7691                     }
7692                 }
7693             }
7694             return this;
7695         },
7696
7697         /**
7698          * More flexible version of {@link #setStyle} for setting style properties.
7699          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7700          * a function which returns such a specification.
7701          * @return {Roo.Element} this
7702          */
7703         applyStyles : function(style){
7704             Roo.DomHelper.applyStyles(this.dom, style);
7705             return this;
7706         },
7707
7708         /**
7709           * 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).
7710           * @return {Number} The X position of the element
7711           */
7712         getX : function(){
7713             return D.getX(this.dom);
7714         },
7715
7716         /**
7717           * 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).
7718           * @return {Number} The Y position of the element
7719           */
7720         getY : function(){
7721             return D.getY(this.dom);
7722         },
7723
7724         /**
7725           * 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).
7726           * @return {Array} The XY position of the element
7727           */
7728         getXY : function(){
7729             return D.getXY(this.dom);
7730         },
7731
7732         /**
7733          * 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).
7734          * @param {Number} The X position of the element
7735          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7736          * @return {Roo.Element} this
7737          */
7738         setX : function(x, animate){
7739             if(!animate || !A){
7740                 D.setX(this.dom, x);
7741             }else{
7742                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7743             }
7744             return this;
7745         },
7746
7747         /**
7748          * 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).
7749          * @param {Number} The Y position of the element
7750          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7751          * @return {Roo.Element} this
7752          */
7753         setY : function(y, animate){
7754             if(!animate || !A){
7755                 D.setY(this.dom, y);
7756             }else{
7757                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7758             }
7759             return this;
7760         },
7761
7762         /**
7763          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7764          * @param {String} left The left CSS property value
7765          * @return {Roo.Element} this
7766          */
7767         setLeft : function(left){
7768             this.setStyle("left", this.addUnits(left));
7769             return this;
7770         },
7771
7772         /**
7773          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7774          * @param {String} top The top CSS property value
7775          * @return {Roo.Element} this
7776          */
7777         setTop : function(top){
7778             this.setStyle("top", this.addUnits(top));
7779             return this;
7780         },
7781
7782         /**
7783          * Sets the element's CSS right style.
7784          * @param {String} right The right CSS property value
7785          * @return {Roo.Element} this
7786          */
7787         setRight : function(right){
7788             this.setStyle("right", this.addUnits(right));
7789             return this;
7790         },
7791
7792         /**
7793          * Sets the element's CSS bottom style.
7794          * @param {String} bottom The bottom CSS property value
7795          * @return {Roo.Element} this
7796          */
7797         setBottom : function(bottom){
7798             this.setStyle("bottom", this.addUnits(bottom));
7799             return this;
7800         },
7801
7802         /**
7803          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7804          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7805          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7806          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7807          * @return {Roo.Element} this
7808          */
7809         setXY : function(pos, animate){
7810             if(!animate || !A){
7811                 D.setXY(this.dom, pos);
7812             }else{
7813                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7814             }
7815             return this;
7816         },
7817
7818         /**
7819          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7820          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7821          * @param {Number} x X value for new position (coordinates are page-based)
7822          * @param {Number} y Y value for new position (coordinates are page-based)
7823          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7824          * @return {Roo.Element} this
7825          */
7826         setLocation : function(x, y, animate){
7827             this.setXY([x, y], this.preanim(arguments, 2));
7828             return this;
7829         },
7830
7831         /**
7832          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7833          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7834          * @param {Number} x X value for new position (coordinates are page-based)
7835          * @param {Number} y Y value for new position (coordinates are page-based)
7836          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7837          * @return {Roo.Element} this
7838          */
7839         moveTo : function(x, y, animate){
7840             this.setXY([x, y], this.preanim(arguments, 2));
7841             return this;
7842         },
7843
7844         /**
7845          * Returns the region of the given element.
7846          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7847          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7848          */
7849         getRegion : function(){
7850             return D.getRegion(this.dom);
7851         },
7852
7853         /**
7854          * Returns the offset height of the element
7855          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7856          * @return {Number} The element's height
7857          */
7858         getHeight : function(contentHeight){
7859             var h = this.dom.offsetHeight || 0;
7860             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7861         },
7862
7863         /**
7864          * Returns the offset width of the element
7865          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7866          * @return {Number} The element's width
7867          */
7868         getWidth : function(contentWidth){
7869             var w = this.dom.offsetWidth || 0;
7870             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7871         },
7872
7873         /**
7874          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7875          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7876          * if a height has not been set using CSS.
7877          * @return {Number}
7878          */
7879         getComputedHeight : function(){
7880             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7881             if(!h){
7882                 h = parseInt(this.getStyle('height'), 10) || 0;
7883                 if(!this.isBorderBox()){
7884                     h += this.getFrameWidth('tb');
7885                 }
7886             }
7887             return h;
7888         },
7889
7890         /**
7891          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7892          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7893          * if a width has not been set using CSS.
7894          * @return {Number}
7895          */
7896         getComputedWidth : function(){
7897             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7898             if(!w){
7899                 w = parseInt(this.getStyle('width'), 10) || 0;
7900                 if(!this.isBorderBox()){
7901                     w += this.getFrameWidth('lr');
7902                 }
7903             }
7904             return w;
7905         },
7906
7907         /**
7908          * Returns the size of the element.
7909          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7910          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7911          */
7912         getSize : function(contentSize){
7913             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7914         },
7915
7916         /**
7917          * Returns the width and height of the viewport.
7918          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7919          */
7920         getViewSize : function(){
7921             var d = this.dom, doc = document, aw = 0, ah = 0;
7922             if(d == doc || d == doc.body){
7923                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7924             }else{
7925                 return {
7926                     width : d.clientWidth,
7927                     height: d.clientHeight
7928                 };
7929             }
7930         },
7931
7932         /**
7933          * Returns the value of the "value" attribute
7934          * @param {Boolean} asNumber true to parse the value as a number
7935          * @return {String/Number}
7936          */
7937         getValue : function(asNumber){
7938             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7939         },
7940
7941         // private
7942         adjustWidth : function(width){
7943             if(typeof width == "number"){
7944                 if(this.autoBoxAdjust && !this.isBorderBox()){
7945                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7946                 }
7947                 if(width < 0){
7948                     width = 0;
7949                 }
7950             }
7951             return width;
7952         },
7953
7954         // private
7955         adjustHeight : function(height){
7956             if(typeof height == "number"){
7957                if(this.autoBoxAdjust && !this.isBorderBox()){
7958                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7959                }
7960                if(height < 0){
7961                    height = 0;
7962                }
7963             }
7964             return height;
7965         },
7966
7967         /**
7968          * Set the width of the element
7969          * @param {Number} width The new width
7970          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7971          * @return {Roo.Element} this
7972          */
7973         setWidth : function(width, animate){
7974             width = this.adjustWidth(width);
7975             if(!animate || !A){
7976                 this.dom.style.width = this.addUnits(width);
7977             }else{
7978                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7979             }
7980             return this;
7981         },
7982
7983         /**
7984          * Set the height of the element
7985          * @param {Number} height The new height
7986          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7987          * @return {Roo.Element} this
7988          */
7989          setHeight : function(height, animate){
7990             height = this.adjustHeight(height);
7991             if(!animate || !A){
7992                 this.dom.style.height = this.addUnits(height);
7993             }else{
7994                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7995             }
7996             return this;
7997         },
7998
7999         /**
8000          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8001          * @param {Number} width The new width
8002          * @param {Number} height The new height
8003          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8004          * @return {Roo.Element} this
8005          */
8006          setSize : function(width, height, animate){
8007             if(typeof width == "object"){ // in case of object from getSize()
8008                 height = width.height; width = width.width;
8009             }
8010             width = this.adjustWidth(width); height = this.adjustHeight(height);
8011             if(!animate || !A){
8012                 this.dom.style.width = this.addUnits(width);
8013                 this.dom.style.height = this.addUnits(height);
8014             }else{
8015                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8016             }
8017             return this;
8018         },
8019
8020         /**
8021          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8022          * @param {Number} x X value for new position (coordinates are page-based)
8023          * @param {Number} y Y value for new position (coordinates are page-based)
8024          * @param {Number} width The new width
8025          * @param {Number} height The new height
8026          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8027          * @return {Roo.Element} this
8028          */
8029         setBounds : function(x, y, width, height, animate){
8030             if(!animate || !A){
8031                 this.setSize(width, height);
8032                 this.setLocation(x, y);
8033             }else{
8034                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8035                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8036                               this.preanim(arguments, 4), 'motion');
8037             }
8038             return this;
8039         },
8040
8041         /**
8042          * 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.
8043          * @param {Roo.lib.Region} region The region to fill
8044          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8045          * @return {Roo.Element} this
8046          */
8047         setRegion : function(region, animate){
8048             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8049             return this;
8050         },
8051
8052         /**
8053          * Appends an event handler
8054          *
8055          * @param {String}   eventName     The type of event to append
8056          * @param {Function} fn        The method the event invokes
8057          * @param {Object} scope       (optional) The scope (this object) of the fn
8058          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8059          */
8060         addListener : function(eventName, fn, scope, options){
8061             if (this.dom) {
8062                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8063             }
8064         },
8065
8066         /**
8067          * Removes an event handler from this element
8068          * @param {String} eventName the type of event to remove
8069          * @param {Function} fn the method the event invokes
8070          * @return {Roo.Element} this
8071          */
8072         removeListener : function(eventName, fn){
8073             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8074             return this;
8075         },
8076
8077         /**
8078          * Removes all previous added listeners from this element
8079          * @return {Roo.Element} this
8080          */
8081         removeAllListeners : function(){
8082             E.purgeElement(this.dom);
8083             return this;
8084         },
8085
8086         relayEvent : function(eventName, observable){
8087             this.on(eventName, function(e){
8088                 observable.fireEvent(eventName, e);
8089             });
8090         },
8091
8092         /**
8093          * Set the opacity of the element
8094          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8095          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8096          * @return {Roo.Element} this
8097          */
8098          setOpacity : function(opacity, animate){
8099             if(!animate || !A){
8100                 var s = this.dom.style;
8101                 if(Roo.isIE){
8102                     s.zoom = 1;
8103                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8104                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8105                 }else{
8106                     s.opacity = opacity;
8107                 }
8108             }else{
8109                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8110             }
8111             return this;
8112         },
8113
8114         /**
8115          * Gets the left X coordinate
8116          * @param {Boolean} local True to get the local css position instead of page coordinate
8117          * @return {Number}
8118          */
8119         getLeft : function(local){
8120             if(!local){
8121                 return this.getX();
8122             }else{
8123                 var x = this.getStyle("left");
8124                 
8125                 if(!x || x === 'AUTO'){
8126                     return 0;
8127                 }
8128                 
8129                 if(new RegExp(this.pxReg).test(x)){
8130                     return parseFloat(x);
8131                 }
8132                 
8133                 x = this.getX();
8134                 
8135                 var  par = this.dom.offsetParent ? Roo.fly(this.dom.offsetParent) : false;
8136                 
8137                  if (par !== false) {
8138                     x -= par.getX();
8139                 }
8140
8141                 return x;
8142             }
8143         },
8144
8145         /**
8146          * Gets the right X coordinate of the element (element X position + element width)
8147          * @param {Boolean} local True to get the local css position instead of page coordinate
8148          * @return {Number}
8149          */
8150         getRight : function(local){
8151             if(!local){
8152                 return this.getX() + this.getWidth();
8153             }else{
8154                 return (this.getLeft(true) + this.getWidth()) || 0;
8155             }
8156         },
8157
8158         /**
8159          * Gets the top Y coordinate
8160          * @param {Boolean} local True to get the local css position instead of page coordinate
8161          * @return {Number}
8162          */
8163         getTop : function(local) {
8164             if(!local){
8165                 return this.getY();
8166             }else{
8167                 return parseInt(this.getStyle("top"), 10) || 0;
8168             }
8169         },
8170
8171         /**
8172          * Gets the bottom Y coordinate of the element (element Y position + element height)
8173          * @param {Boolean} local True to get the local css position instead of page coordinate
8174          * @return {Number}
8175          */
8176         getBottom : function(local){
8177             if(!local){
8178                 return this.getY() + this.getHeight();
8179             }else{
8180                 return (this.getTop(true) + this.getHeight()) || 0;
8181             }
8182         },
8183
8184         /**
8185         * Initializes positioning on this element. If a desired position is not passed, it will make the
8186         * the element positioned relative IF it is not already positioned.
8187         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8188         * @param {Number} zIndex (optional) The zIndex to apply
8189         * @param {Number} x (optional) Set the page X position
8190         * @param {Number} y (optional) Set the page Y position
8191         */
8192         position : function(pos, zIndex, x, y){
8193             if(!pos){
8194                if(this.getStyle('position') == 'static'){
8195                    this.setStyle('position', 'relative');
8196                }
8197             }else{
8198                 this.setStyle("position", pos);
8199             }
8200             if(zIndex){
8201                 this.setStyle("z-index", zIndex);
8202             }
8203             if(x !== undefined && y !== undefined){
8204                 this.setXY([x, y]);
8205             }else if(x !== undefined){
8206                 this.setX(x);
8207             }else if(y !== undefined){
8208                 this.setY(y);
8209             }
8210         },
8211
8212         /**
8213         * Clear positioning back to the default when the document was loaded
8214         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8215         * @return {Roo.Element} this
8216          */
8217         clearPositioning : function(value){
8218             value = value ||'';
8219             this.setStyle({
8220                 "left": value,
8221                 "right": value,
8222                 "top": value,
8223                 "bottom": value,
8224                 "z-index": "",
8225                 "position" : "static"
8226             });
8227             return this;
8228         },
8229
8230         /**
8231         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8232         * snapshot before performing an update and then restoring the element.
8233         * @return {Object}
8234         */
8235         getPositioning : function(){
8236             var l = this.getStyle("left");
8237             var t = this.getStyle("top");
8238             return {
8239                 "position" : this.getStyle("position"),
8240                 "left" : l,
8241                 "right" : l ? "" : this.getStyle("right"),
8242                 "top" : t,
8243                 "bottom" : t ? "" : this.getStyle("bottom"),
8244                 "z-index" : this.getStyle("z-index")
8245             };
8246         },
8247
8248         /**
8249          * Gets the width of the border(s) for the specified side(s)
8250          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8251          * passing lr would get the border (l)eft width + the border (r)ight width.
8252          * @return {Number} The width of the sides passed added together
8253          */
8254         getBorderWidth : function(side){
8255             return this.addStyles(side, El.borders);
8256         },
8257
8258         /**
8259          * Gets the width of the padding(s) for the specified side(s)
8260          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8261          * passing lr would get the padding (l)eft + the padding (r)ight.
8262          * @return {Number} The padding of the sides passed added together
8263          */
8264         getPadding : function(side){
8265             return this.addStyles(side, El.paddings);
8266         },
8267
8268         /**
8269         * Set positioning with an object returned by getPositioning().
8270         * @param {Object} posCfg
8271         * @return {Roo.Element} this
8272          */
8273         setPositioning : function(pc){
8274             this.applyStyles(pc);
8275             if(pc.right == "auto"){
8276                 this.dom.style.right = "";
8277             }
8278             if(pc.bottom == "auto"){
8279                 this.dom.style.bottom = "";
8280             }
8281             return this;
8282         },
8283
8284         // private
8285         fixDisplay : function(){
8286             if(this.getStyle("display") == "none"){
8287                 this.setStyle("visibility", "hidden");
8288                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8289                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8290                     this.setStyle("display", "block");
8291                 }
8292             }
8293         },
8294
8295         /**
8296          * Quick set left and top adding default units
8297          * @param {String} left The left CSS property value
8298          * @param {String} top The top CSS property value
8299          * @return {Roo.Element} this
8300          */
8301          setLeftTop : function(left, top){
8302             this.dom.style.left = this.addUnits(left);
8303             this.dom.style.top = this.addUnits(top);
8304             return this;
8305         },
8306
8307         /**
8308          * Move this element relative to its current position.
8309          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8310          * @param {Number} distance How far to move the element in pixels
8311          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8312          * @return {Roo.Element} this
8313          */
8314          move : function(direction, distance, animate){
8315             var xy = this.getXY();
8316             direction = direction.toLowerCase();
8317             switch(direction){
8318                 case "l":
8319                 case "left":
8320                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8321                     break;
8322                case "r":
8323                case "right":
8324                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8325                     break;
8326                case "t":
8327                case "top":
8328                case "up":
8329                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8330                     break;
8331                case "b":
8332                case "bottom":
8333                case "down":
8334                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8335                     break;
8336             }
8337             return this;
8338         },
8339
8340         /**
8341          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8342          * @return {Roo.Element} this
8343          */
8344         clip : function(){
8345             if(!this.isClipped){
8346                this.isClipped = true;
8347                this.originalClip = {
8348                    "o": this.getStyle("overflow"),
8349                    "x": this.getStyle("overflow-x"),
8350                    "y": this.getStyle("overflow-y")
8351                };
8352                this.setStyle("overflow", "hidden");
8353                this.setStyle("overflow-x", "hidden");
8354                this.setStyle("overflow-y", "hidden");
8355             }
8356             return this;
8357         },
8358
8359         /**
8360          *  Return clipping (overflow) to original clipping before clip() was called
8361          * @return {Roo.Element} this
8362          */
8363         unclip : function(){
8364             if(this.isClipped){
8365                 this.isClipped = false;
8366                 var o = this.originalClip;
8367                 if(o.o){this.setStyle("overflow", o.o);}
8368                 if(o.x){this.setStyle("overflow-x", o.x);}
8369                 if(o.y){this.setStyle("overflow-y", o.y);}
8370             }
8371             return this;
8372         },
8373
8374
8375         /**
8376          * Gets the x,y coordinates specified by the anchor position on the element.
8377          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8378          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8379          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8380          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8381          * @return {Array} [x, y] An array containing the element's x and y coordinates
8382          */
8383         getAnchorXY : function(anchor, local, s){
8384             //Passing a different size is useful for pre-calculating anchors,
8385             //especially for anchored animations that change the el size.
8386
8387             var w, h, vp = false;
8388             if(!s){
8389                 var d = this.dom;
8390                 if(d == document.body || d == document){
8391                     vp = true;
8392                     w = D.getViewWidth(); h = D.getViewHeight();
8393                 }else{
8394                     w = this.getWidth(); h = this.getHeight();
8395                 }
8396             }else{
8397                 w = s.width;  h = s.height;
8398             }
8399             var x = 0, y = 0, r = Math.round;
8400             switch((anchor || "tl").toLowerCase()){
8401                 case "c":
8402                     x = r(w*.5);
8403                     y = r(h*.5);
8404                 break;
8405                 case "t":
8406                     x = r(w*.5);
8407                     y = 0;
8408                 break;
8409                 case "l":
8410                     x = 0;
8411                     y = r(h*.5);
8412                 break;
8413                 case "r":
8414                     x = w;
8415                     y = r(h*.5);
8416                 break;
8417                 case "b":
8418                     x = r(w*.5);
8419                     y = h;
8420                 break;
8421                 case "tl":
8422                     x = 0;
8423                     y = 0;
8424                 break;
8425                 case "bl":
8426                     x = 0;
8427                     y = h;
8428                 break;
8429                 case "br":
8430                     x = w;
8431                     y = h;
8432                 break;
8433                 case "tr":
8434                     x = w;
8435                     y = 0;
8436                 break;
8437             }
8438             if(local === true){
8439                 return [x, y];
8440             }
8441             if(vp){
8442                 var sc = this.getScroll();
8443                 return [x + sc.left, y + sc.top];
8444             }
8445             //Add the element's offset xy
8446             var o = this.getXY();
8447             return [x+o[0], y+o[1]];
8448         },
8449
8450         /**
8451          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8452          * supported position values.
8453          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8454          * @param {String} position The position to align to.
8455          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8456          * @return {Array} [x, y]
8457          */
8458         getAlignToXY : function(el, p, o){
8459             el = Roo.get(el);
8460             var d = this.dom;
8461             if(!el.dom){
8462                 throw "Element.alignTo with an element that doesn't exist";
8463             }
8464             var c = false; //constrain to viewport
8465             var p1 = "", p2 = "";
8466             o = o || [0,0];
8467
8468             if(!p){
8469                 p = "tl-bl";
8470             }else if(p == "?"){
8471                 p = "tl-bl?";
8472             }else if(p.indexOf("-") == -1){
8473                 p = "tl-" + p;
8474             }
8475             p = p.toLowerCase();
8476             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8477             if(!m){
8478                throw "Element.alignTo with an invalid alignment " + p;
8479             }
8480             p1 = m[1]; p2 = m[2]; c = !!m[3];
8481
8482             //Subtract the aligned el's internal xy from the target's offset xy
8483             //plus custom offset to get the aligned el's new offset xy
8484             var a1 = this.getAnchorXY(p1, true);
8485             var a2 = el.getAnchorXY(p2, false);
8486             var x = a2[0] - a1[0] + o[0];
8487             var y = a2[1] - a1[1] + o[1];
8488             if(c){
8489                 //constrain the aligned el to viewport if necessary
8490                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8491                 // 5px of margin for ie
8492                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8493
8494                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8495                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8496                 //otherwise swap the aligned el to the opposite border of the target.
8497                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8498                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8499                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8500                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8501
8502                var doc = document;
8503                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8504                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8505
8506                if((x+w) > dw + scrollX){
8507                     x = swapX ? r.left-w : dw+scrollX-w;
8508                 }
8509                if(x < scrollX){
8510                    x = swapX ? r.right : scrollX;
8511                }
8512                if((y+h) > dh + scrollY){
8513                     y = swapY ? r.top-h : dh+scrollY-h;
8514                 }
8515                if (y < scrollY){
8516                    y = swapY ? r.bottom : scrollY;
8517                }
8518             }
8519             return [x,y];
8520         },
8521
8522         // private
8523         getConstrainToXY : function(){
8524             var os = {top:0, left:0, bottom:0, right: 0};
8525
8526             return function(el, local, offsets, proposedXY){
8527                 el = Roo.get(el);
8528                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8529
8530                 var vw, vh, vx = 0, vy = 0;
8531                 if(el.dom == document.body || el.dom == document){
8532                     vw = Roo.lib.Dom.getViewWidth();
8533                     vh = Roo.lib.Dom.getViewHeight();
8534                 }else{
8535                     vw = el.dom.clientWidth;
8536                     vh = el.dom.clientHeight;
8537                     if(!local){
8538                         var vxy = el.getXY();
8539                         vx = vxy[0];
8540                         vy = vxy[1];
8541                     }
8542                 }
8543
8544                 var s = el.getScroll();
8545
8546                 vx += offsets.left + s.left;
8547                 vy += offsets.top + s.top;
8548
8549                 vw -= offsets.right;
8550                 vh -= offsets.bottom;
8551
8552                 var vr = vx+vw;
8553                 var vb = vy+vh;
8554
8555                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8556                 var x = xy[0], y = xy[1];
8557                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8558
8559                 // only move it if it needs it
8560                 var moved = false;
8561
8562                 // first validate right/bottom
8563                 if((x + w) > vr){
8564                     x = vr - w;
8565                     moved = true;
8566                 }
8567                 if((y + h) > vb){
8568                     y = vb - h;
8569                     moved = true;
8570                 }
8571                 // then make sure top/left isn't negative
8572                 if(x < vx){
8573                     x = vx;
8574                     moved = true;
8575                 }
8576                 if(y < vy){
8577                     y = vy;
8578                     moved = true;
8579                 }
8580                 return moved ? [x, y] : false;
8581             };
8582         }(),
8583
8584         // private
8585         adjustForConstraints : function(xy, parent, offsets){
8586             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8587         },
8588
8589         /**
8590          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8591          * document it aligns it to the viewport.
8592          * The position parameter is optional, and can be specified in any one of the following formats:
8593          * <ul>
8594          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8595          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8596          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8597          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8598          *   <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
8599          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8600          * </ul>
8601          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8602          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8603          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8604          * that specified in order to enforce the viewport constraints.
8605          * Following are all of the supported anchor positions:
8606     <pre>
8607     Value  Description
8608     -----  -----------------------------
8609     tl     The top left corner (default)
8610     t      The center of the top edge
8611     tr     The top right corner
8612     l      The center of the left edge
8613     c      In the center of the element
8614     r      The center of the right edge
8615     bl     The bottom left corner
8616     b      The center of the bottom edge
8617     br     The bottom right corner
8618     </pre>
8619     Example Usage:
8620     <pre><code>
8621     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8622     el.alignTo("other-el");
8623
8624     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8625     el.alignTo("other-el", "tr?");
8626
8627     // align the bottom right corner of el with the center left edge of other-el
8628     el.alignTo("other-el", "br-l?");
8629
8630     // align the center of el with the bottom left corner of other-el and
8631     // adjust the x position by -6 pixels (and the y position by 0)
8632     el.alignTo("other-el", "c-bl", [-6, 0]);
8633     </code></pre>
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          * @return {Roo.Element} this
8639          */
8640         alignTo : function(element, position, offsets, animate){
8641             var xy = this.getAlignToXY(element, position, offsets);
8642             this.setXY(xy, this.preanim(arguments, 3));
8643             return this;
8644         },
8645
8646         /**
8647          * Anchors an element to another element and realigns it when the window is resized.
8648          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8649          * @param {String} position The position to align to.
8650          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8651          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8652          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8653          * is a number, it is used as the buffer delay (defaults to 50ms).
8654          * @param {Function} callback The function to call after the animation finishes
8655          * @return {Roo.Element} this
8656          */
8657         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8658             var action = function(){
8659                 this.alignTo(el, alignment, offsets, animate);
8660                 Roo.callback(callback, this);
8661             };
8662             Roo.EventManager.onWindowResize(action, this);
8663             var tm = typeof monitorScroll;
8664             if(tm != 'undefined'){
8665                 Roo.EventManager.on(window, 'scroll', action, this,
8666                     {buffer: tm == 'number' ? monitorScroll : 50});
8667             }
8668             action.call(this); // align immediately
8669             return this;
8670         },
8671         /**
8672          * Clears any opacity settings from this element. Required in some cases for IE.
8673          * @return {Roo.Element} this
8674          */
8675         clearOpacity : function(){
8676             if (window.ActiveXObject) {
8677                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8678                     this.dom.style.filter = "";
8679                 }
8680             } else {
8681                 this.dom.style.opacity = "";
8682                 this.dom.style["-moz-opacity"] = "";
8683                 this.dom.style["-khtml-opacity"] = "";
8684             }
8685             return this;
8686         },
8687
8688         /**
8689          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8690          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8691          * @return {Roo.Element} this
8692          */
8693         hide : function(animate){
8694             this.setVisible(false, this.preanim(arguments, 0));
8695             return this;
8696         },
8697
8698         /**
8699         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8700         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8701          * @return {Roo.Element} this
8702          */
8703         show : function(animate){
8704             this.setVisible(true, this.preanim(arguments, 0));
8705             return this;
8706         },
8707
8708         /**
8709          * @private Test if size has a unit, otherwise appends the default
8710          */
8711         addUnits : function(size){
8712             return Roo.Element.addUnits(size, this.defaultUnit);
8713         },
8714
8715         /**
8716          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8717          * @return {Roo.Element} this
8718          */
8719         beginMeasure : function(){
8720             var el = this.dom;
8721             if(el.offsetWidth || el.offsetHeight){
8722                 return this; // offsets work already
8723             }
8724             var changed = [];
8725             var p = this.dom, b = document.body; // start with this element
8726             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8727                 var pe = Roo.get(p);
8728                 if(pe.getStyle('display') == 'none'){
8729                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8730                     p.style.visibility = "hidden";
8731                     p.style.display = "block";
8732                 }
8733                 p = p.parentNode;
8734             }
8735             this._measureChanged = changed;
8736             return this;
8737
8738         },
8739
8740         /**
8741          * Restores displays to before beginMeasure was called
8742          * @return {Roo.Element} this
8743          */
8744         endMeasure : function(){
8745             var changed = this._measureChanged;
8746             if(changed){
8747                 for(var i = 0, len = changed.length; i < len; i++) {
8748                     var r = changed[i];
8749                     r.el.style.visibility = r.visibility;
8750                     r.el.style.display = "none";
8751                 }
8752                 this._measureChanged = null;
8753             }
8754             return this;
8755         },
8756
8757         /**
8758         * Update the innerHTML of this element, optionally searching for and processing scripts
8759         * @param {String} html The new HTML
8760         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8761         * @param {Function} callback For async script loading you can be noticed when the update completes
8762         * @return {Roo.Element} this
8763          */
8764         update : function(html, loadScripts, callback){
8765             if(typeof html == "undefined"){
8766                 html = "";
8767             }
8768             if(loadScripts !== true){
8769                 this.dom.innerHTML = html;
8770                 if(typeof callback == "function"){
8771                     callback();
8772                 }
8773                 return this;
8774             }
8775             var id = Roo.id();
8776             var dom = this.dom;
8777
8778             html += '<span id="' + id + '"></span>';
8779
8780             E.onAvailable(id, function(){
8781                 var hd = document.getElementsByTagName("head")[0];
8782                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8783                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8784                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8785
8786                 var match;
8787                 while(match = re.exec(html)){
8788                     var attrs = match[1];
8789                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8790                     if(srcMatch && srcMatch[2]){
8791                        var s = document.createElement("script");
8792                        s.src = srcMatch[2];
8793                        var typeMatch = attrs.match(typeRe);
8794                        if(typeMatch && typeMatch[2]){
8795                            s.type = typeMatch[2];
8796                        }
8797                        hd.appendChild(s);
8798                     }else if(match[2] && match[2].length > 0){
8799                         if(window.execScript) {
8800                            window.execScript(match[2]);
8801                         } else {
8802                             /**
8803                              * eval:var:id
8804                              * eval:var:dom
8805                              * eval:var:html
8806                              * 
8807                              */
8808                            window.eval(match[2]);
8809                         }
8810                     }
8811                 }
8812                 var el = document.getElementById(id);
8813                 if(el){el.parentNode.removeChild(el);}
8814                 if(typeof callback == "function"){
8815                     callback();
8816                 }
8817             });
8818             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8819             return this;
8820         },
8821
8822         /**
8823          * Direct access to the UpdateManager update() method (takes the same parameters).
8824          * @param {String/Function} url The url for this request or a function to call to get the url
8825          * @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}
8826          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8827          * @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.
8828          * @return {Roo.Element} this
8829          */
8830         load : function(){
8831             var um = this.getUpdateManager();
8832             um.update.apply(um, arguments);
8833             return this;
8834         },
8835
8836         /**
8837         * Gets this element's UpdateManager
8838         * @return {Roo.UpdateManager} The UpdateManager
8839         */
8840         getUpdateManager : function(){
8841             if(!this.updateManager){
8842                 this.updateManager = new Roo.UpdateManager(this);
8843             }
8844             return this.updateManager;
8845         },
8846
8847         /**
8848          * Disables text selection for this element (normalized across browsers)
8849          * @return {Roo.Element} this
8850          */
8851         unselectable : function(){
8852             this.dom.unselectable = "on";
8853             this.swallowEvent("selectstart", true);
8854             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8855             this.addClass("x-unselectable");
8856             return this;
8857         },
8858
8859         /**
8860         * Calculates the x, y to center this element on the screen
8861         * @return {Array} The x, y values [x, y]
8862         */
8863         getCenterXY : function(){
8864             return this.getAlignToXY(document, 'c-c');
8865         },
8866
8867         /**
8868         * Centers the Element in either the viewport, or another Element.
8869         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8870         */
8871         center : function(centerIn){
8872             this.alignTo(centerIn || document, 'c-c');
8873             return this;
8874         },
8875
8876         /**
8877          * Tests various css rules/browsers to determine if this element uses a border box
8878          * @return {Boolean}
8879          */
8880         isBorderBox : function(){
8881             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8882         },
8883
8884         /**
8885          * Return a box {x, y, width, height} that can be used to set another elements
8886          * size/location to match this element.
8887          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8888          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8889          * @return {Object} box An object in the format {x, y, width, height}
8890          */
8891         getBox : function(contentBox, local){
8892             var xy;
8893             if(!local){
8894                 xy = this.getXY();
8895             }else{
8896                 var left = parseInt(this.getStyle("left"), 10) || 0;
8897                 var top = parseInt(this.getStyle("top"), 10) || 0;
8898                 xy = [left, top];
8899             }
8900             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8901             if(!contentBox){
8902                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8903             }else{
8904                 var l = this.getBorderWidth("l")+this.getPadding("l");
8905                 var r = this.getBorderWidth("r")+this.getPadding("r");
8906                 var t = this.getBorderWidth("t")+this.getPadding("t");
8907                 var b = this.getBorderWidth("b")+this.getPadding("b");
8908                 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)};
8909             }
8910             bx.right = bx.x + bx.width;
8911             bx.bottom = bx.y + bx.height;
8912             return bx;
8913         },
8914
8915         /**
8916          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8917          for more information about the sides.
8918          * @param {String} sides
8919          * @return {Number}
8920          */
8921         getFrameWidth : function(sides, onlyContentBox){
8922             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8923         },
8924
8925         /**
8926          * 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.
8927          * @param {Object} box The box to fill {x, y, width, height}
8928          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8929          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8930          * @return {Roo.Element} this
8931          */
8932         setBox : function(box, adjust, animate){
8933             var w = box.width, h = box.height;
8934             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8935                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8936                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8937             }
8938             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8939             return this;
8940         },
8941
8942         /**
8943          * Forces the browser to repaint this element
8944          * @return {Roo.Element} this
8945          */
8946          repaint : function(){
8947             var dom = this.dom;
8948             this.addClass("x-repaint");
8949             setTimeout(function(){
8950                 Roo.get(dom).removeClass("x-repaint");
8951             }, 1);
8952             return this;
8953         },
8954
8955         /**
8956          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8957          * then it returns the calculated width of the sides (see getPadding)
8958          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8959          * @return {Object/Number}
8960          */
8961         getMargins : function(side){
8962             if(!side){
8963                 return {
8964                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8965                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8966                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8967                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8968                 };
8969             }else{
8970                 return this.addStyles(side, El.margins);
8971              }
8972         },
8973
8974         // private
8975         addStyles : function(sides, styles){
8976             var val = 0, v, w;
8977             for(var i = 0, len = sides.length; i < len; i++){
8978                 v = this.getStyle(styles[sides.charAt(i)]);
8979                 if(v){
8980                      w = parseInt(v, 10);
8981                      if(w){ val += w; }
8982                 }
8983             }
8984             return val;
8985         },
8986
8987         /**
8988          * Creates a proxy element of this element
8989          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8990          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8991          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8992          * @return {Roo.Element} The new proxy element
8993          */
8994         createProxy : function(config, renderTo, matchBox){
8995             if(renderTo){
8996                 renderTo = Roo.getDom(renderTo);
8997             }else{
8998                 renderTo = document.body;
8999             }
9000             config = typeof config == "object" ?
9001                 config : {tag : "div", cls: config};
9002             var proxy = Roo.DomHelper.append(renderTo, config, true);
9003             if(matchBox){
9004                proxy.setBox(this.getBox());
9005             }
9006             return proxy;
9007         },
9008
9009         /**
9010          * Puts a mask over this element to disable user interaction. Requires core.css.
9011          * This method can only be applied to elements which accept child nodes.
9012          * @param {String} msg (optional) A message to display in the mask
9013          * @param {String} msgCls (optional) A css class to apply to the msg element
9014          * @return {Element} The mask  element
9015          */
9016         mask : function(msg, msgCls)
9017         {
9018             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9019                 this.setStyle("position", "relative");
9020             }
9021             if(!this._mask){
9022                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9023             }
9024             this.addClass("x-masked");
9025             this._mask.setDisplayed(true);
9026             
9027             // we wander
9028             var z = 0;
9029             var dom = this.dom
9030             while (dom && dom.style) {
9031                 if (!isNaN(parseInt(dom.style.zIndex))) {
9032                     z = Math.max(z, parseInt(dom.style.zIndex));
9033                 }
9034                 dom = dom.parentNode;
9035             }
9036             // if we are masking the body - then it hides everything..
9037             if (this.dom == document.body) {
9038                 z = 1000000;
9039                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9040                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9041             }
9042            
9043             if(typeof msg == 'string'){
9044                 if(!this._maskMsg){
9045                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9046                 }
9047                 var mm = this._maskMsg;
9048                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9049                 if (mm.dom.firstChild) { // weird IE issue?
9050                     mm.dom.firstChild.innerHTML = msg;
9051                 }
9052                 mm.setDisplayed(true);
9053                 mm.center(this);
9054                 mm.setStyle('z-index', z + 102);
9055             }
9056             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9057                 this._mask.setHeight(this.getHeight());
9058             }
9059             this._mask.setStyle('z-index', z + 100);
9060             
9061             return this._mask;
9062         },
9063
9064         /**
9065          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9066          * it is cached for reuse.
9067          */
9068         unmask : function(removeEl){
9069             if(this._mask){
9070                 if(removeEl === true){
9071                     this._mask.remove();
9072                     delete this._mask;
9073                     if(this._maskMsg){
9074                         this._maskMsg.remove();
9075                         delete this._maskMsg;
9076                     }
9077                 }else{
9078                     this._mask.setDisplayed(false);
9079                     if(this._maskMsg){
9080                         this._maskMsg.setDisplayed(false);
9081                     }
9082                 }
9083             }
9084             this.removeClass("x-masked");
9085         },
9086
9087         /**
9088          * Returns true if this element is masked
9089          * @return {Boolean}
9090          */
9091         isMasked : function(){
9092             return this._mask && this._mask.isVisible();
9093         },
9094
9095         /**
9096          * Creates an iframe shim for this element to keep selects and other windowed objects from
9097          * showing through.
9098          * @return {Roo.Element} The new shim element
9099          */
9100         createShim : function(){
9101             var el = document.createElement('iframe');
9102             el.frameBorder = 'no';
9103             el.className = 'roo-shim';
9104             if(Roo.isIE && Roo.isSecure){
9105                 el.src = Roo.SSL_SECURE_URL;
9106             }
9107             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9108             shim.autoBoxAdjust = false;
9109             return shim;
9110         },
9111
9112         /**
9113          * Removes this element from the DOM and deletes it from the cache
9114          */
9115         remove : function(){
9116             if(this.dom.parentNode){
9117                 this.dom.parentNode.removeChild(this.dom);
9118             }
9119             delete El.cache[this.dom.id];
9120         },
9121
9122         /**
9123          * Sets up event handlers to add and remove a css class when the mouse is over this element
9124          * @param {String} className
9125          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9126          * mouseout events for children elements
9127          * @return {Roo.Element} this
9128          */
9129         addClassOnOver : function(className, preventFlicker){
9130             this.on("mouseover", function(){
9131                 Roo.fly(this, '_internal').addClass(className);
9132             }, this.dom);
9133             var removeFn = function(e){
9134                 if(preventFlicker !== true || !e.within(this, true)){
9135                     Roo.fly(this, '_internal').removeClass(className);
9136                 }
9137             };
9138             this.on("mouseout", removeFn, this.dom);
9139             return this;
9140         },
9141
9142         /**
9143          * Sets up event handlers to add and remove a css class when this element has the focus
9144          * @param {String} className
9145          * @return {Roo.Element} this
9146          */
9147         addClassOnFocus : function(className){
9148             this.on("focus", function(){
9149                 Roo.fly(this, '_internal').addClass(className);
9150             }, this.dom);
9151             this.on("blur", function(){
9152                 Roo.fly(this, '_internal').removeClass(className);
9153             }, this.dom);
9154             return this;
9155         },
9156         /**
9157          * 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)
9158          * @param {String} className
9159          * @return {Roo.Element} this
9160          */
9161         addClassOnClick : function(className){
9162             var dom = this.dom;
9163             this.on("mousedown", function(){
9164                 Roo.fly(dom, '_internal').addClass(className);
9165                 var d = Roo.get(document);
9166                 var fn = function(){
9167                     Roo.fly(dom, '_internal').removeClass(className);
9168                     d.removeListener("mouseup", fn);
9169                 };
9170                 d.on("mouseup", fn);
9171             });
9172             return this;
9173         },
9174
9175         /**
9176          * Stops the specified event from bubbling and optionally prevents the default action
9177          * @param {String} eventName
9178          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9179          * @return {Roo.Element} this
9180          */
9181         swallowEvent : function(eventName, preventDefault){
9182             var fn = function(e){
9183                 e.stopPropagation();
9184                 if(preventDefault){
9185                     e.preventDefault();
9186                 }
9187             };
9188             if(eventName instanceof Array){
9189                 for(var i = 0, len = eventName.length; i < len; i++){
9190                      this.on(eventName[i], fn);
9191                 }
9192                 return this;
9193             }
9194             this.on(eventName, fn);
9195             return this;
9196         },
9197
9198         /**
9199          * @private
9200          */
9201       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9202
9203         /**
9204          * Sizes this element to its parent element's dimensions performing
9205          * neccessary box adjustments.
9206          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9207          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9208          * @return {Roo.Element} this
9209          */
9210         fitToParent : function(monitorResize, targetParent) {
9211           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9212           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9213           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9214             return;
9215           }
9216           var p = Roo.get(targetParent || this.dom.parentNode);
9217           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9218           if (monitorResize === true) {
9219             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9220             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9221           }
9222           return this;
9223         },
9224
9225         /**
9226          * Gets the next sibling, skipping text nodes
9227          * @return {HTMLElement} The next sibling or null
9228          */
9229         getNextSibling : function(){
9230             var n = this.dom.nextSibling;
9231             while(n && n.nodeType != 1){
9232                 n = n.nextSibling;
9233             }
9234             return n;
9235         },
9236
9237         /**
9238          * Gets the previous sibling, skipping text nodes
9239          * @return {HTMLElement} The previous sibling or null
9240          */
9241         getPrevSibling : function(){
9242             var n = this.dom.previousSibling;
9243             while(n && n.nodeType != 1){
9244                 n = n.previousSibling;
9245             }
9246             return n;
9247         },
9248
9249
9250         /**
9251          * Appends the passed element(s) to this element
9252          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9253          * @return {Roo.Element} this
9254          */
9255         appendChild: function(el){
9256             el = Roo.get(el);
9257             el.appendTo(this);
9258             return this;
9259         },
9260
9261         /**
9262          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9263          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9264          * automatically generated with the specified attributes.
9265          * @param {HTMLElement} insertBefore (optional) a child element of this element
9266          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9267          * @return {Roo.Element} The new child element
9268          */
9269         createChild: function(config, insertBefore, returnDom){
9270             config = config || {tag:'div'};
9271             if(insertBefore){
9272                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9273             }
9274             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9275         },
9276
9277         /**
9278          * Appends this element to the passed element
9279          * @param {String/HTMLElement/Element} el The new parent element
9280          * @return {Roo.Element} this
9281          */
9282         appendTo: function(el){
9283             el = Roo.getDom(el);
9284             el.appendChild(this.dom);
9285             return this;
9286         },
9287
9288         /**
9289          * Inserts this element before the passed element in the DOM
9290          * @param {String/HTMLElement/Element} el The element to insert before
9291          * @return {Roo.Element} this
9292          */
9293         insertBefore: function(el){
9294             el = Roo.getDom(el);
9295             el.parentNode.insertBefore(this.dom, el);
9296             return this;
9297         },
9298
9299         /**
9300          * Inserts this element after the passed element in the DOM
9301          * @param {String/HTMLElement/Element} el The element to insert after
9302          * @return {Roo.Element} this
9303          */
9304         insertAfter: function(el){
9305             el = Roo.getDom(el);
9306             el.parentNode.insertBefore(this.dom, el.nextSibling);
9307             return this;
9308         },
9309
9310         /**
9311          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9312          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9313          * @return {Roo.Element} The new child
9314          */
9315         insertFirst: function(el, returnDom){
9316             el = el || {};
9317             if(typeof el == 'object' && !el.nodeType){ // dh config
9318                 return this.createChild(el, this.dom.firstChild, returnDom);
9319             }else{
9320                 el = Roo.getDom(el);
9321                 this.dom.insertBefore(el, this.dom.firstChild);
9322                 return !returnDom ? Roo.get(el) : el;
9323             }
9324         },
9325
9326         /**
9327          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9328          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9329          * @param {String} where (optional) 'before' or 'after' defaults to before
9330          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9331          * @return {Roo.Element} the inserted Element
9332          */
9333         insertSibling: function(el, where, returnDom){
9334             where = where ? where.toLowerCase() : 'before';
9335             el = el || {};
9336             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9337
9338             if(typeof el == 'object' && !el.nodeType){ // dh config
9339                 if(where == 'after' && !this.dom.nextSibling){
9340                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9341                 }else{
9342                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9343                 }
9344
9345             }else{
9346                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9347                             where == 'before' ? this.dom : this.dom.nextSibling);
9348                 if(!returnDom){
9349                     rt = Roo.get(rt);
9350                 }
9351             }
9352             return rt;
9353         },
9354
9355         /**
9356          * Creates and wraps this element with another element
9357          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9358          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9359          * @return {HTMLElement/Element} The newly created wrapper element
9360          */
9361         wrap: function(config, returnDom){
9362             if(!config){
9363                 config = {tag: "div"};
9364             }
9365             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9366             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9367             return newEl;
9368         },
9369
9370         /**
9371          * Replaces the passed element with this element
9372          * @param {String/HTMLElement/Element} el The element to replace
9373          * @return {Roo.Element} this
9374          */
9375         replace: function(el){
9376             el = Roo.get(el);
9377             this.insertBefore(el);
9378             el.remove();
9379             return this;
9380         },
9381
9382         /**
9383          * Inserts an html fragment into this element
9384          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9385          * @param {String} html The HTML fragment
9386          * @param {Boolean} returnEl True to return an Roo.Element
9387          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9388          */
9389         insertHtml : function(where, html, returnEl){
9390             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9391             return returnEl ? Roo.get(el) : el;
9392         },
9393
9394         /**
9395          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9396          * @param {Object} o The object with the attributes
9397          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9398          * @return {Roo.Element} this
9399          */
9400         set : function(o, useSet){
9401             var el = this.dom;
9402             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9403             for(var attr in o){
9404                 if(attr == "style" || typeof o[attr] == "function") continue;
9405                 if(attr=="cls"){
9406                     el.className = o["cls"];
9407                 }else{
9408                     if(useSet) el.setAttribute(attr, o[attr]);
9409                     else el[attr] = o[attr];
9410                 }
9411             }
9412             if(o.style){
9413                 Roo.DomHelper.applyStyles(el, o.style);
9414             }
9415             return this;
9416         },
9417
9418         /**
9419          * Convenience method for constructing a KeyMap
9420          * @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:
9421          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9422          * @param {Function} fn The function to call
9423          * @param {Object} scope (optional) The scope of the function
9424          * @return {Roo.KeyMap} The KeyMap created
9425          */
9426         addKeyListener : function(key, fn, scope){
9427             var config;
9428             if(typeof key != "object" || key instanceof Array){
9429                 config = {
9430                     key: key,
9431                     fn: fn,
9432                     scope: scope
9433                 };
9434             }else{
9435                 config = {
9436                     key : key.key,
9437                     shift : key.shift,
9438                     ctrl : key.ctrl,
9439                     alt : key.alt,
9440                     fn: fn,
9441                     scope: scope
9442                 };
9443             }
9444             return new Roo.KeyMap(this, config);
9445         },
9446
9447         /**
9448          * Creates a KeyMap for this element
9449          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9450          * @return {Roo.KeyMap} The KeyMap created
9451          */
9452         addKeyMap : function(config){
9453             return new Roo.KeyMap(this, config);
9454         },
9455
9456         /**
9457          * Returns true if this element is scrollable.
9458          * @return {Boolean}
9459          */
9460          isScrollable : function(){
9461             var dom = this.dom;
9462             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9463         },
9464
9465         /**
9466          * 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().
9467          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9468          * @param {Number} value The new scroll value
9469          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9470          * @return {Element} this
9471          */
9472
9473         scrollTo : function(side, value, animate){
9474             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9475             if(!animate || !A){
9476                 this.dom[prop] = value;
9477             }else{
9478                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9479                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9480             }
9481             return this;
9482         },
9483
9484         /**
9485          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9486          * within this element's scrollable range.
9487          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9488          * @param {Number} distance How far to scroll the element in pixels
9489          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9490          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9491          * was scrolled as far as it could go.
9492          */
9493          scroll : function(direction, distance, animate){
9494              if(!this.isScrollable()){
9495                  return;
9496              }
9497              var el = this.dom;
9498              var l = el.scrollLeft, t = el.scrollTop;
9499              var w = el.scrollWidth, h = el.scrollHeight;
9500              var cw = el.clientWidth, ch = el.clientHeight;
9501              direction = direction.toLowerCase();
9502              var scrolled = false;
9503              var a = this.preanim(arguments, 2);
9504              switch(direction){
9505                  case "l":
9506                  case "left":
9507                      if(w - l > cw){
9508                          var v = Math.min(l + distance, w-cw);
9509                          this.scrollTo("left", v, a);
9510                          scrolled = true;
9511                      }
9512                      break;
9513                 case "r":
9514                 case "right":
9515                      if(l > 0){
9516                          var v = Math.max(l - distance, 0);
9517                          this.scrollTo("left", v, a);
9518                          scrolled = true;
9519                      }
9520                      break;
9521                 case "t":
9522                 case "top":
9523                 case "up":
9524                      if(t > 0){
9525                          var v = Math.max(t - distance, 0);
9526                          this.scrollTo("top", v, a);
9527                          scrolled = true;
9528                      }
9529                      break;
9530                 case "b":
9531                 case "bottom":
9532                 case "down":
9533                      if(h - t > ch){
9534                          var v = Math.min(t + distance, h-ch);
9535                          this.scrollTo("top", v, a);
9536                          scrolled = true;
9537                      }
9538                      break;
9539              }
9540              return scrolled;
9541         },
9542
9543         /**
9544          * Translates the passed page coordinates into left/top css values for this element
9545          * @param {Number/Array} x The page x or an array containing [x, y]
9546          * @param {Number} y The page y
9547          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9548          */
9549         translatePoints : function(x, y){
9550             if(typeof x == 'object' || x instanceof Array){
9551                 y = x[1]; x = x[0];
9552             }
9553             var p = this.getStyle('position');
9554             var o = this.getXY();
9555
9556             var l = parseInt(this.getStyle('left'), 10);
9557             var t = parseInt(this.getStyle('top'), 10);
9558
9559             if(isNaN(l)){
9560                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9561             }
9562             if(isNaN(t)){
9563                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9564             }
9565
9566             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9567         },
9568
9569         /**
9570          * Returns the current scroll position of the element.
9571          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9572          */
9573         getScroll : function(){
9574             var d = this.dom, doc = document;
9575             if(d == doc || d == doc.body){
9576                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9577                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9578                 return {left: l, top: t};
9579             }else{
9580                 return {left: d.scrollLeft, top: d.scrollTop};
9581             }
9582         },
9583
9584         /**
9585          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9586          * are convert to standard 6 digit hex color.
9587          * @param {String} attr The css attribute
9588          * @param {String} defaultValue The default value to use when a valid color isn't found
9589          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9590          * YUI color anims.
9591          */
9592         getColor : function(attr, defaultValue, prefix){
9593             var v = this.getStyle(attr);
9594             if(!v || v == "transparent" || v == "inherit") {
9595                 return defaultValue;
9596             }
9597             var color = typeof prefix == "undefined" ? "#" : prefix;
9598             if(v.substr(0, 4) == "rgb("){
9599                 var rvs = v.slice(4, v.length -1).split(",");
9600                 for(var i = 0; i < 3; i++){
9601                     var h = parseInt(rvs[i]).toString(16);
9602                     if(h < 16){
9603                         h = "0" + h;
9604                     }
9605                     color += h;
9606                 }
9607             } else {
9608                 if(v.substr(0, 1) == "#"){
9609                     if(v.length == 4) {
9610                         for(var i = 1; i < 4; i++){
9611                             var c = v.charAt(i);
9612                             color +=  c + c;
9613                         }
9614                     }else if(v.length == 7){
9615                         color += v.substr(1);
9616                     }
9617                 }
9618             }
9619             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9620         },
9621
9622         /**
9623          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9624          * gradient background, rounded corners and a 4-way shadow.
9625          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9626          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9627          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9628          * @return {Roo.Element} this
9629          */
9630         boxWrap : function(cls){
9631             cls = cls || 'x-box';
9632             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9633             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9634             return el;
9635         },
9636
9637         /**
9638          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9639          * @param {String} namespace The namespace in which to look for the attribute
9640          * @param {String} name The attribute name
9641          * @return {String} The attribute value
9642          */
9643         getAttributeNS : Roo.isIE ? function(ns, name){
9644             var d = this.dom;
9645             var type = typeof d[ns+":"+name];
9646             if(type != 'undefined' && type != 'unknown'){
9647                 return d[ns+":"+name];
9648             }
9649             return d[name];
9650         } : function(ns, name){
9651             var d = this.dom;
9652             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9653         },
9654         
9655         
9656         /**
9657          * Sets or Returns the value the dom attribute value
9658          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9659          * @param {String} value (optional) The value to set the attribute to
9660          * @return {String} The attribute value
9661          */
9662         attr : function(name){
9663             if (arguments.length > 1) {
9664                 this.dom.setAttribute(name, arguments[1]);
9665                 return arguments[1];
9666             }
9667             if (typeof(name) == 'object') {
9668                 for(var i in name) {
9669                     this.attr(i, name[i]);
9670                 }
9671                 return name;
9672             }
9673             
9674             
9675             if (!this.dom.hasAttribute(name)) {
9676                 return undefined;
9677             }
9678             return this.dom.getAttribute(name);
9679         }
9680         
9681         
9682         
9683     };
9684
9685     var ep = El.prototype;
9686
9687     /**
9688      * Appends an event handler (Shorthand for addListener)
9689      * @param {String}   eventName     The type of event to append
9690      * @param {Function} fn        The method the event invokes
9691      * @param {Object} scope       (optional) The scope (this object) of the fn
9692      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9693      * @method
9694      */
9695     ep.on = ep.addListener;
9696         // backwards compat
9697     ep.mon = ep.addListener;
9698
9699     /**
9700      * Removes an event handler from this element (shorthand for removeListener)
9701      * @param {String} eventName the type of event to remove
9702      * @param {Function} fn the method the event invokes
9703      * @return {Roo.Element} this
9704      * @method
9705      */
9706     ep.un = ep.removeListener;
9707
9708     /**
9709      * true to automatically adjust width and height settings for box-model issues (default to true)
9710      */
9711     ep.autoBoxAdjust = true;
9712
9713     // private
9714     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9715
9716     // private
9717     El.addUnits = function(v, defaultUnit){
9718         if(v === "" || v == "auto"){
9719             return v;
9720         }
9721         if(v === undefined){
9722             return '';
9723         }
9724         if(typeof v == "number" || !El.unitPattern.test(v)){
9725             return v + (defaultUnit || 'px');
9726         }
9727         return v;
9728     };
9729
9730     // special markup used throughout Roo when box wrapping elements
9731     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>';
9732     /**
9733      * Visibility mode constant - Use visibility to hide element
9734      * @static
9735      * @type Number
9736      */
9737     El.VISIBILITY = 1;
9738     /**
9739      * Visibility mode constant - Use display to hide element
9740      * @static
9741      * @type Number
9742      */
9743     El.DISPLAY = 2;
9744
9745     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9746     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9747     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9748
9749
9750
9751     /**
9752      * @private
9753      */
9754     El.cache = {};
9755
9756     var docEl;
9757
9758     /**
9759      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9760      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9761      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9762      * @return {Element} The Element object
9763      * @static
9764      */
9765     El.get = function(el){
9766         var ex, elm, id;
9767         if(!el){ return null; }
9768         if(typeof el == "string"){ // element id
9769             if(!(elm = document.getElementById(el))){
9770                 return null;
9771             }
9772             if(ex = El.cache[el]){
9773                 ex.dom = elm;
9774             }else{
9775                 ex = El.cache[el] = new El(elm);
9776             }
9777             return ex;
9778         }else if(el.tagName){ // dom element
9779             if(!(id = el.id)){
9780                 id = Roo.id(el);
9781             }
9782             if(ex = El.cache[id]){
9783                 ex.dom = el;
9784             }else{
9785                 ex = El.cache[id] = new El(el);
9786             }
9787             return ex;
9788         }else if(el instanceof El){
9789             if(el != docEl){
9790                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9791                                                               // catch case where it hasn't been appended
9792                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9793             }
9794             return el;
9795         }else if(el.isComposite){
9796             return el;
9797         }else if(el instanceof Array){
9798             return El.select(el);
9799         }else if(el == document){
9800             // create a bogus element object representing the document object
9801             if(!docEl){
9802                 var f = function(){};
9803                 f.prototype = El.prototype;
9804                 docEl = new f();
9805                 docEl.dom = document;
9806             }
9807             return docEl;
9808         }
9809         return null;
9810     };
9811
9812     // private
9813     El.uncache = function(el){
9814         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9815             if(a[i]){
9816                 delete El.cache[a[i].id || a[i]];
9817             }
9818         }
9819     };
9820
9821     // private
9822     // Garbage collection - uncache elements/purge listeners on orphaned elements
9823     // so we don't hold a reference and cause the browser to retain them
9824     El.garbageCollect = function(){
9825         if(!Roo.enableGarbageCollector){
9826             clearInterval(El.collectorThread);
9827             return;
9828         }
9829         for(var eid in El.cache){
9830             var el = El.cache[eid], d = el.dom;
9831             // -------------------------------------------------------
9832             // Determining what is garbage:
9833             // -------------------------------------------------------
9834             // !d
9835             // dom node is null, definitely garbage
9836             // -------------------------------------------------------
9837             // !d.parentNode
9838             // no parentNode == direct orphan, definitely garbage
9839             // -------------------------------------------------------
9840             // !d.offsetParent && !document.getElementById(eid)
9841             // display none elements have no offsetParent so we will
9842             // also try to look it up by it's id. However, check
9843             // offsetParent first so we don't do unneeded lookups.
9844             // This enables collection of elements that are not orphans
9845             // directly, but somewhere up the line they have an orphan
9846             // parent.
9847             // -------------------------------------------------------
9848             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9849                 delete El.cache[eid];
9850                 if(d && Roo.enableListenerCollection){
9851                     E.purgeElement(d);
9852                 }
9853             }
9854         }
9855     }
9856     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9857
9858
9859     // dom is optional
9860     El.Flyweight = function(dom){
9861         this.dom = dom;
9862     };
9863     El.Flyweight.prototype = El.prototype;
9864
9865     El._flyweights = {};
9866     /**
9867      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9868      * the dom node can be overwritten by other code.
9869      * @param {String/HTMLElement} el The dom node or id
9870      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9871      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9872      * @static
9873      * @return {Element} The shared Element object
9874      */
9875     El.fly = function(el, named){
9876         named = named || '_global';
9877         el = Roo.getDom(el);
9878         if(!el){
9879             return null;
9880         }
9881         if(!El._flyweights[named]){
9882             El._flyweights[named] = new El.Flyweight();
9883         }
9884         El._flyweights[named].dom = el;
9885         return El._flyweights[named];
9886     };
9887
9888     /**
9889      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9890      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9891      * Shorthand of {@link Roo.Element#get}
9892      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9893      * @return {Element} The Element object
9894      * @member Roo
9895      * @method get
9896      */
9897     Roo.get = El.get;
9898     /**
9899      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9900      * the dom node can be overwritten by other code.
9901      * Shorthand of {@link Roo.Element#fly}
9902      * @param {String/HTMLElement} el The dom node or id
9903      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9904      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9905      * @static
9906      * @return {Element} The shared Element object
9907      * @member Roo
9908      * @method fly
9909      */
9910     Roo.fly = El.fly;
9911
9912     // speedy lookup for elements never to box adjust
9913     var noBoxAdjust = Roo.isStrict ? {
9914         select:1
9915     } : {
9916         input:1, select:1, textarea:1
9917     };
9918     if(Roo.isIE || Roo.isGecko){
9919         noBoxAdjust['button'] = 1;
9920     }
9921
9922
9923     Roo.EventManager.on(window, 'unload', function(){
9924         delete El.cache;
9925         delete El._flyweights;
9926     });
9927 })();
9928
9929
9930
9931
9932 if(Roo.DomQuery){
9933     Roo.Element.selectorFunction = Roo.DomQuery.select;
9934 }
9935
9936 Roo.Element.select = function(selector, unique, root){
9937     var els;
9938     if(typeof selector == "string"){
9939         els = Roo.Element.selectorFunction(selector, root);
9940     }else if(selector.length !== undefined){
9941         els = selector;
9942     }else{
9943         throw "Invalid selector";
9944     }
9945     if(unique === true){
9946         return new Roo.CompositeElement(els);
9947     }else{
9948         return new Roo.CompositeElementLite(els);
9949     }
9950 };
9951 /**
9952  * Selects elements based on the passed CSS selector to enable working on them as 1.
9953  * @param {String/Array} selector The CSS selector or an array of elements
9954  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9955  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9956  * @return {CompositeElementLite/CompositeElement}
9957  * @member Roo
9958  * @method select
9959  */
9960 Roo.select = Roo.Element.select;
9961
9962
9963
9964
9965
9966
9967
9968
9969
9970
9971
9972
9973
9974
9975 /*
9976  * Based on:
9977  * Ext JS Library 1.1.1
9978  * Copyright(c) 2006-2007, Ext JS, LLC.
9979  *
9980  * Originally Released Under LGPL - original licence link has changed is not relivant.
9981  *
9982  * Fork - LGPL
9983  * <script type="text/javascript">
9984  */
9985
9986
9987
9988 //Notifies Element that fx methods are available
9989 Roo.enableFx = true;
9990
9991 /**
9992  * @class Roo.Fx
9993  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9994  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9995  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9996  * Element effects to work.</p><br/>
9997  *
9998  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9999  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10000  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10001  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10002  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10003  * expected results and should be done with care.</p><br/>
10004  *
10005  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10006  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10007 <pre>
10008 Value  Description
10009 -----  -----------------------------
10010 tl     The top left corner
10011 t      The center of the top edge
10012 tr     The top right corner
10013 l      The center of the left edge
10014 r      The center of the right edge
10015 bl     The bottom left corner
10016 b      The center of the bottom edge
10017 br     The bottom right corner
10018 </pre>
10019  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10020  * below are common options that can be passed to any Fx method.</b>
10021  * @cfg {Function} callback A function called when the effect is finished
10022  * @cfg {Object} scope The scope of the effect function
10023  * @cfg {String} easing A valid Easing value for the effect
10024  * @cfg {String} afterCls A css class to apply after the effect
10025  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10026  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10027  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10028  * effects that end with the element being visually hidden, ignored otherwise)
10029  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10030  * a function which returns such a specification that will be applied to the Element after the effect finishes
10031  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10032  * @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
10033  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10034  */
10035 Roo.Fx = {
10036         /**
10037          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10038          * origin for the slide effect.  This function automatically handles wrapping the element with
10039          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10040          * Usage:
10041          *<pre><code>
10042 // default: slide the element in from the top
10043 el.slideIn();
10044
10045 // custom: slide the element in from the right with a 2-second duration
10046 el.slideIn('r', { duration: 2 });
10047
10048 // common config options shown with default values
10049 el.slideIn('t', {
10050     easing: 'easeOut',
10051     duration: .5
10052 });
10053 </code></pre>
10054          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10055          * @param {Object} options (optional) Object literal with any of the Fx config options
10056          * @return {Roo.Element} The Element
10057          */
10058     slideIn : function(anchor, o){
10059         var el = this.getFxEl();
10060         o = o || {};
10061
10062         el.queueFx(o, function(){
10063
10064             anchor = anchor || "t";
10065
10066             // fix display to visibility
10067             this.fixDisplay();
10068
10069             // restore values after effect
10070             var r = this.getFxRestore();
10071             var b = this.getBox();
10072             // fixed size for slide
10073             this.setSize(b);
10074
10075             // wrap if needed
10076             var wrap = this.fxWrap(r.pos, o, "hidden");
10077
10078             var st = this.dom.style;
10079             st.visibility = "visible";
10080             st.position = "absolute";
10081
10082             // clear out temp styles after slide and unwrap
10083             var after = function(){
10084                 el.fxUnwrap(wrap, r.pos, o);
10085                 st.width = r.width;
10086                 st.height = r.height;
10087                 el.afterFx(o);
10088             };
10089             // time to calc the positions
10090             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10091
10092             switch(anchor.toLowerCase()){
10093                 case "t":
10094                     wrap.setSize(b.width, 0);
10095                     st.left = st.bottom = "0";
10096                     a = {height: bh};
10097                 break;
10098                 case "l":
10099                     wrap.setSize(0, b.height);
10100                     st.right = st.top = "0";
10101                     a = {width: bw};
10102                 break;
10103                 case "r":
10104                     wrap.setSize(0, b.height);
10105                     wrap.setX(b.right);
10106                     st.left = st.top = "0";
10107                     a = {width: bw, points: pt};
10108                 break;
10109                 case "b":
10110                     wrap.setSize(b.width, 0);
10111                     wrap.setY(b.bottom);
10112                     st.left = st.top = "0";
10113                     a = {height: bh, points: pt};
10114                 break;
10115                 case "tl":
10116                     wrap.setSize(0, 0);
10117                     st.right = st.bottom = "0";
10118                     a = {width: bw, height: bh};
10119                 break;
10120                 case "bl":
10121                     wrap.setSize(0, 0);
10122                     wrap.setY(b.y+b.height);
10123                     st.right = st.top = "0";
10124                     a = {width: bw, height: bh, points: pt};
10125                 break;
10126                 case "br":
10127                     wrap.setSize(0, 0);
10128                     wrap.setXY([b.right, b.bottom]);
10129                     st.left = st.top = "0";
10130                     a = {width: bw, height: bh, points: pt};
10131                 break;
10132                 case "tr":
10133                     wrap.setSize(0, 0);
10134                     wrap.setX(b.x+b.width);
10135                     st.left = st.bottom = "0";
10136                     a = {width: bw, height: bh, points: pt};
10137                 break;
10138             }
10139             this.dom.style.visibility = "visible";
10140             wrap.show();
10141
10142             arguments.callee.anim = wrap.fxanim(a,
10143                 o,
10144                 'motion',
10145                 .5,
10146                 'easeOut', after);
10147         });
10148         return this;
10149     },
10150     
10151         /**
10152          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10153          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10154          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10155          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10156          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10157          * Usage:
10158          *<pre><code>
10159 // default: slide the element out to the top
10160 el.slideOut();
10161
10162 // custom: slide the element out to the right with a 2-second duration
10163 el.slideOut('r', { duration: 2 });
10164
10165 // common config options shown with default values
10166 el.slideOut('t', {
10167     easing: 'easeOut',
10168     duration: .5,
10169     remove: false,
10170     useDisplay: false
10171 });
10172 </code></pre>
10173          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10174          * @param {Object} options (optional) Object literal with any of the Fx config options
10175          * @return {Roo.Element} The Element
10176          */
10177     slideOut : function(anchor, o){
10178         var el = this.getFxEl();
10179         o = o || {};
10180
10181         el.queueFx(o, function(){
10182
10183             anchor = anchor || "t";
10184
10185             // restore values after effect
10186             var r = this.getFxRestore();
10187             
10188             var b = this.getBox();
10189             // fixed size for slide
10190             this.setSize(b);
10191
10192             // wrap if needed
10193             var wrap = this.fxWrap(r.pos, o, "visible");
10194
10195             var st = this.dom.style;
10196             st.visibility = "visible";
10197             st.position = "absolute";
10198
10199             wrap.setSize(b);
10200
10201             var after = function(){
10202                 if(o.useDisplay){
10203                     el.setDisplayed(false);
10204                 }else{
10205                     el.hide();
10206                 }
10207
10208                 el.fxUnwrap(wrap, r.pos, o);
10209
10210                 st.width = r.width;
10211                 st.height = r.height;
10212
10213                 el.afterFx(o);
10214             };
10215
10216             var a, zero = {to: 0};
10217             switch(anchor.toLowerCase()){
10218                 case "t":
10219                     st.left = st.bottom = "0";
10220                     a = {height: zero};
10221                 break;
10222                 case "l":
10223                     st.right = st.top = "0";
10224                     a = {width: zero};
10225                 break;
10226                 case "r":
10227                     st.left = st.top = "0";
10228                     a = {width: zero, points: {to:[b.right, b.y]}};
10229                 break;
10230                 case "b":
10231                     st.left = st.top = "0";
10232                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10233                 break;
10234                 case "tl":
10235                     st.right = st.bottom = "0";
10236                     a = {width: zero, height: zero};
10237                 break;
10238                 case "bl":
10239                     st.right = st.top = "0";
10240                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10241                 break;
10242                 case "br":
10243                     st.left = st.top = "0";
10244                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10245                 break;
10246                 case "tr":
10247                     st.left = st.bottom = "0";
10248                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10249                 break;
10250             }
10251
10252             arguments.callee.anim = wrap.fxanim(a,
10253                 o,
10254                 'motion',
10255                 .5,
10256                 "easeOut", after);
10257         });
10258         return this;
10259     },
10260
10261         /**
10262          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10263          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10264          * The element must be removed from the DOM using the 'remove' config option if desired.
10265          * Usage:
10266          *<pre><code>
10267 // default
10268 el.puff();
10269
10270 // common config options shown with default values
10271 el.puff({
10272     easing: 'easeOut',
10273     duration: .5,
10274     remove: false,
10275     useDisplay: false
10276 });
10277 </code></pre>
10278          * @param {Object} options (optional) Object literal with any of the Fx config options
10279          * @return {Roo.Element} The Element
10280          */
10281     puff : function(o){
10282         var el = this.getFxEl();
10283         o = o || {};
10284
10285         el.queueFx(o, function(){
10286             this.clearOpacity();
10287             this.show();
10288
10289             // restore values after effect
10290             var r = this.getFxRestore();
10291             var st = this.dom.style;
10292
10293             var after = function(){
10294                 if(o.useDisplay){
10295                     el.setDisplayed(false);
10296                 }else{
10297                     el.hide();
10298                 }
10299
10300                 el.clearOpacity();
10301
10302                 el.setPositioning(r.pos);
10303                 st.width = r.width;
10304                 st.height = r.height;
10305                 st.fontSize = '';
10306                 el.afterFx(o);
10307             };
10308
10309             var width = this.getWidth();
10310             var height = this.getHeight();
10311
10312             arguments.callee.anim = this.fxanim({
10313                     width : {to: this.adjustWidth(width * 2)},
10314                     height : {to: this.adjustHeight(height * 2)},
10315                     points : {by: [-(width * .5), -(height * .5)]},
10316                     opacity : {to: 0},
10317                     fontSize: {to:200, unit: "%"}
10318                 },
10319                 o,
10320                 'motion',
10321                 .5,
10322                 "easeOut", after);
10323         });
10324         return this;
10325     },
10326
10327         /**
10328          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10329          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10330          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10331          * Usage:
10332          *<pre><code>
10333 // default
10334 el.switchOff();
10335
10336 // all config options shown with default values
10337 el.switchOff({
10338     easing: 'easeIn',
10339     duration: .3,
10340     remove: false,
10341     useDisplay: false
10342 });
10343 </code></pre>
10344          * @param {Object} options (optional) Object literal with any of the Fx config options
10345          * @return {Roo.Element} The Element
10346          */
10347     switchOff : function(o){
10348         var el = this.getFxEl();
10349         o = o || {};
10350
10351         el.queueFx(o, function(){
10352             this.clearOpacity();
10353             this.clip();
10354
10355             // restore values after effect
10356             var r = this.getFxRestore();
10357             var st = this.dom.style;
10358
10359             var after = function(){
10360                 if(o.useDisplay){
10361                     el.setDisplayed(false);
10362                 }else{
10363                     el.hide();
10364                 }
10365
10366                 el.clearOpacity();
10367                 el.setPositioning(r.pos);
10368                 st.width = r.width;
10369                 st.height = r.height;
10370
10371                 el.afterFx(o);
10372             };
10373
10374             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10375                 this.clearOpacity();
10376                 (function(){
10377                     this.fxanim({
10378                         height:{to:1},
10379                         points:{by:[0, this.getHeight() * .5]}
10380                     }, o, 'motion', 0.3, 'easeIn', after);
10381                 }).defer(100, this);
10382             });
10383         });
10384         return this;
10385     },
10386
10387     /**
10388      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10389      * changed using the "attr" config option) and then fading back to the original color. If no original
10390      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10391      * Usage:
10392 <pre><code>
10393 // default: highlight background to yellow
10394 el.highlight();
10395
10396 // custom: highlight foreground text to blue for 2 seconds
10397 el.highlight("0000ff", { attr: 'color', duration: 2 });
10398
10399 // common config options shown with default values
10400 el.highlight("ffff9c", {
10401     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10402     endColor: (current color) or "ffffff",
10403     easing: 'easeIn',
10404     duration: 1
10405 });
10406 </code></pre>
10407      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10408      * @param {Object} options (optional) Object literal with any of the Fx config options
10409      * @return {Roo.Element} The Element
10410      */ 
10411     highlight : function(color, o){
10412         var el = this.getFxEl();
10413         o = o || {};
10414
10415         el.queueFx(o, function(){
10416             color = color || "ffff9c";
10417             attr = o.attr || "backgroundColor";
10418
10419             this.clearOpacity();
10420             this.show();
10421
10422             var origColor = this.getColor(attr);
10423             var restoreColor = this.dom.style[attr];
10424             endColor = (o.endColor || origColor) || "ffffff";
10425
10426             var after = function(){
10427                 el.dom.style[attr] = restoreColor;
10428                 el.afterFx(o);
10429             };
10430
10431             var a = {};
10432             a[attr] = {from: color, to: endColor};
10433             arguments.callee.anim = this.fxanim(a,
10434                 o,
10435                 'color',
10436                 1,
10437                 'easeIn', after);
10438         });
10439         return this;
10440     },
10441
10442    /**
10443     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10444     * Usage:
10445 <pre><code>
10446 // default: a single light blue ripple
10447 el.frame();
10448
10449 // custom: 3 red ripples lasting 3 seconds total
10450 el.frame("ff0000", 3, { duration: 3 });
10451
10452 // common config options shown with default values
10453 el.frame("C3DAF9", 1, {
10454     duration: 1 //duration of entire animation (not each individual ripple)
10455     // Note: Easing is not configurable and will be ignored if included
10456 });
10457 </code></pre>
10458     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10459     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10460     * @param {Object} options (optional) Object literal with any of the Fx config options
10461     * @return {Roo.Element} The Element
10462     */
10463     frame : function(color, count, o){
10464         var el = this.getFxEl();
10465         o = o || {};
10466
10467         el.queueFx(o, function(){
10468             color = color || "#C3DAF9";
10469             if(color.length == 6){
10470                 color = "#" + color;
10471             }
10472             count = count || 1;
10473             duration = o.duration || 1;
10474             this.show();
10475
10476             var b = this.getBox();
10477             var animFn = function(){
10478                 var proxy = this.createProxy({
10479
10480                      style:{
10481                         visbility:"hidden",
10482                         position:"absolute",
10483                         "z-index":"35000", // yee haw
10484                         border:"0px solid " + color
10485                      }
10486                   });
10487                 var scale = Roo.isBorderBox ? 2 : 1;
10488                 proxy.animate({
10489                     top:{from:b.y, to:b.y - 20},
10490                     left:{from:b.x, to:b.x - 20},
10491                     borderWidth:{from:0, to:10},
10492                     opacity:{from:1, to:0},
10493                     height:{from:b.height, to:(b.height + (20*scale))},
10494                     width:{from:b.width, to:(b.width + (20*scale))}
10495                 }, duration, function(){
10496                     proxy.remove();
10497                 });
10498                 if(--count > 0){
10499                      animFn.defer((duration/2)*1000, this);
10500                 }else{
10501                     el.afterFx(o);
10502                 }
10503             };
10504             animFn.call(this);
10505         });
10506         return this;
10507     },
10508
10509    /**
10510     * Creates a pause before any subsequent queued effects begin.  If there are
10511     * no effects queued after the pause it will have no effect.
10512     * Usage:
10513 <pre><code>
10514 el.pause(1);
10515 </code></pre>
10516     * @param {Number} seconds The length of time to pause (in seconds)
10517     * @return {Roo.Element} The Element
10518     */
10519     pause : function(seconds){
10520         var el = this.getFxEl();
10521         var o = {};
10522
10523         el.queueFx(o, function(){
10524             setTimeout(function(){
10525                 el.afterFx(o);
10526             }, seconds * 1000);
10527         });
10528         return this;
10529     },
10530
10531    /**
10532     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10533     * using the "endOpacity" config option.
10534     * Usage:
10535 <pre><code>
10536 // default: fade in from opacity 0 to 100%
10537 el.fadeIn();
10538
10539 // custom: fade in from opacity 0 to 75% over 2 seconds
10540 el.fadeIn({ endOpacity: .75, duration: 2});
10541
10542 // common config options shown with default values
10543 el.fadeIn({
10544     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10545     easing: 'easeOut',
10546     duration: .5
10547 });
10548 </code></pre>
10549     * @param {Object} options (optional) Object literal with any of the Fx config options
10550     * @return {Roo.Element} The Element
10551     */
10552     fadeIn : function(o){
10553         var el = this.getFxEl();
10554         o = o || {};
10555         el.queueFx(o, function(){
10556             this.setOpacity(0);
10557             this.fixDisplay();
10558             this.dom.style.visibility = 'visible';
10559             var to = o.endOpacity || 1;
10560             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10561                 o, null, .5, "easeOut", function(){
10562                 if(to == 1){
10563                     this.clearOpacity();
10564                 }
10565                 el.afterFx(o);
10566             });
10567         });
10568         return this;
10569     },
10570
10571    /**
10572     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10573     * using the "endOpacity" config option.
10574     * Usage:
10575 <pre><code>
10576 // default: fade out from the element's current opacity to 0
10577 el.fadeOut();
10578
10579 // custom: fade out from the element's current opacity to 25% over 2 seconds
10580 el.fadeOut({ endOpacity: .25, duration: 2});
10581
10582 // common config options shown with default values
10583 el.fadeOut({
10584     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10585     easing: 'easeOut',
10586     duration: .5
10587     remove: false,
10588     useDisplay: false
10589 });
10590 </code></pre>
10591     * @param {Object} options (optional) Object literal with any of the Fx config options
10592     * @return {Roo.Element} The Element
10593     */
10594     fadeOut : function(o){
10595         var el = this.getFxEl();
10596         o = o || {};
10597         el.queueFx(o, function(){
10598             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10599                 o, null, .5, "easeOut", function(){
10600                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10601                      this.dom.style.display = "none";
10602                 }else{
10603                      this.dom.style.visibility = "hidden";
10604                 }
10605                 this.clearOpacity();
10606                 el.afterFx(o);
10607             });
10608         });
10609         return this;
10610     },
10611
10612    /**
10613     * Animates the transition of an element's dimensions from a starting height/width
10614     * to an ending height/width.
10615     * Usage:
10616 <pre><code>
10617 // change height and width to 100x100 pixels
10618 el.scale(100, 100);
10619
10620 // common config options shown with default values.  The height and width will default to
10621 // the element's existing values if passed as null.
10622 el.scale(
10623     [element's width],
10624     [element's height], {
10625     easing: 'easeOut',
10626     duration: .35
10627 });
10628 </code></pre>
10629     * @param {Number} width  The new width (pass undefined to keep the original width)
10630     * @param {Number} height  The new height (pass undefined to keep the original height)
10631     * @param {Object} options (optional) Object literal with any of the Fx config options
10632     * @return {Roo.Element} The Element
10633     */
10634     scale : function(w, h, o){
10635         this.shift(Roo.apply({}, o, {
10636             width: w,
10637             height: h
10638         }));
10639         return this;
10640     },
10641
10642    /**
10643     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10644     * Any of these properties not specified in the config object will not be changed.  This effect 
10645     * requires that at least one new dimension, position or opacity setting must be passed in on
10646     * the config object in order for the function to have any effect.
10647     * Usage:
10648 <pre><code>
10649 // slide the element horizontally to x position 200 while changing the height and opacity
10650 el.shift({ x: 200, height: 50, opacity: .8 });
10651
10652 // common config options shown with default values.
10653 el.shift({
10654     width: [element's width],
10655     height: [element's height],
10656     x: [element's x position],
10657     y: [element's y position],
10658     opacity: [element's opacity],
10659     easing: 'easeOut',
10660     duration: .35
10661 });
10662 </code></pre>
10663     * @param {Object} options  Object literal with any of the Fx config options
10664     * @return {Roo.Element} The Element
10665     */
10666     shift : function(o){
10667         var el = this.getFxEl();
10668         o = o || {};
10669         el.queueFx(o, function(){
10670             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10671             if(w !== undefined){
10672                 a.width = {to: this.adjustWidth(w)};
10673             }
10674             if(h !== undefined){
10675                 a.height = {to: this.adjustHeight(h)};
10676             }
10677             if(x !== undefined || y !== undefined){
10678                 a.points = {to: [
10679                     x !== undefined ? x : this.getX(),
10680                     y !== undefined ? y : this.getY()
10681                 ]};
10682             }
10683             if(op !== undefined){
10684                 a.opacity = {to: op};
10685             }
10686             if(o.xy !== undefined){
10687                 a.points = {to: o.xy};
10688             }
10689             arguments.callee.anim = this.fxanim(a,
10690                 o, 'motion', .35, "easeOut", function(){
10691                 el.afterFx(o);
10692             });
10693         });
10694         return this;
10695     },
10696
10697         /**
10698          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10699          * ending point of the effect.
10700          * Usage:
10701          *<pre><code>
10702 // default: slide the element downward while fading out
10703 el.ghost();
10704
10705 // custom: slide the element out to the right with a 2-second duration
10706 el.ghost('r', { duration: 2 });
10707
10708 // common config options shown with default values
10709 el.ghost('b', {
10710     easing: 'easeOut',
10711     duration: .5
10712     remove: false,
10713     useDisplay: false
10714 });
10715 </code></pre>
10716          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10717          * @param {Object} options (optional) Object literal with any of the Fx config options
10718          * @return {Roo.Element} The Element
10719          */
10720     ghost : function(anchor, o){
10721         var el = this.getFxEl();
10722         o = o || {};
10723
10724         el.queueFx(o, function(){
10725             anchor = anchor || "b";
10726
10727             // restore values after effect
10728             var r = this.getFxRestore();
10729             var w = this.getWidth(),
10730                 h = this.getHeight();
10731
10732             var st = this.dom.style;
10733
10734             var after = function(){
10735                 if(o.useDisplay){
10736                     el.setDisplayed(false);
10737                 }else{
10738                     el.hide();
10739                 }
10740
10741                 el.clearOpacity();
10742                 el.setPositioning(r.pos);
10743                 st.width = r.width;
10744                 st.height = r.height;
10745
10746                 el.afterFx(o);
10747             };
10748
10749             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10750             switch(anchor.toLowerCase()){
10751                 case "t":
10752                     pt.by = [0, -h];
10753                 break;
10754                 case "l":
10755                     pt.by = [-w, 0];
10756                 break;
10757                 case "r":
10758                     pt.by = [w, 0];
10759                 break;
10760                 case "b":
10761                     pt.by = [0, h];
10762                 break;
10763                 case "tl":
10764                     pt.by = [-w, -h];
10765                 break;
10766                 case "bl":
10767                     pt.by = [-w, h];
10768                 break;
10769                 case "br":
10770                     pt.by = [w, h];
10771                 break;
10772                 case "tr":
10773                     pt.by = [w, -h];
10774                 break;
10775             }
10776
10777             arguments.callee.anim = this.fxanim(a,
10778                 o,
10779                 'motion',
10780                 .5,
10781                 "easeOut", after);
10782         });
10783         return this;
10784     },
10785
10786         /**
10787          * Ensures that all effects queued after syncFx is called on the element are
10788          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10789          * @return {Roo.Element} The Element
10790          */
10791     syncFx : function(){
10792         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10793             block : false,
10794             concurrent : true,
10795             stopFx : false
10796         });
10797         return this;
10798     },
10799
10800         /**
10801          * Ensures that all effects queued after sequenceFx is called on the element are
10802          * run in sequence.  This is the opposite of {@link #syncFx}.
10803          * @return {Roo.Element} The Element
10804          */
10805     sequenceFx : function(){
10806         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10807             block : false,
10808             concurrent : false,
10809             stopFx : false
10810         });
10811         return this;
10812     },
10813
10814         /* @private */
10815     nextFx : function(){
10816         var ef = this.fxQueue[0];
10817         if(ef){
10818             ef.call(this);
10819         }
10820     },
10821
10822         /**
10823          * Returns true if the element has any effects actively running or queued, else returns false.
10824          * @return {Boolean} True if element has active effects, else false
10825          */
10826     hasActiveFx : function(){
10827         return this.fxQueue && this.fxQueue[0];
10828     },
10829
10830         /**
10831          * Stops any running effects and clears the element's internal effects queue if it contains
10832          * any additional effects that haven't started yet.
10833          * @return {Roo.Element} The Element
10834          */
10835     stopFx : function(){
10836         if(this.hasActiveFx()){
10837             var cur = this.fxQueue[0];
10838             if(cur && cur.anim && cur.anim.isAnimated()){
10839                 this.fxQueue = [cur]; // clear out others
10840                 cur.anim.stop(true);
10841             }
10842         }
10843         return this;
10844     },
10845
10846         /* @private */
10847     beforeFx : function(o){
10848         if(this.hasActiveFx() && !o.concurrent){
10849            if(o.stopFx){
10850                this.stopFx();
10851                return true;
10852            }
10853            return false;
10854         }
10855         return true;
10856     },
10857
10858         /**
10859          * Returns true if the element is currently blocking so that no other effect can be queued
10860          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10861          * used to ensure that an effect initiated by a user action runs to completion prior to the
10862          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10863          * @return {Boolean} True if blocking, else false
10864          */
10865     hasFxBlock : function(){
10866         var q = this.fxQueue;
10867         return q && q[0] && q[0].block;
10868     },
10869
10870         /* @private */
10871     queueFx : function(o, fn){
10872         if(!this.fxQueue){
10873             this.fxQueue = [];
10874         }
10875         if(!this.hasFxBlock()){
10876             Roo.applyIf(o, this.fxDefaults);
10877             if(!o.concurrent){
10878                 var run = this.beforeFx(o);
10879                 fn.block = o.block;
10880                 this.fxQueue.push(fn);
10881                 if(run){
10882                     this.nextFx();
10883                 }
10884             }else{
10885                 fn.call(this);
10886             }
10887         }
10888         return this;
10889     },
10890
10891         /* @private */
10892     fxWrap : function(pos, o, vis){
10893         var wrap;
10894         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10895             var wrapXY;
10896             if(o.fixPosition){
10897                 wrapXY = this.getXY();
10898             }
10899             var div = document.createElement("div");
10900             div.style.visibility = vis;
10901             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10902             wrap.setPositioning(pos);
10903             if(wrap.getStyle("position") == "static"){
10904                 wrap.position("relative");
10905             }
10906             this.clearPositioning('auto');
10907             wrap.clip();
10908             wrap.dom.appendChild(this.dom);
10909             if(wrapXY){
10910                 wrap.setXY(wrapXY);
10911             }
10912         }
10913         return wrap;
10914     },
10915
10916         /* @private */
10917     fxUnwrap : function(wrap, pos, o){
10918         this.clearPositioning();
10919         this.setPositioning(pos);
10920         if(!o.wrap){
10921             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10922             wrap.remove();
10923         }
10924     },
10925
10926         /* @private */
10927     getFxRestore : function(){
10928         var st = this.dom.style;
10929         return {pos: this.getPositioning(), width: st.width, height : st.height};
10930     },
10931
10932         /* @private */
10933     afterFx : function(o){
10934         if(o.afterStyle){
10935             this.applyStyles(o.afterStyle);
10936         }
10937         if(o.afterCls){
10938             this.addClass(o.afterCls);
10939         }
10940         if(o.remove === true){
10941             this.remove();
10942         }
10943         Roo.callback(o.callback, o.scope, [this]);
10944         if(!o.concurrent){
10945             this.fxQueue.shift();
10946             this.nextFx();
10947         }
10948     },
10949
10950         /* @private */
10951     getFxEl : function(){ // support for composite element fx
10952         return Roo.get(this.dom);
10953     },
10954
10955         /* @private */
10956     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10957         animType = animType || 'run';
10958         opt = opt || {};
10959         var anim = Roo.lib.Anim[animType](
10960             this.dom, args,
10961             (opt.duration || defaultDur) || .35,
10962             (opt.easing || defaultEase) || 'easeOut',
10963             function(){
10964                 Roo.callback(cb, this);
10965             },
10966             this
10967         );
10968         opt.anim = anim;
10969         return anim;
10970     }
10971 };
10972
10973 // backwords compat
10974 Roo.Fx.resize = Roo.Fx.scale;
10975
10976 //When included, Roo.Fx is automatically applied to Element so that all basic
10977 //effects are available directly via the Element API
10978 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10979  * Based on:
10980  * Ext JS Library 1.1.1
10981  * Copyright(c) 2006-2007, Ext JS, LLC.
10982  *
10983  * Originally Released Under LGPL - original licence link has changed is not relivant.
10984  *
10985  * Fork - LGPL
10986  * <script type="text/javascript">
10987  */
10988
10989
10990 /**
10991  * @class Roo.CompositeElement
10992  * Standard composite class. Creates a Roo.Element for every element in the collection.
10993  * <br><br>
10994  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10995  * actions will be performed on all the elements in this collection.</b>
10996  * <br><br>
10997  * All methods return <i>this</i> and can be chained.
10998  <pre><code>
10999  var els = Roo.select("#some-el div.some-class", true);
11000  // or select directly from an existing element
11001  var el = Roo.get('some-el');
11002  el.select('div.some-class', true);
11003
11004  els.setWidth(100); // all elements become 100 width
11005  els.hide(true); // all elements fade out and hide
11006  // or
11007  els.setWidth(100).hide(true);
11008  </code></pre>
11009  */
11010 Roo.CompositeElement = function(els){
11011     this.elements = [];
11012     this.addElements(els);
11013 };
11014 Roo.CompositeElement.prototype = {
11015     isComposite: true,
11016     addElements : function(els){
11017         if(!els) return this;
11018         if(typeof els == "string"){
11019             els = Roo.Element.selectorFunction(els);
11020         }
11021         var yels = this.elements;
11022         var index = yels.length-1;
11023         for(var i = 0, len = els.length; i < len; i++) {
11024                 yels[++index] = Roo.get(els[i]);
11025         }
11026         return this;
11027     },
11028
11029     /**
11030     * Clears this composite and adds the elements returned by the passed selector.
11031     * @param {String/Array} els A string CSS selector, an array of elements or an element
11032     * @return {CompositeElement} this
11033     */
11034     fill : function(els){
11035         this.elements = [];
11036         this.add(els);
11037         return this;
11038     },
11039
11040     /**
11041     * Filters this composite to only elements that match the passed selector.
11042     * @param {String} selector A string CSS selector
11043     * @param {Boolean} inverse return inverse filter (not matches)
11044     * @return {CompositeElement} this
11045     */
11046     filter : function(selector, inverse){
11047         var els = [];
11048         inverse = inverse || false;
11049         this.each(function(el){
11050             var match = inverse ? !el.is(selector) : el.is(selector);
11051             if(match){
11052                 els[els.length] = el.dom;
11053             }
11054         });
11055         this.fill(els);
11056         return this;
11057     },
11058
11059     invoke : function(fn, args){
11060         var els = this.elements;
11061         for(var i = 0, len = els.length; i < len; i++) {
11062                 Roo.Element.prototype[fn].apply(els[i], args);
11063         }
11064         return this;
11065     },
11066     /**
11067     * Adds elements to this composite.
11068     * @param {String/Array} els A string CSS selector, an array of elements or an element
11069     * @return {CompositeElement} this
11070     */
11071     add : function(els){
11072         if(typeof els == "string"){
11073             this.addElements(Roo.Element.selectorFunction(els));
11074         }else if(els.length !== undefined){
11075             this.addElements(els);
11076         }else{
11077             this.addElements([els]);
11078         }
11079         return this;
11080     },
11081     /**
11082     * Calls the passed function passing (el, this, index) for each element in this composite.
11083     * @param {Function} fn The function to call
11084     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11085     * @return {CompositeElement} this
11086     */
11087     each : function(fn, scope){
11088         var els = this.elements;
11089         for(var i = 0, len = els.length; i < len; i++){
11090             if(fn.call(scope || els[i], els[i], this, i) === false) {
11091                 break;
11092             }
11093         }
11094         return this;
11095     },
11096
11097     /**
11098      * Returns the Element object at the specified index
11099      * @param {Number} index
11100      * @return {Roo.Element}
11101      */
11102     item : function(index){
11103         return this.elements[index] || null;
11104     },
11105
11106     /**
11107      * Returns the first Element
11108      * @return {Roo.Element}
11109      */
11110     first : function(){
11111         return this.item(0);
11112     },
11113
11114     /**
11115      * Returns the last Element
11116      * @return {Roo.Element}
11117      */
11118     last : function(){
11119         return this.item(this.elements.length-1);
11120     },
11121
11122     /**
11123      * Returns the number of elements in this composite
11124      * @return Number
11125      */
11126     getCount : function(){
11127         return this.elements.length;
11128     },
11129
11130     /**
11131      * Returns true if this composite contains the passed element
11132      * @return Boolean
11133      */
11134     contains : function(el){
11135         return this.indexOf(el) !== -1;
11136     },
11137
11138     /**
11139      * Returns true if this composite contains the passed element
11140      * @return Boolean
11141      */
11142     indexOf : function(el){
11143         return this.elements.indexOf(Roo.get(el));
11144     },
11145
11146
11147     /**
11148     * Removes the specified element(s).
11149     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11150     * or an array of any of those.
11151     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11152     * @return {CompositeElement} this
11153     */
11154     removeElement : function(el, removeDom){
11155         if(el instanceof Array){
11156             for(var i = 0, len = el.length; i < len; i++){
11157                 this.removeElement(el[i]);
11158             }
11159             return this;
11160         }
11161         var index = typeof el == 'number' ? el : this.indexOf(el);
11162         if(index !== -1){
11163             if(removeDom){
11164                 var d = this.elements[index];
11165                 if(d.dom){
11166                     d.remove();
11167                 }else{
11168                     d.parentNode.removeChild(d);
11169                 }
11170             }
11171             this.elements.splice(index, 1);
11172         }
11173         return this;
11174     },
11175
11176     /**
11177     * Replaces the specified element with the passed element.
11178     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11179     * to replace.
11180     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11181     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11182     * @return {CompositeElement} this
11183     */
11184     replaceElement : function(el, replacement, domReplace){
11185         var index = typeof el == 'number' ? el : this.indexOf(el);
11186         if(index !== -1){
11187             if(domReplace){
11188                 this.elements[index].replaceWith(replacement);
11189             }else{
11190                 this.elements.splice(index, 1, Roo.get(replacement))
11191             }
11192         }
11193         return this;
11194     },
11195
11196     /**
11197      * Removes all elements.
11198      */
11199     clear : function(){
11200         this.elements = [];
11201     }
11202 };
11203 (function(){
11204     Roo.CompositeElement.createCall = function(proto, fnName){
11205         if(!proto[fnName]){
11206             proto[fnName] = function(){
11207                 return this.invoke(fnName, arguments);
11208             };
11209         }
11210     };
11211     for(var fnName in Roo.Element.prototype){
11212         if(typeof Roo.Element.prototype[fnName] == "function"){
11213             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11214         }
11215     };
11216 })();
11217 /*
11218  * Based on:
11219  * Ext JS Library 1.1.1
11220  * Copyright(c) 2006-2007, Ext JS, LLC.
11221  *
11222  * Originally Released Under LGPL - original licence link has changed is not relivant.
11223  *
11224  * Fork - LGPL
11225  * <script type="text/javascript">
11226  */
11227
11228 /**
11229  * @class Roo.CompositeElementLite
11230  * @extends Roo.CompositeElement
11231  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11232  <pre><code>
11233  var els = Roo.select("#some-el div.some-class");
11234  // or select directly from an existing element
11235  var el = Roo.get('some-el');
11236  el.select('div.some-class');
11237
11238  els.setWidth(100); // all elements become 100 width
11239  els.hide(true); // all elements fade out and hide
11240  // or
11241  els.setWidth(100).hide(true);
11242  </code></pre><br><br>
11243  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11244  * actions will be performed on all the elements in this collection.</b>
11245  */
11246 Roo.CompositeElementLite = function(els){
11247     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11248     this.el = new Roo.Element.Flyweight();
11249 };
11250 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11251     addElements : function(els){
11252         if(els){
11253             if(els instanceof Array){
11254                 this.elements = this.elements.concat(els);
11255             }else{
11256                 var yels = this.elements;
11257                 var index = yels.length-1;
11258                 for(var i = 0, len = els.length; i < len; i++) {
11259                     yels[++index] = els[i];
11260                 }
11261             }
11262         }
11263         return this;
11264     },
11265     invoke : function(fn, args){
11266         var els = this.elements;
11267         var el = this.el;
11268         for(var i = 0, len = els.length; i < len; i++) {
11269             el.dom = els[i];
11270                 Roo.Element.prototype[fn].apply(el, args);
11271         }
11272         return this;
11273     },
11274     /**
11275      * Returns a flyweight Element of the dom element object at the specified index
11276      * @param {Number} index
11277      * @return {Roo.Element}
11278      */
11279     item : function(index){
11280         if(!this.elements[index]){
11281             return null;
11282         }
11283         this.el.dom = this.elements[index];
11284         return this.el;
11285     },
11286
11287     // fixes scope with flyweight
11288     addListener : function(eventName, handler, scope, opt){
11289         var els = this.elements;
11290         for(var i = 0, len = els.length; i < len; i++) {
11291             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11292         }
11293         return this;
11294     },
11295
11296     /**
11297     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11298     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11299     * a reference to the dom node, use el.dom.</b>
11300     * @param {Function} fn The function to call
11301     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11302     * @return {CompositeElement} this
11303     */
11304     each : function(fn, scope){
11305         var els = this.elements;
11306         var el = this.el;
11307         for(var i = 0, len = els.length; i < len; i++){
11308             el.dom = els[i];
11309                 if(fn.call(scope || el, el, this, i) === false){
11310                 break;
11311             }
11312         }
11313         return this;
11314     },
11315
11316     indexOf : function(el){
11317         return this.elements.indexOf(Roo.getDom(el));
11318     },
11319
11320     replaceElement : function(el, replacement, domReplace){
11321         var index = typeof el == 'number' ? el : this.indexOf(el);
11322         if(index !== -1){
11323             replacement = Roo.getDom(replacement);
11324             if(domReplace){
11325                 var d = this.elements[index];
11326                 d.parentNode.insertBefore(replacement, d);
11327                 d.parentNode.removeChild(d);
11328             }
11329             this.elements.splice(index, 1, replacement);
11330         }
11331         return this;
11332     }
11333 });
11334 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11335
11336 /*
11337  * Based on:
11338  * Ext JS Library 1.1.1
11339  * Copyright(c) 2006-2007, Ext JS, LLC.
11340  *
11341  * Originally Released Under LGPL - original licence link has changed is not relivant.
11342  *
11343  * Fork - LGPL
11344  * <script type="text/javascript">
11345  */
11346
11347  
11348
11349 /**
11350  * @class Roo.data.Connection
11351  * @extends Roo.util.Observable
11352  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11353  * either to a configured URL, or to a URL specified at request time.<br><br>
11354  * <p>
11355  * Requests made by this class are asynchronous, and will return immediately. No data from
11356  * the server will be available to the statement immediately following the {@link #request} call.
11357  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11358  * <p>
11359  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11360  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11361  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11362  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11363  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11364  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11365  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11366  * standard DOM methods.
11367  * @constructor
11368  * @param {Object} config a configuration object.
11369  */
11370 Roo.data.Connection = function(config){
11371     Roo.apply(this, config);
11372     this.addEvents({
11373         /**
11374          * @event beforerequest
11375          * Fires before a network request is made to retrieve a data object.
11376          * @param {Connection} conn This Connection object.
11377          * @param {Object} options The options config object passed to the {@link #request} method.
11378          */
11379         "beforerequest" : true,
11380         /**
11381          * @event requestcomplete
11382          * Fires if the request was successfully completed.
11383          * @param {Connection} conn This Connection object.
11384          * @param {Object} response The XHR object containing the response data.
11385          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11386          * @param {Object} options The options config object passed to the {@link #request} method.
11387          */
11388         "requestcomplete" : true,
11389         /**
11390          * @event requestexception
11391          * Fires if an error HTTP status was returned from the server.
11392          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11393          * @param {Connection} conn This Connection object.
11394          * @param {Object} response The XHR object containing the response data.
11395          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11396          * @param {Object} options The options config object passed to the {@link #request} method.
11397          */
11398         "requestexception" : true
11399     });
11400     Roo.data.Connection.superclass.constructor.call(this);
11401 };
11402
11403 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11404     /**
11405      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11406      */
11407     /**
11408      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11409      * extra parameters to each request made by this object. (defaults to undefined)
11410      */
11411     /**
11412      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11413      *  to each request made by this object. (defaults to undefined)
11414      */
11415     /**
11416      * @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)
11417      */
11418     /**
11419      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11420      */
11421     timeout : 30000,
11422     /**
11423      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11424      * @type Boolean
11425      */
11426     autoAbort:false,
11427
11428     /**
11429      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11430      * @type Boolean
11431      */
11432     disableCaching: true,
11433
11434     /**
11435      * Sends an HTTP request to a remote server.
11436      * @param {Object} options An object which may contain the following properties:<ul>
11437      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11438      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11439      * request, a url encoded string or a function to call to get either.</li>
11440      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11441      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11442      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11443      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11444      * <li>options {Object} The parameter to the request call.</li>
11445      * <li>success {Boolean} True if the request succeeded.</li>
11446      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11447      * </ul></li>
11448      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11449      * The callback is passed the following parameters:<ul>
11450      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11451      * <li>options {Object} The parameter to the request call.</li>
11452      * </ul></li>
11453      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11454      * The callback is passed the following parameters:<ul>
11455      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11456      * <li>options {Object} The parameter to the request call.</li>
11457      * </ul></li>
11458      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11459      * for the callback function. Defaults to the browser window.</li>
11460      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11461      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11462      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11463      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11464      * params for the post data. Any params will be appended to the URL.</li>
11465      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11466      * </ul>
11467      * @return {Number} transactionId
11468      */
11469     request : function(o){
11470         if(this.fireEvent("beforerequest", this, o) !== false){
11471             var p = o.params;
11472
11473             if(typeof p == "function"){
11474                 p = p.call(o.scope||window, o);
11475             }
11476             if(typeof p == "object"){
11477                 p = Roo.urlEncode(o.params);
11478             }
11479             if(this.extraParams){
11480                 var extras = Roo.urlEncode(this.extraParams);
11481                 p = p ? (p + '&' + extras) : extras;
11482             }
11483
11484             var url = o.url || this.url;
11485             if(typeof url == 'function'){
11486                 url = url.call(o.scope||window, o);
11487             }
11488
11489             if(o.form){
11490                 var form = Roo.getDom(o.form);
11491                 url = url || form.action;
11492
11493                 var enctype = form.getAttribute("enctype");
11494                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11495                     return this.doFormUpload(o, p, url);
11496                 }
11497                 var f = Roo.lib.Ajax.serializeForm(form);
11498                 p = p ? (p + '&' + f) : f;
11499             }
11500
11501             var hs = o.headers;
11502             if(this.defaultHeaders){
11503                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11504                 if(!o.headers){
11505                     o.headers = hs;
11506                 }
11507             }
11508
11509             var cb = {
11510                 success: this.handleResponse,
11511                 failure: this.handleFailure,
11512                 scope: this,
11513                 argument: {options: o},
11514                 timeout : o.timeout || this.timeout
11515             };
11516
11517             var method = o.method||this.method||(p ? "POST" : "GET");
11518
11519             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11520                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11521             }
11522
11523             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11524                 if(o.autoAbort){
11525                     this.abort();
11526                 }
11527             }else if(this.autoAbort !== false){
11528                 this.abort();
11529             }
11530
11531             if((method == 'GET' && p) || o.xmlData){
11532                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11533                 p = '';
11534             }
11535             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11536             return this.transId;
11537         }else{
11538             Roo.callback(o.callback, o.scope, [o, null, null]);
11539             return null;
11540         }
11541     },
11542
11543     /**
11544      * Determine whether this object has a request outstanding.
11545      * @param {Number} transactionId (Optional) defaults to the last transaction
11546      * @return {Boolean} True if there is an outstanding request.
11547      */
11548     isLoading : function(transId){
11549         if(transId){
11550             return Roo.lib.Ajax.isCallInProgress(transId);
11551         }else{
11552             return this.transId ? true : false;
11553         }
11554     },
11555
11556     /**
11557      * Aborts any outstanding request.
11558      * @param {Number} transactionId (Optional) defaults to the last transaction
11559      */
11560     abort : function(transId){
11561         if(transId || this.isLoading()){
11562             Roo.lib.Ajax.abort(transId || this.transId);
11563         }
11564     },
11565
11566     // private
11567     handleResponse : function(response){
11568         this.transId = false;
11569         var options = response.argument.options;
11570         response.argument = options ? options.argument : null;
11571         this.fireEvent("requestcomplete", this, response, options);
11572         Roo.callback(options.success, options.scope, [response, options]);
11573         Roo.callback(options.callback, options.scope, [options, true, response]);
11574     },
11575
11576     // private
11577     handleFailure : function(response, e){
11578         this.transId = false;
11579         var options = response.argument.options;
11580         response.argument = options ? options.argument : null;
11581         this.fireEvent("requestexception", this, response, options, e);
11582         Roo.callback(options.failure, options.scope, [response, options]);
11583         Roo.callback(options.callback, options.scope, [options, false, response]);
11584     },
11585
11586     // private
11587     doFormUpload : function(o, ps, url){
11588         var id = Roo.id();
11589         var frame = document.createElement('iframe');
11590         frame.id = id;
11591         frame.name = id;
11592         frame.className = 'x-hidden';
11593         if(Roo.isIE){
11594             frame.src = Roo.SSL_SECURE_URL;
11595         }
11596         document.body.appendChild(frame);
11597
11598         if(Roo.isIE){
11599            document.frames[id].name = id;
11600         }
11601
11602         var form = Roo.getDom(o.form);
11603         form.target = id;
11604         form.method = 'POST';
11605         form.enctype = form.encoding = 'multipart/form-data';
11606         if(url){
11607             form.action = url;
11608         }
11609
11610         var hiddens, hd;
11611         if(ps){ // add dynamic params
11612             hiddens = [];
11613             ps = Roo.urlDecode(ps, false);
11614             for(var k in ps){
11615                 if(ps.hasOwnProperty(k)){
11616                     hd = document.createElement('input');
11617                     hd.type = 'hidden';
11618                     hd.name = k;
11619                     hd.value = ps[k];
11620                     form.appendChild(hd);
11621                     hiddens.push(hd);
11622                 }
11623             }
11624         }
11625
11626         function cb(){
11627             var r = {  // bogus response object
11628                 responseText : '',
11629                 responseXML : null
11630             };
11631
11632             r.argument = o ? o.argument : null;
11633
11634             try { //
11635                 var doc;
11636                 if(Roo.isIE){
11637                     doc = frame.contentWindow.document;
11638                 }else {
11639                     doc = (frame.contentDocument || window.frames[id].document);
11640                 }
11641                 if(doc && doc.body){
11642                     r.responseText = doc.body.innerHTML;
11643                 }
11644                 if(doc && doc.XMLDocument){
11645                     r.responseXML = doc.XMLDocument;
11646                 }else {
11647                     r.responseXML = doc;
11648                 }
11649             }
11650             catch(e) {
11651                 // ignore
11652             }
11653
11654             Roo.EventManager.removeListener(frame, 'load', cb, this);
11655
11656             this.fireEvent("requestcomplete", this, r, o);
11657             Roo.callback(o.success, o.scope, [r, o]);
11658             Roo.callback(o.callback, o.scope, [o, true, r]);
11659
11660             setTimeout(function(){document.body.removeChild(frame);}, 100);
11661         }
11662
11663         Roo.EventManager.on(frame, 'load', cb, this);
11664         form.submit();
11665
11666         if(hiddens){ // remove dynamic params
11667             for(var i = 0, len = hiddens.length; i < len; i++){
11668                 form.removeChild(hiddens[i]);
11669             }
11670         }
11671     }
11672 });
11673 /*
11674  * Based on:
11675  * Ext JS Library 1.1.1
11676  * Copyright(c) 2006-2007, Ext JS, LLC.
11677  *
11678  * Originally Released Under LGPL - original licence link has changed is not relivant.
11679  *
11680  * Fork - LGPL
11681  * <script type="text/javascript">
11682  */
11683  
11684 /**
11685  * Global Ajax request class.
11686  * 
11687  * @class Roo.Ajax
11688  * @extends Roo.data.Connection
11689  * @static
11690  * 
11691  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11692  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11693  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11694  * @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)
11695  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11696  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11697  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11698  */
11699 Roo.Ajax = new Roo.data.Connection({
11700     // fix up the docs
11701     /**
11702      * @scope Roo.Ajax
11703      * @type {Boolear} 
11704      */
11705     autoAbort : false,
11706
11707     /**
11708      * Serialize the passed form into a url encoded string
11709      * @scope Roo.Ajax
11710      * @param {String/HTMLElement} form
11711      * @return {String}
11712      */
11713     serializeForm : function(form){
11714         return Roo.lib.Ajax.serializeForm(form);
11715     }
11716 });/*
11717  * Based on:
11718  * Ext JS Library 1.1.1
11719  * Copyright(c) 2006-2007, Ext JS, LLC.
11720  *
11721  * Originally Released Under LGPL - original licence link has changed is not relivant.
11722  *
11723  * Fork - LGPL
11724  * <script type="text/javascript">
11725  */
11726
11727  
11728 /**
11729  * @class Roo.UpdateManager
11730  * @extends Roo.util.Observable
11731  * Provides AJAX-style update for Element object.<br><br>
11732  * Usage:<br>
11733  * <pre><code>
11734  * // Get it from a Roo.Element object
11735  * var el = Roo.get("foo");
11736  * var mgr = el.getUpdateManager();
11737  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11738  * ...
11739  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11740  * <br>
11741  * // or directly (returns the same UpdateManager instance)
11742  * var mgr = new Roo.UpdateManager("myElementId");
11743  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11744  * mgr.on("update", myFcnNeedsToKnow);
11745  * <br>
11746    // short handed call directly from the element object
11747    Roo.get("foo").load({
11748         url: "bar.php",
11749         scripts:true,
11750         params: "for=bar",
11751         text: "Loading Foo..."
11752    });
11753  * </code></pre>
11754  * @constructor
11755  * Create new UpdateManager directly.
11756  * @param {String/HTMLElement/Roo.Element} el The element to update
11757  * @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).
11758  */
11759 Roo.UpdateManager = function(el, forceNew){
11760     el = Roo.get(el);
11761     if(!forceNew && el.updateManager){
11762         return el.updateManager;
11763     }
11764     /**
11765      * The Element object
11766      * @type Roo.Element
11767      */
11768     this.el = el;
11769     /**
11770      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11771      * @type String
11772      */
11773     this.defaultUrl = null;
11774
11775     this.addEvents({
11776         /**
11777          * @event beforeupdate
11778          * Fired before an update is made, return false from your handler and the update is cancelled.
11779          * @param {Roo.Element} el
11780          * @param {String/Object/Function} url
11781          * @param {String/Object} params
11782          */
11783         "beforeupdate": true,
11784         /**
11785          * @event update
11786          * Fired after successful update is made.
11787          * @param {Roo.Element} el
11788          * @param {Object} oResponseObject The response Object
11789          */
11790         "update": true,
11791         /**
11792          * @event failure
11793          * Fired on update failure.
11794          * @param {Roo.Element} el
11795          * @param {Object} oResponseObject The response Object
11796          */
11797         "failure": true
11798     });
11799     var d = Roo.UpdateManager.defaults;
11800     /**
11801      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11802      * @type String
11803      */
11804     this.sslBlankUrl = d.sslBlankUrl;
11805     /**
11806      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11807      * @type Boolean
11808      */
11809     this.disableCaching = d.disableCaching;
11810     /**
11811      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11812      * @type String
11813      */
11814     this.indicatorText = d.indicatorText;
11815     /**
11816      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11817      * @type String
11818      */
11819     this.showLoadIndicator = d.showLoadIndicator;
11820     /**
11821      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11822      * @type Number
11823      */
11824     this.timeout = d.timeout;
11825
11826     /**
11827      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11828      * @type Boolean
11829      */
11830     this.loadScripts = d.loadScripts;
11831
11832     /**
11833      * Transaction object of current executing transaction
11834      */
11835     this.transaction = null;
11836
11837     /**
11838      * @private
11839      */
11840     this.autoRefreshProcId = null;
11841     /**
11842      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11843      * @type Function
11844      */
11845     this.refreshDelegate = this.refresh.createDelegate(this);
11846     /**
11847      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11848      * @type Function
11849      */
11850     this.updateDelegate = this.update.createDelegate(this);
11851     /**
11852      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11853      * @type Function
11854      */
11855     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11856     /**
11857      * @private
11858      */
11859     this.successDelegate = this.processSuccess.createDelegate(this);
11860     /**
11861      * @private
11862      */
11863     this.failureDelegate = this.processFailure.createDelegate(this);
11864
11865     if(!this.renderer){
11866      /**
11867       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11868       */
11869     this.renderer = new Roo.UpdateManager.BasicRenderer();
11870     }
11871     
11872     Roo.UpdateManager.superclass.constructor.call(this);
11873 };
11874
11875 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11876     /**
11877      * Get the Element this UpdateManager is bound to
11878      * @return {Roo.Element} The element
11879      */
11880     getEl : function(){
11881         return this.el;
11882     },
11883     /**
11884      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11885      * @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:
11886 <pre><code>
11887 um.update({<br/>
11888     url: "your-url.php",<br/>
11889     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11890     callback: yourFunction,<br/>
11891     scope: yourObject, //(optional scope)  <br/>
11892     discardUrl: false, <br/>
11893     nocache: false,<br/>
11894     text: "Loading...",<br/>
11895     timeout: 30,<br/>
11896     scripts: false<br/>
11897 });
11898 </code></pre>
11899      * The only required property is url. The optional properties nocache, text and scripts
11900      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11901      * @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}
11902      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11903      * @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.
11904      */
11905     update : function(url, params, callback, discardUrl){
11906         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11907             var method = this.method,
11908                 cfg;
11909             if(typeof url == "object"){ // must be config object
11910                 cfg = url;
11911                 url = cfg.url;
11912                 params = params || cfg.params;
11913                 callback = callback || cfg.callback;
11914                 discardUrl = discardUrl || cfg.discardUrl;
11915                 if(callback && cfg.scope){
11916                     callback = callback.createDelegate(cfg.scope);
11917                 }
11918                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11919                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11920                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11921                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11922                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11923             }
11924             this.showLoading();
11925             if(!discardUrl){
11926                 this.defaultUrl = url;
11927             }
11928             if(typeof url == "function"){
11929                 url = url.call(this);
11930             }
11931
11932             method = method || (params ? "POST" : "GET");
11933             if(method == "GET"){
11934                 url = this.prepareUrl(url);
11935             }
11936
11937             var o = Roo.apply(cfg ||{}, {
11938                 url : url,
11939                 params: params,
11940                 success: this.successDelegate,
11941                 failure: this.failureDelegate,
11942                 callback: undefined,
11943                 timeout: (this.timeout*1000),
11944                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11945             });
11946             Roo.log("updated manager called with timeout of " + o.timeout);
11947             this.transaction = Roo.Ajax.request(o);
11948         }
11949     },
11950
11951     /**
11952      * 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.
11953      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11954      * @param {String/HTMLElement} form The form Id or form element
11955      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11956      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11957      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11958      */
11959     formUpdate : function(form, url, reset, callback){
11960         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11961             if(typeof url == "function"){
11962                 url = url.call(this);
11963             }
11964             form = Roo.getDom(form);
11965             this.transaction = Roo.Ajax.request({
11966                 form: form,
11967                 url:url,
11968                 success: this.successDelegate,
11969                 failure: this.failureDelegate,
11970                 timeout: (this.timeout*1000),
11971                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11972             });
11973             this.showLoading.defer(1, this);
11974         }
11975     },
11976
11977     /**
11978      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11979      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11980      */
11981     refresh : function(callback){
11982         if(this.defaultUrl == null){
11983             return;
11984         }
11985         this.update(this.defaultUrl, null, callback, true);
11986     },
11987
11988     /**
11989      * Set this element to auto refresh.
11990      * @param {Number} interval How often to update (in seconds).
11991      * @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)
11992      * @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}
11993      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11994      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11995      */
11996     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11997         if(refreshNow){
11998             this.update(url || this.defaultUrl, params, callback, true);
11999         }
12000         if(this.autoRefreshProcId){
12001             clearInterval(this.autoRefreshProcId);
12002         }
12003         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12004     },
12005
12006     /**
12007      * Stop auto refresh on this element.
12008      */
12009      stopAutoRefresh : function(){
12010         if(this.autoRefreshProcId){
12011             clearInterval(this.autoRefreshProcId);
12012             delete this.autoRefreshProcId;
12013         }
12014     },
12015
12016     isAutoRefreshing : function(){
12017        return this.autoRefreshProcId ? true : false;
12018     },
12019     /**
12020      * Called to update the element to "Loading" state. Override to perform custom action.
12021      */
12022     showLoading : function(){
12023         if(this.showLoadIndicator){
12024             this.el.update(this.indicatorText);
12025         }
12026     },
12027
12028     /**
12029      * Adds unique parameter to query string if disableCaching = true
12030      * @private
12031      */
12032     prepareUrl : function(url){
12033         if(this.disableCaching){
12034             var append = "_dc=" + (new Date().getTime());
12035             if(url.indexOf("?") !== -1){
12036                 url += "&" + append;
12037             }else{
12038                 url += "?" + append;
12039             }
12040         }
12041         return url;
12042     },
12043
12044     /**
12045      * @private
12046      */
12047     processSuccess : function(response){
12048         this.transaction = null;
12049         if(response.argument.form && response.argument.reset){
12050             try{ // put in try/catch since some older FF releases had problems with this
12051                 response.argument.form.reset();
12052             }catch(e){}
12053         }
12054         if(this.loadScripts){
12055             this.renderer.render(this.el, response, this,
12056                 this.updateComplete.createDelegate(this, [response]));
12057         }else{
12058             this.renderer.render(this.el, response, this);
12059             this.updateComplete(response);
12060         }
12061     },
12062
12063     updateComplete : function(response){
12064         this.fireEvent("update", this.el, response);
12065         if(typeof response.argument.callback == "function"){
12066             response.argument.callback(this.el, true, response);
12067         }
12068     },
12069
12070     /**
12071      * @private
12072      */
12073     processFailure : function(response){
12074         this.transaction = null;
12075         this.fireEvent("failure", this.el, response);
12076         if(typeof response.argument.callback == "function"){
12077             response.argument.callback(this.el, false, response);
12078         }
12079     },
12080
12081     /**
12082      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12083      * @param {Object} renderer The object implementing the render() method
12084      */
12085     setRenderer : function(renderer){
12086         this.renderer = renderer;
12087     },
12088
12089     getRenderer : function(){
12090        return this.renderer;
12091     },
12092
12093     /**
12094      * Set the defaultUrl used for updates
12095      * @param {String/Function} defaultUrl The url or a function to call to get the url
12096      */
12097     setDefaultUrl : function(defaultUrl){
12098         this.defaultUrl = defaultUrl;
12099     },
12100
12101     /**
12102      * Aborts the executing transaction
12103      */
12104     abort : function(){
12105         if(this.transaction){
12106             Roo.Ajax.abort(this.transaction);
12107         }
12108     },
12109
12110     /**
12111      * Returns true if an update is in progress
12112      * @return {Boolean}
12113      */
12114     isUpdating : function(){
12115         if(this.transaction){
12116             return Roo.Ajax.isLoading(this.transaction);
12117         }
12118         return false;
12119     }
12120 });
12121
12122 /**
12123  * @class Roo.UpdateManager.defaults
12124  * @static (not really - but it helps the doc tool)
12125  * The defaults collection enables customizing the default properties of UpdateManager
12126  */
12127    Roo.UpdateManager.defaults = {
12128        /**
12129          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12130          * @type Number
12131          */
12132          timeout : 30,
12133
12134          /**
12135          * True to process scripts by default (Defaults to false).
12136          * @type Boolean
12137          */
12138         loadScripts : false,
12139
12140         /**
12141         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12142         * @type String
12143         */
12144         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12145         /**
12146          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12147          * @type Boolean
12148          */
12149         disableCaching : false,
12150         /**
12151          * Whether to show indicatorText when loading (Defaults to true).
12152          * @type Boolean
12153          */
12154         showLoadIndicator : true,
12155         /**
12156          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12157          * @type String
12158          */
12159         indicatorText : '<div class="loading-indicator">Loading...</div>'
12160    };
12161
12162 /**
12163  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12164  *Usage:
12165  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12166  * @param {String/HTMLElement/Roo.Element} el The element to update
12167  * @param {String} url The url
12168  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12169  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12170  * @static
12171  * @deprecated
12172  * @member Roo.UpdateManager
12173  */
12174 Roo.UpdateManager.updateElement = function(el, url, params, options){
12175     var um = Roo.get(el, true).getUpdateManager();
12176     Roo.apply(um, options);
12177     um.update(url, params, options ? options.callback : null);
12178 };
12179 // alias for backwards compat
12180 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12181 /**
12182  * @class Roo.UpdateManager.BasicRenderer
12183  * Default Content renderer. Updates the elements innerHTML with the responseText.
12184  */
12185 Roo.UpdateManager.BasicRenderer = function(){};
12186
12187 Roo.UpdateManager.BasicRenderer.prototype = {
12188     /**
12189      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12190      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12191      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12192      * @param {Roo.Element} el The element being rendered
12193      * @param {Object} response The YUI Connect response object
12194      * @param {UpdateManager} updateManager The calling update manager
12195      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12196      */
12197      render : function(el, response, updateManager, callback){
12198         el.update(response.responseText, updateManager.loadScripts, callback);
12199     }
12200 };
12201 /*
12202  * Based on:
12203  * Roo JS
12204  * (c)) Alan Knowles
12205  * Licence : LGPL
12206  */
12207
12208
12209 /**
12210  * @class Roo.DomTemplate
12211  * @extends Roo.Template
12212  * An effort at a dom based template engine..
12213  *
12214  * Similar to XTemplate, except it uses dom parsing to create the template..
12215  *
12216  * Supported features:
12217  *
12218  *  Tags:
12219
12220 <pre><code>
12221       {a_variable} - output encoded.
12222       {a_variable.format:("Y-m-d")} - call a method on the variable
12223       {a_variable:raw} - unencoded output
12224       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12225       {a_variable:this.method_on_template(...)} - call a method on the template object.
12226  
12227 </code></pre>
12228  *  The tpl tag:
12229 <pre><code>
12230         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12231         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12232         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12233         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12234   
12235 </code></pre>
12236  *      
12237  */
12238 Roo.DomTemplate = function()
12239 {
12240      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12241      if (this.html) {
12242         this.compile();
12243      }
12244 };
12245
12246
12247 Roo.extend(Roo.DomTemplate, Roo.Template, {
12248     /**
12249      * id counter for sub templates.
12250      */
12251     id : 0,
12252     /**
12253      * flag to indicate if dom parser is inside a pre,
12254      * it will strip whitespace if not.
12255      */
12256     inPre : false,
12257     
12258     /**
12259      * The various sub templates
12260      */
12261     tpls : false,
12262     
12263     
12264     
12265     /**
12266      *
12267      * basic tag replacing syntax
12268      * WORD:WORD()
12269      *
12270      * // you can fake an object call by doing this
12271      *  x.t:(test,tesT) 
12272      * 
12273      */
12274     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12275     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12276     
12277     iterChild : function (node, method) {
12278         
12279         var oldPre = this.inPre;
12280         if (node.tagName == 'PRE') {
12281             this.inPre = true;
12282         }
12283         for( var i = 0; i < node.childNodes.length; i++) {
12284             method.call(this, node.childNodes[i]);
12285         }
12286         this.inPre = oldPre;
12287     },
12288     
12289     
12290     
12291     /**
12292      * compile the template
12293      *
12294      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12295      *
12296      */
12297     compile: function()
12298     {
12299         var s = this.html;
12300         
12301         // covert the html into DOM...
12302         var doc = false;
12303         var div =false;
12304         try {
12305             doc = document.implementation.createHTMLDocument("");
12306             doc.documentElement.innerHTML =   this.html  ;
12307             div = doc.documentElement;
12308         } catch (e) {
12309             // old IE... - nasty -- it causes all sorts of issues.. with
12310             // images getting pulled from server..
12311             div = document.createElement('div');
12312             div.innerHTML = this.html;
12313         }
12314         //doc.documentElement.innerHTML = htmlBody
12315          
12316         
12317         
12318         this.tpls = [];
12319         var _t = this;
12320         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12321         
12322         var tpls = this.tpls;
12323         
12324         // create a top level template from the snippet..
12325         
12326         //Roo.log(div.innerHTML);
12327         
12328         var tpl = {
12329             uid : 'master',
12330             id : this.id++,
12331             attr : false,
12332             value : false,
12333             body : div.innerHTML,
12334             
12335             forCall : false,
12336             execCall : false,
12337             dom : div,
12338             isTop : true
12339             
12340         };
12341         tpls.unshift(tpl);
12342         
12343         
12344         // compile them...
12345         this.tpls = [];
12346         Roo.each(tpls, function(tp){
12347             this.compileTpl(tp);
12348             this.tpls[tp.id] = tp;
12349         }, this);
12350         
12351         this.master = tpls[0];
12352         return this;
12353         
12354         
12355     },
12356     
12357     compileNode : function(node, istop) {
12358         // test for
12359         //Roo.log(node);
12360         
12361         
12362         // skip anything not a tag..
12363         if (node.nodeType != 1) {
12364             if (node.nodeType == 3 && !this.inPre) {
12365                 // reduce white space..
12366                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12367                 
12368             }
12369             return;
12370         }
12371         
12372         var tpl = {
12373             uid : false,
12374             id : false,
12375             attr : false,
12376             value : false,
12377             body : '',
12378             
12379             forCall : false,
12380             execCall : false,
12381             dom : false,
12382             isTop : istop
12383             
12384             
12385         };
12386         
12387         
12388         switch(true) {
12389             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12390             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12391             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12392             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12393             // no default..
12394         }
12395         
12396         
12397         if (!tpl.attr) {
12398             // just itterate children..
12399             this.iterChild(node,this.compileNode);
12400             return;
12401         }
12402         tpl.uid = this.id++;
12403         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12404         node.removeAttribute('roo-'+ tpl.attr);
12405         if (tpl.attr != 'name') {
12406             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12407             node.parentNode.replaceChild(placeholder,  node);
12408         } else {
12409             
12410             var placeholder =  document.createElement('span');
12411             placeholder.className = 'roo-tpl-' + tpl.value;
12412             node.parentNode.replaceChild(placeholder,  node);
12413         }
12414         
12415         // parent now sees '{domtplXXXX}
12416         this.iterChild(node,this.compileNode);
12417         
12418         // we should now have node body...
12419         var div = document.createElement('div');
12420         div.appendChild(node);
12421         tpl.dom = node;
12422         // this has the unfortunate side effect of converting tagged attributes
12423         // eg. href="{...}" into %7C...%7D
12424         // this has been fixed by searching for those combo's although it's a bit hacky..
12425         
12426         
12427         tpl.body = div.innerHTML;
12428         
12429         
12430          
12431         tpl.id = tpl.uid;
12432         switch(tpl.attr) {
12433             case 'for' :
12434                 switch (tpl.value) {
12435                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12436                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12437                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12438                 }
12439                 break;
12440             
12441             case 'exec':
12442                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12443                 break;
12444             
12445             case 'if':     
12446                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12447                 break;
12448             
12449             case 'name':
12450                 tpl.id  = tpl.value; // replace non characters???
12451                 break;
12452             
12453         }
12454         
12455         
12456         this.tpls.push(tpl);
12457         
12458         
12459         
12460     },
12461     
12462     
12463     
12464     
12465     /**
12466      * Compile a segment of the template into a 'sub-template'
12467      *
12468      * 
12469      * 
12470      *
12471      */
12472     compileTpl : function(tpl)
12473     {
12474         var fm = Roo.util.Format;
12475         var useF = this.disableFormats !== true;
12476         
12477         var sep = Roo.isGecko ? "+\n" : ",\n";
12478         
12479         var undef = function(str) {
12480             Roo.debug && Roo.log("Property not found :"  + str);
12481             return '';
12482         };
12483           
12484         //Roo.log(tpl.body);
12485         
12486         
12487         
12488         var fn = function(m, lbrace, name, format, args)
12489         {
12490             //Roo.log("ARGS");
12491             //Roo.log(arguments);
12492             args = args ? args.replace(/\\'/g,"'") : args;
12493             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12494             if (typeof(format) == 'undefined') {
12495                 format =  'htmlEncode'; 
12496             }
12497             if (format == 'raw' ) {
12498                 format = false;
12499             }
12500             
12501             if(name.substr(0, 6) == 'domtpl'){
12502                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12503             }
12504             
12505             // build an array of options to determine if value is undefined..
12506             
12507             // basically get 'xxxx.yyyy' then do
12508             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12509             //    (function () { Roo.log("Property not found"); return ''; })() :
12510             //    ......
12511             
12512             var udef_ar = [];
12513             var lookfor = '';
12514             Roo.each(name.split('.'), function(st) {
12515                 lookfor += (lookfor.length ? '.': '') + st;
12516                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12517             });
12518             
12519             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12520             
12521             
12522             if(format && useF){
12523                 
12524                 args = args ? ',' + args : "";
12525                  
12526                 if(format.substr(0, 5) != "this."){
12527                     format = "fm." + format + '(';
12528                 }else{
12529                     format = 'this.call("'+ format.substr(5) + '", ';
12530                     args = ", values";
12531                 }
12532                 
12533                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12534             }
12535              
12536             if (args && args.length) {
12537                 // called with xxyx.yuu:(test,test)
12538                 // change to ()
12539                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12540             }
12541             // raw.. - :raw modifier..
12542             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12543             
12544         };
12545         var body;
12546         // branched to use + in gecko and [].join() in others
12547         if(Roo.isGecko){
12548             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12549                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12550                     "';};};";
12551         }else{
12552             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12553             body.push(tpl.body.replace(/(\r\n|\n)/g,
12554                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12555             body.push("'].join('');};};");
12556             body = body.join('');
12557         }
12558         
12559         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12560        
12561         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12562         eval(body);
12563         
12564         return this;
12565     },
12566      
12567     /**
12568      * same as applyTemplate, except it's done to one of the subTemplates
12569      * when using named templates, you can do:
12570      *
12571      * var str = pl.applySubTemplate('your-name', values);
12572      *
12573      * 
12574      * @param {Number} id of the template
12575      * @param {Object} values to apply to template
12576      * @param {Object} parent (normaly the instance of this object)
12577      */
12578     applySubTemplate : function(id, values, parent)
12579     {
12580         
12581         
12582         var t = this.tpls[id];
12583         
12584         
12585         try { 
12586             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12587                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12588                 return '';
12589             }
12590         } catch(e) {
12591             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12592             Roo.log(values);
12593           
12594             return '';
12595         }
12596         try { 
12597             
12598             if(t.execCall && t.execCall.call(this, values, parent)){
12599                 return '';
12600             }
12601         } catch(e) {
12602             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12603             Roo.log(values);
12604             return '';
12605         }
12606         
12607         try {
12608             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12609             parent = t.target ? values : parent;
12610             if(t.forCall && vs instanceof Array){
12611                 var buf = [];
12612                 for(var i = 0, len = vs.length; i < len; i++){
12613                     try {
12614                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12615                     } catch (e) {
12616                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12617                         Roo.log(e.body);
12618                         //Roo.log(t.compiled);
12619                         Roo.log(vs[i]);
12620                     }   
12621                 }
12622                 return buf.join('');
12623             }
12624         } catch (e) {
12625             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12626             Roo.log(values);
12627             return '';
12628         }
12629         try {
12630             return t.compiled.call(this, vs, parent);
12631         } catch (e) {
12632             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12633             Roo.log(e.body);
12634             //Roo.log(t.compiled);
12635             Roo.log(values);
12636             return '';
12637         }
12638     },
12639
12640    
12641
12642     applyTemplate : function(values){
12643         return this.master.compiled.call(this, values, {});
12644         //var s = this.subs;
12645     },
12646
12647     apply : function(){
12648         return this.applyTemplate.apply(this, arguments);
12649     }
12650
12651  });
12652
12653 Roo.DomTemplate.from = function(el){
12654     el = Roo.getDom(el);
12655     return new Roo.Domtemplate(el.value || el.innerHTML);
12656 };/*
12657  * Based on:
12658  * Ext JS Library 1.1.1
12659  * Copyright(c) 2006-2007, Ext JS, LLC.
12660  *
12661  * Originally Released Under LGPL - original licence link has changed is not relivant.
12662  *
12663  * Fork - LGPL
12664  * <script type="text/javascript">
12665  */
12666
12667 /**
12668  * @class Roo.util.DelayedTask
12669  * Provides a convenient method of performing setTimeout where a new
12670  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12671  * You can use this class to buffer
12672  * the keypress events for a certain number of milliseconds, and perform only if they stop
12673  * for that amount of time.
12674  * @constructor The parameters to this constructor serve as defaults and are not required.
12675  * @param {Function} fn (optional) The default function to timeout
12676  * @param {Object} scope (optional) The default scope of that timeout
12677  * @param {Array} args (optional) The default Array of arguments
12678  */
12679 Roo.util.DelayedTask = function(fn, scope, args){
12680     var id = null, d, t;
12681
12682     var call = function(){
12683         var now = new Date().getTime();
12684         if(now - t >= d){
12685             clearInterval(id);
12686             id = null;
12687             fn.apply(scope, args || []);
12688         }
12689     };
12690     /**
12691      * Cancels any pending timeout and queues a new one
12692      * @param {Number} delay The milliseconds to delay
12693      * @param {Function} newFn (optional) Overrides function passed to constructor
12694      * @param {Object} newScope (optional) Overrides scope passed to constructor
12695      * @param {Array} newArgs (optional) Overrides args passed to constructor
12696      */
12697     this.delay = function(delay, newFn, newScope, newArgs){
12698         if(id && delay != d){
12699             this.cancel();
12700         }
12701         d = delay;
12702         t = new Date().getTime();
12703         fn = newFn || fn;
12704         scope = newScope || scope;
12705         args = newArgs || args;
12706         if(!id){
12707             id = setInterval(call, d);
12708         }
12709     };
12710
12711     /**
12712      * Cancel the last queued timeout
12713      */
12714     this.cancel = function(){
12715         if(id){
12716             clearInterval(id);
12717             id = null;
12718         }
12719     };
12720 };/*
12721  * Based on:
12722  * Ext JS Library 1.1.1
12723  * Copyright(c) 2006-2007, Ext JS, LLC.
12724  *
12725  * Originally Released Under LGPL - original licence link has changed is not relivant.
12726  *
12727  * Fork - LGPL
12728  * <script type="text/javascript">
12729  */
12730  
12731  
12732 Roo.util.TaskRunner = function(interval){
12733     interval = interval || 10;
12734     var tasks = [], removeQueue = [];
12735     var id = 0;
12736     var running = false;
12737
12738     var stopThread = function(){
12739         running = false;
12740         clearInterval(id);
12741         id = 0;
12742     };
12743
12744     var startThread = function(){
12745         if(!running){
12746             running = true;
12747             id = setInterval(runTasks, interval);
12748         }
12749     };
12750
12751     var removeTask = function(task){
12752         removeQueue.push(task);
12753         if(task.onStop){
12754             task.onStop();
12755         }
12756     };
12757
12758     var runTasks = function(){
12759         if(removeQueue.length > 0){
12760             for(var i = 0, len = removeQueue.length; i < len; i++){
12761                 tasks.remove(removeQueue[i]);
12762             }
12763             removeQueue = [];
12764             if(tasks.length < 1){
12765                 stopThread();
12766                 return;
12767             }
12768         }
12769         var now = new Date().getTime();
12770         for(var i = 0, len = tasks.length; i < len; ++i){
12771             var t = tasks[i];
12772             var itime = now - t.taskRunTime;
12773             if(t.interval <= itime){
12774                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12775                 t.taskRunTime = now;
12776                 if(rt === false || t.taskRunCount === t.repeat){
12777                     removeTask(t);
12778                     return;
12779                 }
12780             }
12781             if(t.duration && t.duration <= (now - t.taskStartTime)){
12782                 removeTask(t);
12783             }
12784         }
12785     };
12786
12787     /**
12788      * Queues a new task.
12789      * @param {Object} task
12790      */
12791     this.start = function(task){
12792         tasks.push(task);
12793         task.taskStartTime = new Date().getTime();
12794         task.taskRunTime = 0;
12795         task.taskRunCount = 0;
12796         startThread();
12797         return task;
12798     };
12799
12800     this.stop = function(task){
12801         removeTask(task);
12802         return task;
12803     };
12804
12805     this.stopAll = function(){
12806         stopThread();
12807         for(var i = 0, len = tasks.length; i < len; i++){
12808             if(tasks[i].onStop){
12809                 tasks[i].onStop();
12810             }
12811         }
12812         tasks = [];
12813         removeQueue = [];
12814     };
12815 };
12816
12817 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12818  * Based on:
12819  * Ext JS Library 1.1.1
12820  * Copyright(c) 2006-2007, Ext JS, LLC.
12821  *
12822  * Originally Released Under LGPL - original licence link has changed is not relivant.
12823  *
12824  * Fork - LGPL
12825  * <script type="text/javascript">
12826  */
12827
12828  
12829 /**
12830  * @class Roo.util.MixedCollection
12831  * @extends Roo.util.Observable
12832  * A Collection class that maintains both numeric indexes and keys and exposes events.
12833  * @constructor
12834  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12835  * collection (defaults to false)
12836  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12837  * and return the key value for that item.  This is used when available to look up the key on items that
12838  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12839  * equivalent to providing an implementation for the {@link #getKey} method.
12840  */
12841 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12842     this.items = [];
12843     this.map = {};
12844     this.keys = [];
12845     this.length = 0;
12846     this.addEvents({
12847         /**
12848          * @event clear
12849          * Fires when the collection is cleared.
12850          */
12851         "clear" : true,
12852         /**
12853          * @event add
12854          * Fires when an item is added to the collection.
12855          * @param {Number} index The index at which the item was added.
12856          * @param {Object} o The item added.
12857          * @param {String} key The key associated with the added item.
12858          */
12859         "add" : true,
12860         /**
12861          * @event replace
12862          * Fires when an item is replaced in the collection.
12863          * @param {String} key he key associated with the new added.
12864          * @param {Object} old The item being replaced.
12865          * @param {Object} new The new item.
12866          */
12867         "replace" : true,
12868         /**
12869          * @event remove
12870          * Fires when an item is removed from the collection.
12871          * @param {Object} o The item being removed.
12872          * @param {String} key (optional) The key associated with the removed item.
12873          */
12874         "remove" : true,
12875         "sort" : true
12876     });
12877     this.allowFunctions = allowFunctions === true;
12878     if(keyFn){
12879         this.getKey = keyFn;
12880     }
12881     Roo.util.MixedCollection.superclass.constructor.call(this);
12882 };
12883
12884 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12885     allowFunctions : false,
12886     
12887 /**
12888  * Adds an item to the collection.
12889  * @param {String} key The key to associate with the item
12890  * @param {Object} o The item to add.
12891  * @return {Object} The item added.
12892  */
12893     add : function(key, o){
12894         if(arguments.length == 1){
12895             o = arguments[0];
12896             key = this.getKey(o);
12897         }
12898         if(typeof key == "undefined" || key === null){
12899             this.length++;
12900             this.items.push(o);
12901             this.keys.push(null);
12902         }else{
12903             var old = this.map[key];
12904             if(old){
12905                 return this.replace(key, o);
12906             }
12907             this.length++;
12908             this.items.push(o);
12909             this.map[key] = o;
12910             this.keys.push(key);
12911         }
12912         this.fireEvent("add", this.length-1, o, key);
12913         return o;
12914     },
12915        
12916 /**
12917   * MixedCollection has a generic way to fetch keys if you implement getKey.
12918 <pre><code>
12919 // normal way
12920 var mc = new Roo.util.MixedCollection();
12921 mc.add(someEl.dom.id, someEl);
12922 mc.add(otherEl.dom.id, otherEl);
12923 //and so on
12924
12925 // using getKey
12926 var mc = new Roo.util.MixedCollection();
12927 mc.getKey = function(el){
12928    return el.dom.id;
12929 };
12930 mc.add(someEl);
12931 mc.add(otherEl);
12932
12933 // or via the constructor
12934 var mc = new Roo.util.MixedCollection(false, function(el){
12935    return el.dom.id;
12936 });
12937 mc.add(someEl);
12938 mc.add(otherEl);
12939 </code></pre>
12940  * @param o {Object} The item for which to find the key.
12941  * @return {Object} The key for the passed item.
12942  */
12943     getKey : function(o){
12944          return o.id; 
12945     },
12946    
12947 /**
12948  * Replaces an item in the collection.
12949  * @param {String} key The key associated with the item to replace, or the item to replace.
12950  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12951  * @return {Object}  The new item.
12952  */
12953     replace : function(key, o){
12954         if(arguments.length == 1){
12955             o = arguments[0];
12956             key = this.getKey(o);
12957         }
12958         var old = this.item(key);
12959         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12960              return this.add(key, o);
12961         }
12962         var index = this.indexOfKey(key);
12963         this.items[index] = o;
12964         this.map[key] = o;
12965         this.fireEvent("replace", key, old, o);
12966         return o;
12967     },
12968    
12969 /**
12970  * Adds all elements of an Array or an Object to the collection.
12971  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12972  * an Array of values, each of which are added to the collection.
12973  */
12974     addAll : function(objs){
12975         if(arguments.length > 1 || objs instanceof Array){
12976             var args = arguments.length > 1 ? arguments : objs;
12977             for(var i = 0, len = args.length; i < len; i++){
12978                 this.add(args[i]);
12979             }
12980         }else{
12981             for(var key in objs){
12982                 if(this.allowFunctions || typeof objs[key] != "function"){
12983                     this.add(key, objs[key]);
12984                 }
12985             }
12986         }
12987     },
12988    
12989 /**
12990  * Executes the specified function once for every item in the collection, passing each
12991  * item as the first and only parameter. returning false from the function will stop the iteration.
12992  * @param {Function} fn The function to execute for each item.
12993  * @param {Object} scope (optional) The scope in which to execute the function.
12994  */
12995     each : function(fn, scope){
12996         var items = [].concat(this.items); // each safe for removal
12997         for(var i = 0, len = items.length; i < len; i++){
12998             if(fn.call(scope || items[i], items[i], i, len) === false){
12999                 break;
13000             }
13001         }
13002     },
13003    
13004 /**
13005  * Executes the specified function once for every key in the collection, passing each
13006  * key, and its associated item as the first two parameters.
13007  * @param {Function} fn The function to execute for each item.
13008  * @param {Object} scope (optional) The scope in which to execute the function.
13009  */
13010     eachKey : function(fn, scope){
13011         for(var i = 0, len = this.keys.length; i < len; i++){
13012             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13013         }
13014     },
13015    
13016 /**
13017  * Returns the first item in the collection which elicits a true return value from the
13018  * passed selection function.
13019  * @param {Function} fn The selection function to execute for each item.
13020  * @param {Object} scope (optional) The scope in which to execute the function.
13021  * @return {Object} The first item in the collection which returned true from the selection function.
13022  */
13023     find : function(fn, scope){
13024         for(var i = 0, len = this.items.length; i < len; i++){
13025             if(fn.call(scope || window, this.items[i], this.keys[i])){
13026                 return this.items[i];
13027             }
13028         }
13029         return null;
13030     },
13031    
13032 /**
13033  * Inserts an item at the specified index in the collection.
13034  * @param {Number} index The index to insert the item at.
13035  * @param {String} key The key to associate with the new item, or the item itself.
13036  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13037  * @return {Object} The item inserted.
13038  */
13039     insert : function(index, key, o){
13040         if(arguments.length == 2){
13041             o = arguments[1];
13042             key = this.getKey(o);
13043         }
13044         if(index >= this.length){
13045             return this.add(key, o);
13046         }
13047         this.length++;
13048         this.items.splice(index, 0, o);
13049         if(typeof key != "undefined" && key != null){
13050             this.map[key] = o;
13051         }
13052         this.keys.splice(index, 0, key);
13053         this.fireEvent("add", index, o, key);
13054         return o;
13055     },
13056    
13057 /**
13058  * Removed an item from the collection.
13059  * @param {Object} o The item to remove.
13060  * @return {Object} The item removed.
13061  */
13062     remove : function(o){
13063         return this.removeAt(this.indexOf(o));
13064     },
13065    
13066 /**
13067  * Remove an item from a specified index in the collection.
13068  * @param {Number} index The index within the collection of the item to remove.
13069  */
13070     removeAt : function(index){
13071         if(index < this.length && index >= 0){
13072             this.length--;
13073             var o = this.items[index];
13074             this.items.splice(index, 1);
13075             var key = this.keys[index];
13076             if(typeof key != "undefined"){
13077                 delete this.map[key];
13078             }
13079             this.keys.splice(index, 1);
13080             this.fireEvent("remove", o, key);
13081         }
13082     },
13083    
13084 /**
13085  * Removed an item associated with the passed key fom the collection.
13086  * @param {String} key The key of the item to remove.
13087  */
13088     removeKey : function(key){
13089         return this.removeAt(this.indexOfKey(key));
13090     },
13091    
13092 /**
13093  * Returns the number of items in the collection.
13094  * @return {Number} the number of items in the collection.
13095  */
13096     getCount : function(){
13097         return this.length; 
13098     },
13099    
13100 /**
13101  * Returns index within the collection of the passed Object.
13102  * @param {Object} o The item to find the index of.
13103  * @return {Number} index of the item.
13104  */
13105     indexOf : function(o){
13106         if(!this.items.indexOf){
13107             for(var i = 0, len = this.items.length; i < len; i++){
13108                 if(this.items[i] == o) return i;
13109             }
13110             return -1;
13111         }else{
13112             return this.items.indexOf(o);
13113         }
13114     },
13115    
13116 /**
13117  * Returns index within the collection of the passed key.
13118  * @param {String} key The key to find the index of.
13119  * @return {Number} index of the key.
13120  */
13121     indexOfKey : function(key){
13122         if(!this.keys.indexOf){
13123             for(var i = 0, len = this.keys.length; i < len; i++){
13124                 if(this.keys[i] == key) return i;
13125             }
13126             return -1;
13127         }else{
13128             return this.keys.indexOf(key);
13129         }
13130     },
13131    
13132 /**
13133  * Returns the item associated with the passed key OR index. Key has priority over index.
13134  * @param {String/Number} key The key or index of the item.
13135  * @return {Object} The item associated with the passed key.
13136  */
13137     item : function(key){
13138         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13139         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13140     },
13141     
13142 /**
13143  * Returns the item at the specified index.
13144  * @param {Number} index The index of the item.
13145  * @return {Object}
13146  */
13147     itemAt : function(index){
13148         return this.items[index];
13149     },
13150     
13151 /**
13152  * Returns the item associated with the passed key.
13153  * @param {String/Number} key The key of the item.
13154  * @return {Object} The item associated with the passed key.
13155  */
13156     key : function(key){
13157         return this.map[key];
13158     },
13159    
13160 /**
13161  * Returns true if the collection contains the passed Object as an item.
13162  * @param {Object} o  The Object to look for in the collection.
13163  * @return {Boolean} True if the collection contains the Object as an item.
13164  */
13165     contains : function(o){
13166         return this.indexOf(o) != -1;
13167     },
13168    
13169 /**
13170  * Returns true if the collection contains the passed Object as a key.
13171  * @param {String} key The key to look for in the collection.
13172  * @return {Boolean} True if the collection contains the Object as a key.
13173  */
13174     containsKey : function(key){
13175         return typeof this.map[key] != "undefined";
13176     },
13177    
13178 /**
13179  * Removes all items from the collection.
13180  */
13181     clear : function(){
13182         this.length = 0;
13183         this.items = [];
13184         this.keys = [];
13185         this.map = {};
13186         this.fireEvent("clear");
13187     },
13188    
13189 /**
13190  * Returns the first item in the collection.
13191  * @return {Object} the first item in the collection..
13192  */
13193     first : function(){
13194         return this.items[0]; 
13195     },
13196    
13197 /**
13198  * Returns the last item in the collection.
13199  * @return {Object} the last item in the collection..
13200  */
13201     last : function(){
13202         return this.items[this.length-1];   
13203     },
13204     
13205     _sort : function(property, dir, fn){
13206         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13207         fn = fn || function(a, b){
13208             return a-b;
13209         };
13210         var c = [], k = this.keys, items = this.items;
13211         for(var i = 0, len = items.length; i < len; i++){
13212             c[c.length] = {key: k[i], value: items[i], index: i};
13213         }
13214         c.sort(function(a, b){
13215             var v = fn(a[property], b[property]) * dsc;
13216             if(v == 0){
13217                 v = (a.index < b.index ? -1 : 1);
13218             }
13219             return v;
13220         });
13221         for(var i = 0, len = c.length; i < len; i++){
13222             items[i] = c[i].value;
13223             k[i] = c[i].key;
13224         }
13225         this.fireEvent("sort", this);
13226     },
13227     
13228     /**
13229      * Sorts this collection with the passed comparison function
13230      * @param {String} direction (optional) "ASC" or "DESC"
13231      * @param {Function} fn (optional) comparison function
13232      */
13233     sort : function(dir, fn){
13234         this._sort("value", dir, fn);
13235     },
13236     
13237     /**
13238      * Sorts this collection by keys
13239      * @param {String} direction (optional) "ASC" or "DESC"
13240      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13241      */
13242     keySort : function(dir, fn){
13243         this._sort("key", dir, fn || function(a, b){
13244             return String(a).toUpperCase()-String(b).toUpperCase();
13245         });
13246     },
13247     
13248     /**
13249      * Returns a range of items in this collection
13250      * @param {Number} startIndex (optional) defaults to 0
13251      * @param {Number} endIndex (optional) default to the last item
13252      * @return {Array} An array of items
13253      */
13254     getRange : function(start, end){
13255         var items = this.items;
13256         if(items.length < 1){
13257             return [];
13258         }
13259         start = start || 0;
13260         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13261         var r = [];
13262         if(start <= end){
13263             for(var i = start; i <= end; i++) {
13264                     r[r.length] = items[i];
13265             }
13266         }else{
13267             for(var i = start; i >= end; i--) {
13268                     r[r.length] = items[i];
13269             }
13270         }
13271         return r;
13272     },
13273         
13274     /**
13275      * Filter the <i>objects</i> in this collection by a specific property. 
13276      * Returns a new collection that has been filtered.
13277      * @param {String} property A property on your objects
13278      * @param {String/RegExp} value Either string that the property values 
13279      * should start with or a RegExp to test against the property
13280      * @return {MixedCollection} The new filtered collection
13281      */
13282     filter : function(property, value){
13283         if(!value.exec){ // not a regex
13284             value = String(value);
13285             if(value.length == 0){
13286                 return this.clone();
13287             }
13288             value = new RegExp("^" + Roo.escapeRe(value), "i");
13289         }
13290         return this.filterBy(function(o){
13291             return o && value.test(o[property]);
13292         });
13293         },
13294     
13295     /**
13296      * Filter by a function. * Returns a new collection that has been filtered.
13297      * The passed function will be called with each 
13298      * object in the collection. If the function returns true, the value is included 
13299      * otherwise it is filtered.
13300      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13301      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13302      * @return {MixedCollection} The new filtered collection
13303      */
13304     filterBy : function(fn, scope){
13305         var r = new Roo.util.MixedCollection();
13306         r.getKey = this.getKey;
13307         var k = this.keys, it = this.items;
13308         for(var i = 0, len = it.length; i < len; i++){
13309             if(fn.call(scope||this, it[i], k[i])){
13310                                 r.add(k[i], it[i]);
13311                         }
13312         }
13313         return r;
13314     },
13315     
13316     /**
13317      * Creates a duplicate of this collection
13318      * @return {MixedCollection}
13319      */
13320     clone : function(){
13321         var r = new Roo.util.MixedCollection();
13322         var k = this.keys, it = this.items;
13323         for(var i = 0, len = it.length; i < len; i++){
13324             r.add(k[i], it[i]);
13325         }
13326         r.getKey = this.getKey;
13327         return r;
13328     }
13329 });
13330 /**
13331  * Returns the item associated with the passed key or index.
13332  * @method
13333  * @param {String/Number} key The key or index of the item.
13334  * @return {Object} The item associated with the passed key.
13335  */
13336 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13337  * Based on:
13338  * Ext JS Library 1.1.1
13339  * Copyright(c) 2006-2007, Ext JS, LLC.
13340  *
13341  * Originally Released Under LGPL - original licence link has changed is not relivant.
13342  *
13343  * Fork - LGPL
13344  * <script type="text/javascript">
13345  */
13346 /**
13347  * @class Roo.util.JSON
13348  * Modified version of Douglas Crockford"s json.js that doesn"t
13349  * mess with the Object prototype 
13350  * http://www.json.org/js.html
13351  * @singleton
13352  */
13353 Roo.util.JSON = new (function(){
13354     var useHasOwn = {}.hasOwnProperty ? true : false;
13355     
13356     // crashes Safari in some instances
13357     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13358     
13359     var pad = function(n) {
13360         return n < 10 ? "0" + n : n;
13361     };
13362     
13363     var m = {
13364         "\b": '\\b',
13365         "\t": '\\t',
13366         "\n": '\\n',
13367         "\f": '\\f',
13368         "\r": '\\r',
13369         '"' : '\\"',
13370         "\\": '\\\\'
13371     };
13372
13373     var encodeString = function(s){
13374         if (/["\\\x00-\x1f]/.test(s)) {
13375             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13376                 var c = m[b];
13377                 if(c){
13378                     return c;
13379                 }
13380                 c = b.charCodeAt();
13381                 return "\\u00" +
13382                     Math.floor(c / 16).toString(16) +
13383                     (c % 16).toString(16);
13384             }) + '"';
13385         }
13386         return '"' + s + '"';
13387     };
13388     
13389     var encodeArray = function(o){
13390         var a = ["["], b, i, l = o.length, v;
13391             for (i = 0; i < l; i += 1) {
13392                 v = o[i];
13393                 switch (typeof v) {
13394                     case "undefined":
13395                     case "function":
13396                     case "unknown":
13397                         break;
13398                     default:
13399                         if (b) {
13400                             a.push(',');
13401                         }
13402                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13403                         b = true;
13404                 }
13405             }
13406             a.push("]");
13407             return a.join("");
13408     };
13409     
13410     var encodeDate = function(o){
13411         return '"' + o.getFullYear() + "-" +
13412                 pad(o.getMonth() + 1) + "-" +
13413                 pad(o.getDate()) + "T" +
13414                 pad(o.getHours()) + ":" +
13415                 pad(o.getMinutes()) + ":" +
13416                 pad(o.getSeconds()) + '"';
13417     };
13418     
13419     /**
13420      * Encodes an Object, Array or other value
13421      * @param {Mixed} o The variable to encode
13422      * @return {String} The JSON string
13423      */
13424     this.encode = function(o)
13425     {
13426         // should this be extended to fully wrap stringify..
13427         
13428         if(typeof o == "undefined" || o === null){
13429             return "null";
13430         }else if(o instanceof Array){
13431             return encodeArray(o);
13432         }else if(o instanceof Date){
13433             return encodeDate(o);
13434         }else if(typeof o == "string"){
13435             return encodeString(o);
13436         }else if(typeof o == "number"){
13437             return isFinite(o) ? String(o) : "null";
13438         }else if(typeof o == "boolean"){
13439             return String(o);
13440         }else {
13441             var a = ["{"], b, i, v;
13442             for (i in o) {
13443                 if(!useHasOwn || o.hasOwnProperty(i)) {
13444                     v = o[i];
13445                     switch (typeof v) {
13446                     case "undefined":
13447                     case "function":
13448                     case "unknown":
13449                         break;
13450                     default:
13451                         if(b){
13452                             a.push(',');
13453                         }
13454                         a.push(this.encode(i), ":",
13455                                 v === null ? "null" : this.encode(v));
13456                         b = true;
13457                     }
13458                 }
13459             }
13460             a.push("}");
13461             return a.join("");
13462         }
13463     };
13464     
13465     /**
13466      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13467      * @param {String} json The JSON string
13468      * @return {Object} The resulting object
13469      */
13470     this.decode = function(json){
13471         
13472         return  /** eval:var:json */ eval("(" + json + ')');
13473     };
13474 })();
13475 /** 
13476  * Shorthand for {@link Roo.util.JSON#encode}
13477  * @member Roo encode 
13478  * @method */
13479 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13480 /** 
13481  * Shorthand for {@link Roo.util.JSON#decode}
13482  * @member Roo decode 
13483  * @method */
13484 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13485 /*
13486  * Based on:
13487  * Ext JS Library 1.1.1
13488  * Copyright(c) 2006-2007, Ext JS, LLC.
13489  *
13490  * Originally Released Under LGPL - original licence link has changed is not relivant.
13491  *
13492  * Fork - LGPL
13493  * <script type="text/javascript">
13494  */
13495  
13496 /**
13497  * @class Roo.util.Format
13498  * Reusable data formatting functions
13499  * @singleton
13500  */
13501 Roo.util.Format = function(){
13502     var trimRe = /^\s+|\s+$/g;
13503     return {
13504         /**
13505          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13506          * @param {String} value The string to truncate
13507          * @param {Number} length The maximum length to allow before truncating
13508          * @return {String} The converted text
13509          */
13510         ellipsis : function(value, len){
13511             if(value && value.length > len){
13512                 return value.substr(0, len-3)+"...";
13513             }
13514             return value;
13515         },
13516
13517         /**
13518          * Checks a reference and converts it to empty string if it is undefined
13519          * @param {Mixed} value Reference to check
13520          * @return {Mixed} Empty string if converted, otherwise the original value
13521          */
13522         undef : function(value){
13523             return typeof value != "undefined" ? value : "";
13524         },
13525
13526         /**
13527          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13528          * @param {String} value The string to encode
13529          * @return {String} The encoded text
13530          */
13531         htmlEncode : function(value){
13532             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13533         },
13534
13535         /**
13536          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13537          * @param {String} value The string to decode
13538          * @return {String} The decoded text
13539          */
13540         htmlDecode : function(value){
13541             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13542         },
13543
13544         /**
13545          * Trims any whitespace from either side of a string
13546          * @param {String} value The text to trim
13547          * @return {String} The trimmed text
13548          */
13549         trim : function(value){
13550             return String(value).replace(trimRe, "");
13551         },
13552
13553         /**
13554          * Returns a substring from within an original string
13555          * @param {String} value The original text
13556          * @param {Number} start The start index of the substring
13557          * @param {Number} length The length of the substring
13558          * @return {String} The substring
13559          */
13560         substr : function(value, start, length){
13561             return String(value).substr(start, length);
13562         },
13563
13564         /**
13565          * Converts a string to all lower case letters
13566          * @param {String} value The text to convert
13567          * @return {String} The converted text
13568          */
13569         lowercase : function(value){
13570             return String(value).toLowerCase();
13571         },
13572
13573         /**
13574          * Converts a string to all upper case letters
13575          * @param {String} value The text to convert
13576          * @return {String} The converted text
13577          */
13578         uppercase : function(value){
13579             return String(value).toUpperCase();
13580         },
13581
13582         /**
13583          * Converts the first character only of a string to upper case
13584          * @param {String} value The text to convert
13585          * @return {String} The converted text
13586          */
13587         capitalize : function(value){
13588             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13589         },
13590
13591         // private
13592         call : function(value, fn){
13593             if(arguments.length > 2){
13594                 var args = Array.prototype.slice.call(arguments, 2);
13595                 args.unshift(value);
13596                  
13597                 return /** eval:var:value */  eval(fn).apply(window, args);
13598             }else{
13599                 /** eval:var:value */
13600                 return /** eval:var:value */ eval(fn).call(window, value);
13601             }
13602         },
13603
13604        
13605         /**
13606          * safer version of Math.toFixed..??/
13607          * @param {Number/String} value The numeric value to format
13608          * @param {Number/String} value Decimal places 
13609          * @return {String} The formatted currency string
13610          */
13611         toFixed : function(v, n)
13612         {
13613             // why not use to fixed - precision is buggered???
13614             if (!n) {
13615                 return Math.round(v-0);
13616             }
13617             var fact = Math.pow(10,n+1);
13618             v = (Math.round((v-0)*fact))/fact;
13619             var z = (''+fact).substring(2);
13620             if (v == Math.floor(v)) {
13621                 return Math.floor(v) + '.' + z;
13622             }
13623             
13624             // now just padd decimals..
13625             var ps = String(v).split('.');
13626             var fd = (ps[1] + z);
13627             var r = fd.substring(0,n); 
13628             var rm = fd.substring(n); 
13629             if (rm < 5) {
13630                 return ps[0] + '.' + r;
13631             }
13632             r*=1; // turn it into a number;
13633             r++;
13634             if (String(r).length != n) {
13635                 ps[0]*=1;
13636                 ps[0]++;
13637                 r = String(r).substring(1); // chop the end off.
13638             }
13639             
13640             return ps[0] + '.' + r;
13641              
13642         },
13643         
13644         /**
13645          * Format a number as US currency
13646          * @param {Number/String} value The numeric value to format
13647          * @return {String} The formatted currency string
13648          */
13649         usMoney : function(v){
13650             return '$' + Roo.util.Format.number(v);
13651         },
13652         
13653         /**
13654          * Format a number
13655          * eventually this should probably emulate php's number_format
13656          * @param {Number/String} value The numeric value to format
13657          * @param {Number} decimals number of decimal places
13658          * @return {String} The formatted currency string
13659          */
13660         number : function(v,decimals)
13661         {
13662             // multiply and round.
13663             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13664             var mul = Math.pow(10, decimals);
13665             var zero = String(mul).substring(1);
13666             v = (Math.round((v-0)*mul))/mul;
13667             
13668             // if it's '0' number.. then
13669             
13670             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13671             v = String(v);
13672             var ps = v.split('.');
13673             var whole = ps[0];
13674             
13675             
13676             var r = /(\d+)(\d{3})/;
13677             // add comma's
13678             while (r.test(whole)) {
13679                 whole = whole.replace(r, '$1' + ',' + '$2');
13680             }
13681             
13682             
13683             var sub = ps[1] ?
13684                     // has decimals..
13685                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13686                     // does not have decimals
13687                     (decimals ? ('.' + zero) : '');
13688             
13689             
13690             return whole + sub ;
13691         },
13692         
13693         /**
13694          * Parse a value into a formatted date using the specified format pattern.
13695          * @param {Mixed} value The value to format
13696          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13697          * @return {String} The formatted date string
13698          */
13699         date : function(v, format){
13700             if(!v){
13701                 return "";
13702             }
13703             if(!(v instanceof Date)){
13704                 v = new Date(Date.parse(v));
13705             }
13706             return v.dateFormat(format || Roo.util.Format.defaults.date);
13707         },
13708
13709         /**
13710          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13711          * @param {String} format Any valid date format string
13712          * @return {Function} The date formatting function
13713          */
13714         dateRenderer : function(format){
13715             return function(v){
13716                 return Roo.util.Format.date(v, format);  
13717             };
13718         },
13719
13720         // private
13721         stripTagsRE : /<\/?[^>]+>/gi,
13722         
13723         /**
13724          * Strips all HTML tags
13725          * @param {Mixed} value The text from which to strip tags
13726          * @return {String} The stripped text
13727          */
13728         stripTags : function(v){
13729             return !v ? v : String(v).replace(this.stripTagsRE, "");
13730         }
13731     };
13732 }();
13733 Roo.util.Format.defaults = {
13734     date : 'd/M/Y'
13735 };/*
13736  * Based on:
13737  * Ext JS Library 1.1.1
13738  * Copyright(c) 2006-2007, Ext JS, LLC.
13739  *
13740  * Originally Released Under LGPL - original licence link has changed is not relivant.
13741  *
13742  * Fork - LGPL
13743  * <script type="text/javascript">
13744  */
13745
13746
13747  
13748
13749 /**
13750  * @class Roo.MasterTemplate
13751  * @extends Roo.Template
13752  * Provides a template that can have child templates. The syntax is:
13753 <pre><code>
13754 var t = new Roo.MasterTemplate(
13755         '&lt;select name="{name}"&gt;',
13756                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13757         '&lt;/select&gt;'
13758 );
13759 t.add('options', {value: 'foo', text: 'bar'});
13760 // or you can add multiple child elements in one shot
13761 t.addAll('options', [
13762     {value: 'foo', text: 'bar'},
13763     {value: 'foo2', text: 'bar2'},
13764     {value: 'foo3', text: 'bar3'}
13765 ]);
13766 // then append, applying the master template values
13767 t.append('my-form', {name: 'my-select'});
13768 </code></pre>
13769 * A name attribute for the child template is not required if you have only one child
13770 * template or you want to refer to them by index.
13771  */
13772 Roo.MasterTemplate = function(){
13773     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13774     this.originalHtml = this.html;
13775     var st = {};
13776     var m, re = this.subTemplateRe;
13777     re.lastIndex = 0;
13778     var subIndex = 0;
13779     while(m = re.exec(this.html)){
13780         var name = m[1], content = m[2];
13781         st[subIndex] = {
13782             name: name,
13783             index: subIndex,
13784             buffer: [],
13785             tpl : new Roo.Template(content)
13786         };
13787         if(name){
13788             st[name] = st[subIndex];
13789         }
13790         st[subIndex].tpl.compile();
13791         st[subIndex].tpl.call = this.call.createDelegate(this);
13792         subIndex++;
13793     }
13794     this.subCount = subIndex;
13795     this.subs = st;
13796 };
13797 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13798     /**
13799     * The regular expression used to match sub templates
13800     * @type RegExp
13801     * @property
13802     */
13803     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13804
13805     /**
13806      * Applies the passed values to a child template.
13807      * @param {String/Number} name (optional) The name or index of the child template
13808      * @param {Array/Object} values The values to be applied to the template
13809      * @return {MasterTemplate} this
13810      */
13811      add : function(name, values){
13812         if(arguments.length == 1){
13813             values = arguments[0];
13814             name = 0;
13815         }
13816         var s = this.subs[name];
13817         s.buffer[s.buffer.length] = s.tpl.apply(values);
13818         return this;
13819     },
13820
13821     /**
13822      * Applies all the passed values to a child template.
13823      * @param {String/Number} name (optional) The name or index of the child template
13824      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13825      * @param {Boolean} reset (optional) True to reset the template first
13826      * @return {MasterTemplate} this
13827      */
13828     fill : function(name, values, reset){
13829         var a = arguments;
13830         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13831             values = a[0];
13832             name = 0;
13833             reset = a[1];
13834         }
13835         if(reset){
13836             this.reset();
13837         }
13838         for(var i = 0, len = values.length; i < len; i++){
13839             this.add(name, values[i]);
13840         }
13841         return this;
13842     },
13843
13844     /**
13845      * Resets the template for reuse
13846      * @return {MasterTemplate} this
13847      */
13848      reset : function(){
13849         var s = this.subs;
13850         for(var i = 0; i < this.subCount; i++){
13851             s[i].buffer = [];
13852         }
13853         return this;
13854     },
13855
13856     applyTemplate : function(values){
13857         var s = this.subs;
13858         var replaceIndex = -1;
13859         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13860             return s[++replaceIndex].buffer.join("");
13861         });
13862         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13863     },
13864
13865     apply : function(){
13866         return this.applyTemplate.apply(this, arguments);
13867     },
13868
13869     compile : function(){return this;}
13870 });
13871
13872 /**
13873  * Alias for fill().
13874  * @method
13875  */
13876 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13877  /**
13878  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13879  * var tpl = Roo.MasterTemplate.from('element-id');
13880  * @param {String/HTMLElement} el
13881  * @param {Object} config
13882  * @static
13883  */
13884 Roo.MasterTemplate.from = function(el, config){
13885     el = Roo.getDom(el);
13886     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13887 };/*
13888  * Based on:
13889  * Ext JS Library 1.1.1
13890  * Copyright(c) 2006-2007, Ext JS, LLC.
13891  *
13892  * Originally Released Under LGPL - original licence link has changed is not relivant.
13893  *
13894  * Fork - LGPL
13895  * <script type="text/javascript">
13896  */
13897
13898  
13899 /**
13900  * @class Roo.util.CSS
13901  * Utility class for manipulating CSS rules
13902  * @singleton
13903  */
13904 Roo.util.CSS = function(){
13905         var rules = null;
13906         var doc = document;
13907
13908     var camelRe = /(-[a-z])/gi;
13909     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13910
13911    return {
13912    /**
13913     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13914     * tag and appended to the HEAD of the document.
13915     * @param {String|Object} cssText The text containing the css rules
13916     * @param {String} id An id to add to the stylesheet for later removal
13917     * @return {StyleSheet}
13918     */
13919     createStyleSheet : function(cssText, id){
13920         var ss;
13921         var head = doc.getElementsByTagName("head")[0];
13922         var nrules = doc.createElement("style");
13923         nrules.setAttribute("type", "text/css");
13924         if(id){
13925             nrules.setAttribute("id", id);
13926         }
13927         if (typeof(cssText) != 'string') {
13928             // support object maps..
13929             // not sure if this a good idea.. 
13930             // perhaps it should be merged with the general css handling
13931             // and handle js style props.
13932             var cssTextNew = [];
13933             for(var n in cssText) {
13934                 var citems = [];
13935                 for(var k in cssText[n]) {
13936                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13937                 }
13938                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13939                 
13940             }
13941             cssText = cssTextNew.join("\n");
13942             
13943         }
13944        
13945        
13946        if(Roo.isIE){
13947            head.appendChild(nrules);
13948            ss = nrules.styleSheet;
13949            ss.cssText = cssText;
13950        }else{
13951            try{
13952                 nrules.appendChild(doc.createTextNode(cssText));
13953            }catch(e){
13954                nrules.cssText = cssText; 
13955            }
13956            head.appendChild(nrules);
13957            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13958        }
13959        this.cacheStyleSheet(ss);
13960        return ss;
13961    },
13962
13963    /**
13964     * Removes a style or link tag by id
13965     * @param {String} id The id of the tag
13966     */
13967    removeStyleSheet : function(id){
13968        var existing = doc.getElementById(id);
13969        if(existing){
13970            existing.parentNode.removeChild(existing);
13971        }
13972    },
13973
13974    /**
13975     * Dynamically swaps an existing stylesheet reference for a new one
13976     * @param {String} id The id of an existing link tag to remove
13977     * @param {String} url The href of the new stylesheet to include
13978     */
13979    swapStyleSheet : function(id, url){
13980        this.removeStyleSheet(id);
13981        var ss = doc.createElement("link");
13982        ss.setAttribute("rel", "stylesheet");
13983        ss.setAttribute("type", "text/css");
13984        ss.setAttribute("id", id);
13985        ss.setAttribute("href", url);
13986        doc.getElementsByTagName("head")[0].appendChild(ss);
13987    },
13988    
13989    /**
13990     * Refresh the rule cache if you have dynamically added stylesheets
13991     * @return {Object} An object (hash) of rules indexed by selector
13992     */
13993    refreshCache : function(){
13994        return this.getRules(true);
13995    },
13996
13997    // private
13998    cacheStyleSheet : function(stylesheet){
13999        if(!rules){
14000            rules = {};
14001        }
14002        try{// try catch for cross domain access issue
14003            var ssRules = stylesheet.cssRules || stylesheet.rules;
14004            for(var j = ssRules.length-1; j >= 0; --j){
14005                rules[ssRules[j].selectorText] = ssRules[j];
14006            }
14007        }catch(e){}
14008    },
14009    
14010    /**
14011     * Gets all css rules for the document
14012     * @param {Boolean} refreshCache true to refresh the internal cache
14013     * @return {Object} An object (hash) of rules indexed by selector
14014     */
14015    getRules : function(refreshCache){
14016                 if(rules == null || refreshCache){
14017                         rules = {};
14018                         var ds = doc.styleSheets;
14019                         for(var i =0, len = ds.length; i < len; i++){
14020                             try{
14021                         this.cacheStyleSheet(ds[i]);
14022                     }catch(e){} 
14023                 }
14024                 }
14025                 return rules;
14026         },
14027         
14028         /**
14029     * Gets an an individual CSS rule by selector(s)
14030     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14031     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14032     * @return {CSSRule} The CSS rule or null if one is not found
14033     */
14034    getRule : function(selector, refreshCache){
14035                 var rs = this.getRules(refreshCache);
14036                 if(!(selector instanceof Array)){
14037                     return rs[selector];
14038                 }
14039                 for(var i = 0; i < selector.length; i++){
14040                         if(rs[selector[i]]){
14041                                 return rs[selector[i]];
14042                         }
14043                 }
14044                 return null;
14045         },
14046         
14047         
14048         /**
14049     * Updates a rule property
14050     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14051     * @param {String} property The css property
14052     * @param {String} value The new value for the property
14053     * @return {Boolean} true If a rule was found and updated
14054     */
14055    updateRule : function(selector, property, value){
14056                 if(!(selector instanceof Array)){
14057                         var rule = this.getRule(selector);
14058                         if(rule){
14059                                 rule.style[property.replace(camelRe, camelFn)] = value;
14060                                 return true;
14061                         }
14062                 }else{
14063                         for(var i = 0; i < selector.length; i++){
14064                                 if(this.updateRule(selector[i], property, value)){
14065                                         return true;
14066                                 }
14067                         }
14068                 }
14069                 return false;
14070         }
14071    };   
14072 }();/*
14073  * Based on:
14074  * Ext JS Library 1.1.1
14075  * Copyright(c) 2006-2007, Ext JS, LLC.
14076  *
14077  * Originally Released Under LGPL - original licence link has changed is not relivant.
14078  *
14079  * Fork - LGPL
14080  * <script type="text/javascript">
14081  */
14082
14083  
14084
14085 /**
14086  * @class Roo.util.ClickRepeater
14087  * @extends Roo.util.Observable
14088  * 
14089  * A wrapper class which can be applied to any element. Fires a "click" event while the
14090  * mouse is pressed. The interval between firings may be specified in the config but
14091  * defaults to 10 milliseconds.
14092  * 
14093  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14094  * 
14095  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14096  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14097  * Similar to an autorepeat key delay.
14098  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14099  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14100  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14101  *           "interval" and "delay" are ignored. "immediate" is honored.
14102  * @cfg {Boolean} preventDefault True to prevent the default click event
14103  * @cfg {Boolean} stopDefault True to stop the default click event
14104  * 
14105  * @history
14106  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14107  *     2007-02-02 jvs Renamed to ClickRepeater
14108  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14109  *
14110  *  @constructor
14111  * @param {String/HTMLElement/Element} el The element to listen on
14112  * @param {Object} config
14113  **/
14114 Roo.util.ClickRepeater = function(el, config)
14115 {
14116     this.el = Roo.get(el);
14117     this.el.unselectable();
14118
14119     Roo.apply(this, config);
14120
14121     this.addEvents({
14122     /**
14123      * @event mousedown
14124      * Fires when the mouse button is depressed.
14125      * @param {Roo.util.ClickRepeater} this
14126      */
14127         "mousedown" : true,
14128     /**
14129      * @event click
14130      * Fires on a specified interval during the time the element is pressed.
14131      * @param {Roo.util.ClickRepeater} this
14132      */
14133         "click" : true,
14134     /**
14135      * @event mouseup
14136      * Fires when the mouse key is released.
14137      * @param {Roo.util.ClickRepeater} this
14138      */
14139         "mouseup" : true
14140     });
14141
14142     this.el.on("mousedown", this.handleMouseDown, this);
14143     if(this.preventDefault || this.stopDefault){
14144         this.el.on("click", function(e){
14145             if(this.preventDefault){
14146                 e.preventDefault();
14147             }
14148             if(this.stopDefault){
14149                 e.stopEvent();
14150             }
14151         }, this);
14152     }
14153
14154     // allow inline handler
14155     if(this.handler){
14156         this.on("click", this.handler,  this.scope || this);
14157     }
14158
14159     Roo.util.ClickRepeater.superclass.constructor.call(this);
14160 };
14161
14162 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14163     interval : 20,
14164     delay: 250,
14165     preventDefault : true,
14166     stopDefault : false,
14167     timer : 0,
14168
14169     // private
14170     handleMouseDown : function(){
14171         clearTimeout(this.timer);
14172         this.el.blur();
14173         if(this.pressClass){
14174             this.el.addClass(this.pressClass);
14175         }
14176         this.mousedownTime = new Date();
14177
14178         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14179         this.el.on("mouseout", this.handleMouseOut, this);
14180
14181         this.fireEvent("mousedown", this);
14182         this.fireEvent("click", this);
14183         
14184         this.timer = this.click.defer(this.delay || this.interval, this);
14185     },
14186
14187     // private
14188     click : function(){
14189         this.fireEvent("click", this);
14190         this.timer = this.click.defer(this.getInterval(), this);
14191     },
14192
14193     // private
14194     getInterval: function(){
14195         if(!this.accelerate){
14196             return this.interval;
14197         }
14198         var pressTime = this.mousedownTime.getElapsed();
14199         if(pressTime < 500){
14200             return 400;
14201         }else if(pressTime < 1700){
14202             return 320;
14203         }else if(pressTime < 2600){
14204             return 250;
14205         }else if(pressTime < 3500){
14206             return 180;
14207         }else if(pressTime < 4400){
14208             return 140;
14209         }else if(pressTime < 5300){
14210             return 80;
14211         }else if(pressTime < 6200){
14212             return 50;
14213         }else{
14214             return 10;
14215         }
14216     },
14217
14218     // private
14219     handleMouseOut : function(){
14220         clearTimeout(this.timer);
14221         if(this.pressClass){
14222             this.el.removeClass(this.pressClass);
14223         }
14224         this.el.on("mouseover", this.handleMouseReturn, this);
14225     },
14226
14227     // private
14228     handleMouseReturn : function(){
14229         this.el.un("mouseover", this.handleMouseReturn);
14230         if(this.pressClass){
14231             this.el.addClass(this.pressClass);
14232         }
14233         this.click();
14234     },
14235
14236     // private
14237     handleMouseUp : function(){
14238         clearTimeout(this.timer);
14239         this.el.un("mouseover", this.handleMouseReturn);
14240         this.el.un("mouseout", this.handleMouseOut);
14241         Roo.get(document).un("mouseup", this.handleMouseUp);
14242         this.el.removeClass(this.pressClass);
14243         this.fireEvent("mouseup", this);
14244     }
14245 });/*
14246  * Based on:
14247  * Ext JS Library 1.1.1
14248  * Copyright(c) 2006-2007, Ext JS, LLC.
14249  *
14250  * Originally Released Under LGPL - original licence link has changed is not relivant.
14251  *
14252  * Fork - LGPL
14253  * <script type="text/javascript">
14254  */
14255
14256  
14257 /**
14258  * @class Roo.KeyNav
14259  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14260  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14261  * way to implement custom navigation schemes for any UI component.</p>
14262  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14263  * pageUp, pageDown, del, home, end.  Usage:</p>
14264  <pre><code>
14265 var nav = new Roo.KeyNav("my-element", {
14266     "left" : function(e){
14267         this.moveLeft(e.ctrlKey);
14268     },
14269     "right" : function(e){
14270         this.moveRight(e.ctrlKey);
14271     },
14272     "enter" : function(e){
14273         this.save();
14274     },
14275     scope : this
14276 });
14277 </code></pre>
14278  * @constructor
14279  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14280  * @param {Object} config The config
14281  */
14282 Roo.KeyNav = function(el, config){
14283     this.el = Roo.get(el);
14284     Roo.apply(this, config);
14285     if(!this.disabled){
14286         this.disabled = true;
14287         this.enable();
14288     }
14289 };
14290
14291 Roo.KeyNav.prototype = {
14292     /**
14293      * @cfg {Boolean} disabled
14294      * True to disable this KeyNav instance (defaults to false)
14295      */
14296     disabled : false,
14297     /**
14298      * @cfg {String} defaultEventAction
14299      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14300      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14301      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14302      */
14303     defaultEventAction: "stopEvent",
14304     /**
14305      * @cfg {Boolean} forceKeyDown
14306      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14307      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14308      * handle keydown instead of keypress.
14309      */
14310     forceKeyDown : false,
14311
14312     // private
14313     prepareEvent : function(e){
14314         var k = e.getKey();
14315         var h = this.keyToHandler[k];
14316         //if(h && this[h]){
14317         //    e.stopPropagation();
14318         //}
14319         if(Roo.isSafari && h && k >= 37 && k <= 40){
14320             e.stopEvent();
14321         }
14322     },
14323
14324     // private
14325     relay : function(e){
14326         var k = e.getKey();
14327         var h = this.keyToHandler[k];
14328         if(h && this[h]){
14329             if(this.doRelay(e, this[h], h) !== true){
14330                 e[this.defaultEventAction]();
14331             }
14332         }
14333     },
14334
14335     // private
14336     doRelay : function(e, h, hname){
14337         return h.call(this.scope || this, e);
14338     },
14339
14340     // possible handlers
14341     enter : false,
14342     left : false,
14343     right : false,
14344     up : false,
14345     down : false,
14346     tab : false,
14347     esc : false,
14348     pageUp : false,
14349     pageDown : false,
14350     del : false,
14351     home : false,
14352     end : false,
14353
14354     // quick lookup hash
14355     keyToHandler : {
14356         37 : "left",
14357         39 : "right",
14358         38 : "up",
14359         40 : "down",
14360         33 : "pageUp",
14361         34 : "pageDown",
14362         46 : "del",
14363         36 : "home",
14364         35 : "end",
14365         13 : "enter",
14366         27 : "esc",
14367         9  : "tab"
14368     },
14369
14370         /**
14371          * Enable this KeyNav
14372          */
14373         enable: function(){
14374                 if(this.disabled){
14375             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14376             // the EventObject will normalize Safari automatically
14377             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14378                 this.el.on("keydown", this.relay,  this);
14379             }else{
14380                 this.el.on("keydown", this.prepareEvent,  this);
14381                 this.el.on("keypress", this.relay,  this);
14382             }
14383                     this.disabled = false;
14384                 }
14385         },
14386
14387         /**
14388          * Disable this KeyNav
14389          */
14390         disable: function(){
14391                 if(!this.disabled){
14392                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14393                 this.el.un("keydown", this.relay);
14394             }else{
14395                 this.el.un("keydown", this.prepareEvent);
14396                 this.el.un("keypress", this.relay);
14397             }
14398                     this.disabled = true;
14399                 }
14400         }
14401 };/*
14402  * Based on:
14403  * Ext JS Library 1.1.1
14404  * Copyright(c) 2006-2007, Ext JS, LLC.
14405  *
14406  * Originally Released Under LGPL - original licence link has changed is not relivant.
14407  *
14408  * Fork - LGPL
14409  * <script type="text/javascript">
14410  */
14411
14412  
14413 /**
14414  * @class Roo.KeyMap
14415  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14416  * The constructor accepts the same config object as defined by {@link #addBinding}.
14417  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14418  * combination it will call the function with this signature (if the match is a multi-key
14419  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14420  * A KeyMap can also handle a string representation of keys.<br />
14421  * Usage:
14422  <pre><code>
14423 // map one key by key code
14424 var map = new Roo.KeyMap("my-element", {
14425     key: 13, // or Roo.EventObject.ENTER
14426     fn: myHandler,
14427     scope: myObject
14428 });
14429
14430 // map multiple keys to one action by string
14431 var map = new Roo.KeyMap("my-element", {
14432     key: "a\r\n\t",
14433     fn: myHandler,
14434     scope: myObject
14435 });
14436
14437 // map multiple keys to multiple actions by strings and array of codes
14438 var map = new Roo.KeyMap("my-element", [
14439     {
14440         key: [10,13],
14441         fn: function(){ alert("Return was pressed"); }
14442     }, {
14443         key: "abc",
14444         fn: function(){ alert('a, b or c was pressed'); }
14445     }, {
14446         key: "\t",
14447         ctrl:true,
14448         shift:true,
14449         fn: function(){ alert('Control + shift + tab was pressed.'); }
14450     }
14451 ]);
14452 </code></pre>
14453  * <b>Note: A KeyMap starts enabled</b>
14454  * @constructor
14455  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14456  * @param {Object} config The config (see {@link #addBinding})
14457  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14458  */
14459 Roo.KeyMap = function(el, config, eventName){
14460     this.el  = Roo.get(el);
14461     this.eventName = eventName || "keydown";
14462     this.bindings = [];
14463     if(config){
14464         this.addBinding(config);
14465     }
14466     this.enable();
14467 };
14468
14469 Roo.KeyMap.prototype = {
14470     /**
14471      * True to stop the event from bubbling and prevent the default browser action if the
14472      * key was handled by the KeyMap (defaults to false)
14473      * @type Boolean
14474      */
14475     stopEvent : false,
14476
14477     /**
14478      * Add a new binding to this KeyMap. The following config object properties are supported:
14479      * <pre>
14480 Property    Type             Description
14481 ----------  ---------------  ----------------------------------------------------------------------
14482 key         String/Array     A single keycode or an array of keycodes to handle
14483 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14484 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14485 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14486 fn          Function         The function to call when KeyMap finds the expected key combination
14487 scope       Object           The scope of the callback function
14488 </pre>
14489      *
14490      * Usage:
14491      * <pre><code>
14492 // Create a KeyMap
14493 var map = new Roo.KeyMap(document, {
14494     key: Roo.EventObject.ENTER,
14495     fn: handleKey,
14496     scope: this
14497 });
14498
14499 //Add a new binding to the existing KeyMap later
14500 map.addBinding({
14501     key: 'abc',
14502     shift: true,
14503     fn: handleKey,
14504     scope: this
14505 });
14506 </code></pre>
14507      * @param {Object/Array} config A single KeyMap config or an array of configs
14508      */
14509         addBinding : function(config){
14510         if(config instanceof Array){
14511             for(var i = 0, len = config.length; i < len; i++){
14512                 this.addBinding(config[i]);
14513             }
14514             return;
14515         }
14516         var keyCode = config.key,
14517             shift = config.shift, 
14518             ctrl = config.ctrl, 
14519             alt = config.alt,
14520             fn = config.fn,
14521             scope = config.scope;
14522         if(typeof keyCode == "string"){
14523             var ks = [];
14524             var keyString = keyCode.toUpperCase();
14525             for(var j = 0, len = keyString.length; j < len; j++){
14526                 ks.push(keyString.charCodeAt(j));
14527             }
14528             keyCode = ks;
14529         }
14530         var keyArray = keyCode instanceof Array;
14531         var handler = function(e){
14532             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14533                 var k = e.getKey();
14534                 if(keyArray){
14535                     for(var i = 0, len = keyCode.length; i < len; i++){
14536                         if(keyCode[i] == k){
14537                           if(this.stopEvent){
14538                               e.stopEvent();
14539                           }
14540                           fn.call(scope || window, k, e);
14541                           return;
14542                         }
14543                     }
14544                 }else{
14545                     if(k == keyCode){
14546                         if(this.stopEvent){
14547                            e.stopEvent();
14548                         }
14549                         fn.call(scope || window, k, e);
14550                     }
14551                 }
14552             }
14553         };
14554         this.bindings.push(handler);  
14555         },
14556
14557     /**
14558      * Shorthand for adding a single key listener
14559      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14560      * following options:
14561      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14562      * @param {Function} fn The function to call
14563      * @param {Object} scope (optional) The scope of the function
14564      */
14565     on : function(key, fn, scope){
14566         var keyCode, shift, ctrl, alt;
14567         if(typeof key == "object" && !(key instanceof Array)){
14568             keyCode = key.key;
14569             shift = key.shift;
14570             ctrl = key.ctrl;
14571             alt = key.alt;
14572         }else{
14573             keyCode = key;
14574         }
14575         this.addBinding({
14576             key: keyCode,
14577             shift: shift,
14578             ctrl: ctrl,
14579             alt: alt,
14580             fn: fn,
14581             scope: scope
14582         })
14583     },
14584
14585     // private
14586     handleKeyDown : function(e){
14587             if(this.enabled){ //just in case
14588             var b = this.bindings;
14589             for(var i = 0, len = b.length; i < len; i++){
14590                 b[i].call(this, e);
14591             }
14592             }
14593         },
14594         
14595         /**
14596          * Returns true if this KeyMap is enabled
14597          * @return {Boolean} 
14598          */
14599         isEnabled : function(){
14600             return this.enabled;  
14601         },
14602         
14603         /**
14604          * Enables this KeyMap
14605          */
14606         enable: function(){
14607                 if(!this.enabled){
14608                     this.el.on(this.eventName, this.handleKeyDown, this);
14609                     this.enabled = true;
14610                 }
14611         },
14612
14613         /**
14614          * Disable this KeyMap
14615          */
14616         disable: function(){
14617                 if(this.enabled){
14618                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14619                     this.enabled = false;
14620                 }
14621         }
14622 };/*
14623  * Based on:
14624  * Ext JS Library 1.1.1
14625  * Copyright(c) 2006-2007, Ext JS, LLC.
14626  *
14627  * Originally Released Under LGPL - original licence link has changed is not relivant.
14628  *
14629  * Fork - LGPL
14630  * <script type="text/javascript">
14631  */
14632
14633  
14634 /**
14635  * @class Roo.util.TextMetrics
14636  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14637  * wide, in pixels, a given block of text will be.
14638  * @singleton
14639  */
14640 Roo.util.TextMetrics = function(){
14641     var shared;
14642     return {
14643         /**
14644          * Measures the size of the specified text
14645          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14646          * that can affect the size of the rendered text
14647          * @param {String} text The text to measure
14648          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14649          * in order to accurately measure the text height
14650          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14651          */
14652         measure : function(el, text, fixedWidth){
14653             if(!shared){
14654                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14655             }
14656             shared.bind(el);
14657             shared.setFixedWidth(fixedWidth || 'auto');
14658             return shared.getSize(text);
14659         },
14660
14661         /**
14662          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14663          * the overhead of multiple calls to initialize the style properties on each measurement.
14664          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14665          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14666          * in order to accurately measure the text height
14667          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14668          */
14669         createInstance : function(el, fixedWidth){
14670             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14671         }
14672     };
14673 }();
14674
14675  
14676
14677 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14678     var ml = new Roo.Element(document.createElement('div'));
14679     document.body.appendChild(ml.dom);
14680     ml.position('absolute');
14681     ml.setLeftTop(-1000, -1000);
14682     ml.hide();
14683
14684     if(fixedWidth){
14685         ml.setWidth(fixedWidth);
14686     }
14687      
14688     var instance = {
14689         /**
14690          * Returns the size of the specified text based on the internal element's style and width properties
14691          * @memberOf Roo.util.TextMetrics.Instance#
14692          * @param {String} text The text to measure
14693          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14694          */
14695         getSize : function(text){
14696             ml.update(text);
14697             var s = ml.getSize();
14698             ml.update('');
14699             return s;
14700         },
14701
14702         /**
14703          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14704          * that can affect the size of the rendered text
14705          * @memberOf Roo.util.TextMetrics.Instance#
14706          * @param {String/HTMLElement} el The element, dom node or id
14707          */
14708         bind : function(el){
14709             ml.setStyle(
14710                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14711             );
14712         },
14713
14714         /**
14715          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14716          * to set a fixed width in order to accurately measure the text height.
14717          * @memberOf Roo.util.TextMetrics.Instance#
14718          * @param {Number} width The width to set on the element
14719          */
14720         setFixedWidth : function(width){
14721             ml.setWidth(width);
14722         },
14723
14724         /**
14725          * Returns the measured width of the specified text
14726          * @memberOf Roo.util.TextMetrics.Instance#
14727          * @param {String} text The text to measure
14728          * @return {Number} width The width in pixels
14729          */
14730         getWidth : function(text){
14731             ml.dom.style.width = 'auto';
14732             return this.getSize(text).width;
14733         },
14734
14735         /**
14736          * Returns the measured height of the specified text.  For multiline text, be sure to call
14737          * {@link #setFixedWidth} if necessary.
14738          * @memberOf Roo.util.TextMetrics.Instance#
14739          * @param {String} text The text to measure
14740          * @return {Number} height The height in pixels
14741          */
14742         getHeight : function(text){
14743             return this.getSize(text).height;
14744         }
14745     };
14746
14747     instance.bind(bindTo);
14748
14749     return instance;
14750 };
14751
14752 // backwards compat
14753 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14754  * Based on:
14755  * Ext JS Library 1.1.1
14756  * Copyright(c) 2006-2007, Ext JS, LLC.
14757  *
14758  * Originally Released Under LGPL - original licence link has changed is not relivant.
14759  *
14760  * Fork - LGPL
14761  * <script type="text/javascript">
14762  */
14763
14764 /**
14765  * @class Roo.state.Provider
14766  * Abstract base class for state provider implementations. This class provides methods
14767  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14768  * Provider interface.
14769  */
14770 Roo.state.Provider = function(){
14771     /**
14772      * @event statechange
14773      * Fires when a state change occurs.
14774      * @param {Provider} this This state provider
14775      * @param {String} key The state key which was changed
14776      * @param {String} value The encoded value for the state
14777      */
14778     this.addEvents({
14779         "statechange": true
14780     });
14781     this.state = {};
14782     Roo.state.Provider.superclass.constructor.call(this);
14783 };
14784 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14785     /**
14786      * Returns the current value for a key
14787      * @param {String} name The key name
14788      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14789      * @return {Mixed} The state data
14790      */
14791     get : function(name, defaultValue){
14792         return typeof this.state[name] == "undefined" ?
14793             defaultValue : this.state[name];
14794     },
14795     
14796     /**
14797      * Clears a value from the state
14798      * @param {String} name The key name
14799      */
14800     clear : function(name){
14801         delete this.state[name];
14802         this.fireEvent("statechange", this, name, null);
14803     },
14804     
14805     /**
14806      * Sets the value for a key
14807      * @param {String} name The key name
14808      * @param {Mixed} value The value to set
14809      */
14810     set : function(name, value){
14811         this.state[name] = value;
14812         this.fireEvent("statechange", this, name, value);
14813     },
14814     
14815     /**
14816      * Decodes a string previously encoded with {@link #encodeValue}.
14817      * @param {String} value The value to decode
14818      * @return {Mixed} The decoded value
14819      */
14820     decodeValue : function(cookie){
14821         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14822         var matches = re.exec(unescape(cookie));
14823         if(!matches || !matches[1]) return; // non state cookie
14824         var type = matches[1];
14825         var v = matches[2];
14826         switch(type){
14827             case "n":
14828                 return parseFloat(v);
14829             case "d":
14830                 return new Date(Date.parse(v));
14831             case "b":
14832                 return (v == "1");
14833             case "a":
14834                 var all = [];
14835                 var values = v.split("^");
14836                 for(var i = 0, len = values.length; i < len; i++){
14837                     all.push(this.decodeValue(values[i]));
14838                 }
14839                 return all;
14840            case "o":
14841                 var all = {};
14842                 var values = v.split("^");
14843                 for(var i = 0, len = values.length; i < len; i++){
14844                     var kv = values[i].split("=");
14845                     all[kv[0]] = this.decodeValue(kv[1]);
14846                 }
14847                 return all;
14848            default:
14849                 return v;
14850         }
14851     },
14852     
14853     /**
14854      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14855      * @param {Mixed} value The value to encode
14856      * @return {String} The encoded value
14857      */
14858     encodeValue : function(v){
14859         var enc;
14860         if(typeof v == "number"){
14861             enc = "n:" + v;
14862         }else if(typeof v == "boolean"){
14863             enc = "b:" + (v ? "1" : "0");
14864         }else if(v instanceof Date){
14865             enc = "d:" + v.toGMTString();
14866         }else if(v instanceof Array){
14867             var flat = "";
14868             for(var i = 0, len = v.length; i < len; i++){
14869                 flat += this.encodeValue(v[i]);
14870                 if(i != len-1) flat += "^";
14871             }
14872             enc = "a:" + flat;
14873         }else if(typeof v == "object"){
14874             var flat = "";
14875             for(var key in v){
14876                 if(typeof v[key] != "function"){
14877                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14878                 }
14879             }
14880             enc = "o:" + flat.substring(0, flat.length-1);
14881         }else{
14882             enc = "s:" + v;
14883         }
14884         return escape(enc);        
14885     }
14886 });
14887
14888 /*
14889  * Based on:
14890  * Ext JS Library 1.1.1
14891  * Copyright(c) 2006-2007, Ext JS, LLC.
14892  *
14893  * Originally Released Under LGPL - original licence link has changed is not relivant.
14894  *
14895  * Fork - LGPL
14896  * <script type="text/javascript">
14897  */
14898 /**
14899  * @class Roo.state.Manager
14900  * This is the global state manager. By default all components that are "state aware" check this class
14901  * for state information if you don't pass them a custom state provider. In order for this class
14902  * to be useful, it must be initialized with a provider when your application initializes.
14903  <pre><code>
14904 // in your initialization function
14905 init : function(){
14906    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14907    ...
14908    // supposed you have a {@link Roo.BorderLayout}
14909    var layout = new Roo.BorderLayout(...);
14910    layout.restoreState();
14911    // or a {Roo.BasicDialog}
14912    var dialog = new Roo.BasicDialog(...);
14913    dialog.restoreState();
14914  </code></pre>
14915  * @singleton
14916  */
14917 Roo.state.Manager = function(){
14918     var provider = new Roo.state.Provider();
14919     
14920     return {
14921         /**
14922          * Configures the default state provider for your application
14923          * @param {Provider} stateProvider The state provider to set
14924          */
14925         setProvider : function(stateProvider){
14926             provider = stateProvider;
14927         },
14928         
14929         /**
14930          * Returns the current value for a key
14931          * @param {String} name The key name
14932          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14933          * @return {Mixed} The state data
14934          */
14935         get : function(key, defaultValue){
14936             return provider.get(key, defaultValue);
14937         },
14938         
14939         /**
14940          * Sets the value for a key
14941          * @param {String} name The key name
14942          * @param {Mixed} value The state data
14943          */
14944          set : function(key, value){
14945             provider.set(key, value);
14946         },
14947         
14948         /**
14949          * Clears a value from the state
14950          * @param {String} name The key name
14951          */
14952         clear : function(key){
14953             provider.clear(key);
14954         },
14955         
14956         /**
14957          * Gets the currently configured state provider
14958          * @return {Provider} The state provider
14959          */
14960         getProvider : function(){
14961             return provider;
14962         }
14963     };
14964 }();
14965 /*
14966  * Based on:
14967  * Ext JS Library 1.1.1
14968  * Copyright(c) 2006-2007, Ext JS, LLC.
14969  *
14970  * Originally Released Under LGPL - original licence link has changed is not relivant.
14971  *
14972  * Fork - LGPL
14973  * <script type="text/javascript">
14974  */
14975 /**
14976  * @class Roo.state.CookieProvider
14977  * @extends Roo.state.Provider
14978  * The default Provider implementation which saves state via cookies.
14979  * <br />Usage:
14980  <pre><code>
14981    var cp = new Roo.state.CookieProvider({
14982        path: "/cgi-bin/",
14983        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14984        domain: "roojs.com"
14985    })
14986    Roo.state.Manager.setProvider(cp);
14987  </code></pre>
14988  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14989  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14990  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14991  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14992  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14993  * domain the page is running on including the 'www' like 'www.roojs.com')
14994  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14995  * @constructor
14996  * Create a new CookieProvider
14997  * @param {Object} config The configuration object
14998  */
14999 Roo.state.CookieProvider = function(config){
15000     Roo.state.CookieProvider.superclass.constructor.call(this);
15001     this.path = "/";
15002     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15003     this.domain = null;
15004     this.secure = false;
15005     Roo.apply(this, config);
15006     this.state = this.readCookies();
15007 };
15008
15009 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15010     // private
15011     set : function(name, value){
15012         if(typeof value == "undefined" || value === null){
15013             this.clear(name);
15014             return;
15015         }
15016         this.setCookie(name, value);
15017         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15018     },
15019
15020     // private
15021     clear : function(name){
15022         this.clearCookie(name);
15023         Roo.state.CookieProvider.superclass.clear.call(this, name);
15024     },
15025
15026     // private
15027     readCookies : function(){
15028         var cookies = {};
15029         var c = document.cookie + ";";
15030         var re = /\s?(.*?)=(.*?);/g;
15031         var matches;
15032         while((matches = re.exec(c)) != null){
15033             var name = matches[1];
15034             var value = matches[2];
15035             if(name && name.substring(0,3) == "ys-"){
15036                 cookies[name.substr(3)] = this.decodeValue(value);
15037             }
15038         }
15039         return cookies;
15040     },
15041
15042     // private
15043     setCookie : function(name, value){
15044         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15045            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15046            ((this.path == null) ? "" : ("; path=" + this.path)) +
15047            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15048            ((this.secure == true) ? "; secure" : "");
15049     },
15050
15051     // private
15052     clearCookie : function(name){
15053         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15054            ((this.path == null) ? "" : ("; path=" + this.path)) +
15055            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15056            ((this.secure == true) ? "; secure" : "");
15057     }
15058 });/*
15059  * Based on:
15060  * Ext JS Library 1.1.1
15061  * Copyright(c) 2006-2007, Ext JS, LLC.
15062  *
15063  * Originally Released Under LGPL - original licence link has changed is not relivant.
15064  *
15065  * Fork - LGPL
15066  * <script type="text/javascript">
15067  */
15068  
15069
15070 /**
15071  * @class Roo.ComponentMgr
15072  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15073  * @singleton
15074  */
15075 Roo.ComponentMgr = function(){
15076     var all = new Roo.util.MixedCollection();
15077
15078     return {
15079         /**
15080          * Registers a component.
15081          * @param {Roo.Component} c The component
15082          */
15083         register : function(c){
15084             all.add(c);
15085         },
15086
15087         /**
15088          * Unregisters a component.
15089          * @param {Roo.Component} c The component
15090          */
15091         unregister : function(c){
15092             all.remove(c);
15093         },
15094
15095         /**
15096          * Returns a component by id
15097          * @param {String} id The component id
15098          */
15099         get : function(id){
15100             return all.get(id);
15101         },
15102
15103         /**
15104          * Registers a function that will be called when a specified component is added to ComponentMgr
15105          * @param {String} id The component id
15106          * @param {Funtction} fn The callback function
15107          * @param {Object} scope The scope of the callback
15108          */
15109         onAvailable : function(id, fn, scope){
15110             all.on("add", function(index, o){
15111                 if(o.id == id){
15112                     fn.call(scope || o, o);
15113                     all.un("add", fn, scope);
15114                 }
15115             });
15116         }
15117     };
15118 }();/*
15119  * Based on:
15120  * Ext JS Library 1.1.1
15121  * Copyright(c) 2006-2007, Ext JS, LLC.
15122  *
15123  * Originally Released Under LGPL - original licence link has changed is not relivant.
15124  *
15125  * Fork - LGPL
15126  * <script type="text/javascript">
15127  */
15128  
15129 /**
15130  * @class Roo.Component
15131  * @extends Roo.util.Observable
15132  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15133  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15134  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15135  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15136  * All visual components (widgets) that require rendering into a layout should subclass Component.
15137  * @constructor
15138  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15139  * 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
15140  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15141  */
15142 Roo.Component = function(config){
15143     config = config || {};
15144     if(config.tagName || config.dom || typeof config == "string"){ // element object
15145         config = {el: config, id: config.id || config};
15146     }
15147     this.initialConfig = config;
15148
15149     Roo.apply(this, config);
15150     this.addEvents({
15151         /**
15152          * @event disable
15153          * Fires after the component is disabled.
15154              * @param {Roo.Component} this
15155              */
15156         disable : true,
15157         /**
15158          * @event enable
15159          * Fires after the component is enabled.
15160              * @param {Roo.Component} this
15161              */
15162         enable : true,
15163         /**
15164          * @event beforeshow
15165          * Fires before the component is shown.  Return false to stop the show.
15166              * @param {Roo.Component} this
15167              */
15168         beforeshow : true,
15169         /**
15170          * @event show
15171          * Fires after the component is shown.
15172              * @param {Roo.Component} this
15173              */
15174         show : true,
15175         /**
15176          * @event beforehide
15177          * Fires before the component is hidden. Return false to stop the hide.
15178              * @param {Roo.Component} this
15179              */
15180         beforehide : true,
15181         /**
15182          * @event hide
15183          * Fires after the component is hidden.
15184              * @param {Roo.Component} this
15185              */
15186         hide : true,
15187         /**
15188          * @event beforerender
15189          * Fires before the component is rendered. Return false to stop the render.
15190              * @param {Roo.Component} this
15191              */
15192         beforerender : true,
15193         /**
15194          * @event render
15195          * Fires after the component is rendered.
15196              * @param {Roo.Component} this
15197              */
15198         render : true,
15199         /**
15200          * @event beforedestroy
15201          * Fires before the component is destroyed. Return false to stop the destroy.
15202              * @param {Roo.Component} this
15203              */
15204         beforedestroy : true,
15205         /**
15206          * @event destroy
15207          * Fires after the component is destroyed.
15208              * @param {Roo.Component} this
15209              */
15210         destroy : true
15211     });
15212     if(!this.id){
15213         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15214     }
15215     Roo.ComponentMgr.register(this);
15216     Roo.Component.superclass.constructor.call(this);
15217     this.initComponent();
15218     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15219         this.render(this.renderTo);
15220         delete this.renderTo;
15221     }
15222 };
15223
15224 /** @private */
15225 Roo.Component.AUTO_ID = 1000;
15226
15227 Roo.extend(Roo.Component, Roo.util.Observable, {
15228     /**
15229      * @scope Roo.Component.prototype
15230      * @type {Boolean}
15231      * true if this component is hidden. Read-only.
15232      */
15233     hidden : false,
15234     /**
15235      * @type {Boolean}
15236      * true if this component is disabled. Read-only.
15237      */
15238     disabled : false,
15239     /**
15240      * @type {Boolean}
15241      * true if this component has been rendered. Read-only.
15242      */
15243     rendered : false,
15244     
15245     /** @cfg {String} disableClass
15246      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15247      */
15248     disabledClass : "x-item-disabled",
15249         /** @cfg {Boolean} allowDomMove
15250          * Whether the component can move the Dom node when rendering (defaults to true).
15251          */
15252     allowDomMove : true,
15253     /** @cfg {String} hideMode (display|visibility)
15254      * How this component should hidden. Supported values are
15255      * "visibility" (css visibility), "offsets" (negative offset position) and
15256      * "display" (css display) - defaults to "display".
15257      */
15258     hideMode: 'display',
15259
15260     /** @private */
15261     ctype : "Roo.Component",
15262
15263     /**
15264      * @cfg {String} actionMode 
15265      * which property holds the element that used for  hide() / show() / disable() / enable()
15266      * default is 'el' 
15267      */
15268     actionMode : "el",
15269
15270     /** @private */
15271     getActionEl : function(){
15272         return this[this.actionMode];
15273     },
15274
15275     initComponent : Roo.emptyFn,
15276     /**
15277      * If this is a lazy rendering component, render it to its container element.
15278      * @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.
15279      */
15280     render : function(container, position){
15281         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15282             if(!container && this.el){
15283                 this.el = Roo.get(this.el);
15284                 container = this.el.dom.parentNode;
15285                 this.allowDomMove = false;
15286             }
15287             this.container = Roo.get(container);
15288             this.rendered = true;
15289             if(position !== undefined){
15290                 if(typeof position == 'number'){
15291                     position = this.container.dom.childNodes[position];
15292                 }else{
15293                     position = Roo.getDom(position);
15294                 }
15295             }
15296             this.onRender(this.container, position || null);
15297             if(this.cls){
15298                 this.el.addClass(this.cls);
15299                 delete this.cls;
15300             }
15301             if(this.style){
15302                 this.el.applyStyles(this.style);
15303                 delete this.style;
15304             }
15305             this.fireEvent("render", this);
15306             this.afterRender(this.container);
15307             if(this.hidden){
15308                 this.hide();
15309             }
15310             if(this.disabled){
15311                 this.disable();
15312             }
15313         }
15314         return this;
15315     },
15316
15317     /** @private */
15318     // default function is not really useful
15319     onRender : function(ct, position){
15320         if(this.el){
15321             this.el = Roo.get(this.el);
15322             if(this.allowDomMove !== false){
15323                 ct.dom.insertBefore(this.el.dom, position);
15324             }
15325         }
15326     },
15327
15328     /** @private */
15329     getAutoCreate : function(){
15330         var cfg = typeof this.autoCreate == "object" ?
15331                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15332         if(this.id && !cfg.id){
15333             cfg.id = this.id;
15334         }
15335         return cfg;
15336     },
15337
15338     /** @private */
15339     afterRender : Roo.emptyFn,
15340
15341     /**
15342      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15343      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15344      */
15345     destroy : function(){
15346         if(this.fireEvent("beforedestroy", this) !== false){
15347             this.purgeListeners();
15348             this.beforeDestroy();
15349             if(this.rendered){
15350                 this.el.removeAllListeners();
15351                 this.el.remove();
15352                 if(this.actionMode == "container"){
15353                     this.container.remove();
15354                 }
15355             }
15356             this.onDestroy();
15357             Roo.ComponentMgr.unregister(this);
15358             this.fireEvent("destroy", this);
15359         }
15360     },
15361
15362         /** @private */
15363     beforeDestroy : function(){
15364
15365     },
15366
15367         /** @private */
15368         onDestroy : function(){
15369
15370     },
15371
15372     /**
15373      * Returns the underlying {@link Roo.Element}.
15374      * @return {Roo.Element} The element
15375      */
15376     getEl : function(){
15377         return this.el;
15378     },
15379
15380     /**
15381      * Returns the id of this component.
15382      * @return {String}
15383      */
15384     getId : function(){
15385         return this.id;
15386     },
15387
15388     /**
15389      * Try to focus this component.
15390      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15391      * @return {Roo.Component} this
15392      */
15393     focus : function(selectText){
15394         if(this.rendered){
15395             this.el.focus();
15396             if(selectText === true){
15397                 this.el.dom.select();
15398             }
15399         }
15400         return this;
15401     },
15402
15403     /** @private */
15404     blur : function(){
15405         if(this.rendered){
15406             this.el.blur();
15407         }
15408         return this;
15409     },
15410
15411     /**
15412      * Disable this component.
15413      * @return {Roo.Component} this
15414      */
15415     disable : function(){
15416         if(this.rendered){
15417             this.onDisable();
15418         }
15419         this.disabled = true;
15420         this.fireEvent("disable", this);
15421         return this;
15422     },
15423
15424         // private
15425     onDisable : function(){
15426         this.getActionEl().addClass(this.disabledClass);
15427         this.el.dom.disabled = true;
15428     },
15429
15430     /**
15431      * Enable this component.
15432      * @return {Roo.Component} this
15433      */
15434     enable : function(){
15435         if(this.rendered){
15436             this.onEnable();
15437         }
15438         this.disabled = false;
15439         this.fireEvent("enable", this);
15440         return this;
15441     },
15442
15443         // private
15444     onEnable : function(){
15445         this.getActionEl().removeClass(this.disabledClass);
15446         this.el.dom.disabled = false;
15447     },
15448
15449     /**
15450      * Convenience function for setting disabled/enabled by boolean.
15451      * @param {Boolean} disabled
15452      */
15453     setDisabled : function(disabled){
15454         this[disabled ? "disable" : "enable"]();
15455     },
15456
15457     /**
15458      * Show this component.
15459      * @return {Roo.Component} this
15460      */
15461     show: function(){
15462         if(this.fireEvent("beforeshow", this) !== false){
15463             this.hidden = false;
15464             if(this.rendered){
15465                 this.onShow();
15466             }
15467             this.fireEvent("show", this);
15468         }
15469         return this;
15470     },
15471
15472     // private
15473     onShow : function(){
15474         var ae = this.getActionEl();
15475         if(this.hideMode == 'visibility'){
15476             ae.dom.style.visibility = "visible";
15477         }else if(this.hideMode == 'offsets'){
15478             ae.removeClass('x-hidden');
15479         }else{
15480             ae.dom.style.display = "";
15481         }
15482     },
15483
15484     /**
15485      * Hide this component.
15486      * @return {Roo.Component} this
15487      */
15488     hide: function(){
15489         if(this.fireEvent("beforehide", this) !== false){
15490             this.hidden = true;
15491             if(this.rendered){
15492                 this.onHide();
15493             }
15494             this.fireEvent("hide", this);
15495         }
15496         return this;
15497     },
15498
15499     // private
15500     onHide : function(){
15501         var ae = this.getActionEl();
15502         if(this.hideMode == 'visibility'){
15503             ae.dom.style.visibility = "hidden";
15504         }else if(this.hideMode == 'offsets'){
15505             ae.addClass('x-hidden');
15506         }else{
15507             ae.dom.style.display = "none";
15508         }
15509     },
15510
15511     /**
15512      * Convenience function to hide or show this component by boolean.
15513      * @param {Boolean} visible True to show, false to hide
15514      * @return {Roo.Component} this
15515      */
15516     setVisible: function(visible){
15517         if(visible) {
15518             this.show();
15519         }else{
15520             this.hide();
15521         }
15522         return this;
15523     },
15524
15525     /**
15526      * Returns true if this component is visible.
15527      */
15528     isVisible : function(){
15529         return this.getActionEl().isVisible();
15530     },
15531
15532     cloneConfig : function(overrides){
15533         overrides = overrides || {};
15534         var id = overrides.id || Roo.id();
15535         var cfg = Roo.applyIf(overrides, this.initialConfig);
15536         cfg.id = id; // prevent dup id
15537         return new this.constructor(cfg);
15538     }
15539 });/*
15540  * Based on:
15541  * Ext JS Library 1.1.1
15542  * Copyright(c) 2006-2007, Ext JS, LLC.
15543  *
15544  * Originally Released Under LGPL - original licence link has changed is not relivant.
15545  *
15546  * Fork - LGPL
15547  * <script type="text/javascript">
15548  */
15549
15550 /**
15551  * @class Roo.BoxComponent
15552  * @extends Roo.Component
15553  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15554  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15555  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15556  * layout containers.
15557  * @constructor
15558  * @param {Roo.Element/String/Object} config The configuration options.
15559  */
15560 Roo.BoxComponent = function(config){
15561     Roo.Component.call(this, config);
15562     this.addEvents({
15563         /**
15564          * @event resize
15565          * Fires after the component is resized.
15566              * @param {Roo.Component} this
15567              * @param {Number} adjWidth The box-adjusted width that was set
15568              * @param {Number} adjHeight The box-adjusted height that was set
15569              * @param {Number} rawWidth The width that was originally specified
15570              * @param {Number} rawHeight The height that was originally specified
15571              */
15572         resize : true,
15573         /**
15574          * @event move
15575          * Fires after the component is moved.
15576              * @param {Roo.Component} this
15577              * @param {Number} x The new x position
15578              * @param {Number} y The new y position
15579              */
15580         move : true
15581     });
15582 };
15583
15584 Roo.extend(Roo.BoxComponent, Roo.Component, {
15585     // private, set in afterRender to signify that the component has been rendered
15586     boxReady : false,
15587     // private, used to defer height settings to subclasses
15588     deferHeight: false,
15589     /** @cfg {Number} width
15590      * width (optional) size of component
15591      */
15592      /** @cfg {Number} height
15593      * height (optional) size of component
15594      */
15595      
15596     /**
15597      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15598      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15599      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15600      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15601      * @return {Roo.BoxComponent} this
15602      */
15603     setSize : function(w, h){
15604         // support for standard size objects
15605         if(typeof w == 'object'){
15606             h = w.height;
15607             w = w.width;
15608         }
15609         // not rendered
15610         if(!this.boxReady){
15611             this.width = w;
15612             this.height = h;
15613             return this;
15614         }
15615
15616         // prevent recalcs when not needed
15617         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15618             return this;
15619         }
15620         this.lastSize = {width: w, height: h};
15621
15622         var adj = this.adjustSize(w, h);
15623         var aw = adj.width, ah = adj.height;
15624         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15625             var rz = this.getResizeEl();
15626             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15627                 rz.setSize(aw, ah);
15628             }else if(!this.deferHeight && ah !== undefined){
15629                 rz.setHeight(ah);
15630             }else if(aw !== undefined){
15631                 rz.setWidth(aw);
15632             }
15633             this.onResize(aw, ah, w, h);
15634             this.fireEvent('resize', this, aw, ah, w, h);
15635         }
15636         return this;
15637     },
15638
15639     /**
15640      * Gets the current size of the component's underlying element.
15641      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15642      */
15643     getSize : function(){
15644         return this.el.getSize();
15645     },
15646
15647     /**
15648      * Gets the current XY position of the component's underlying element.
15649      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15650      * @return {Array} The XY position of the element (e.g., [100, 200])
15651      */
15652     getPosition : function(local){
15653         if(local === true){
15654             return [this.el.getLeft(true), this.el.getTop(true)];
15655         }
15656         return this.xy || this.el.getXY();
15657     },
15658
15659     /**
15660      * Gets the current box measurements of the component's underlying element.
15661      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15662      * @returns {Object} box An object in the format {x, y, width, height}
15663      */
15664     getBox : function(local){
15665         var s = this.el.getSize();
15666         if(local){
15667             s.x = this.el.getLeft(true);
15668             s.y = this.el.getTop(true);
15669         }else{
15670             var xy = this.xy || this.el.getXY();
15671             s.x = xy[0];
15672             s.y = xy[1];
15673         }
15674         return s;
15675     },
15676
15677     /**
15678      * Sets the current box measurements of the component's underlying element.
15679      * @param {Object} box An object in the format {x, y, width, height}
15680      * @returns {Roo.BoxComponent} this
15681      */
15682     updateBox : function(box){
15683         this.setSize(box.width, box.height);
15684         this.setPagePosition(box.x, box.y);
15685         return this;
15686     },
15687
15688     // protected
15689     getResizeEl : function(){
15690         return this.resizeEl || this.el;
15691     },
15692
15693     // protected
15694     getPositionEl : function(){
15695         return this.positionEl || this.el;
15696     },
15697
15698     /**
15699      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15700      * This method fires the move event.
15701      * @param {Number} left The new left
15702      * @param {Number} top The new top
15703      * @returns {Roo.BoxComponent} this
15704      */
15705     setPosition : function(x, y){
15706         this.x = x;
15707         this.y = y;
15708         if(!this.boxReady){
15709             return this;
15710         }
15711         var adj = this.adjustPosition(x, y);
15712         var ax = adj.x, ay = adj.y;
15713
15714         var el = this.getPositionEl();
15715         if(ax !== undefined || ay !== undefined){
15716             if(ax !== undefined && ay !== undefined){
15717                 el.setLeftTop(ax, ay);
15718             }else if(ax !== undefined){
15719                 el.setLeft(ax);
15720             }else if(ay !== undefined){
15721                 el.setTop(ay);
15722             }
15723             this.onPosition(ax, ay);
15724             this.fireEvent('move', this, ax, ay);
15725         }
15726         return this;
15727     },
15728
15729     /**
15730      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15731      * This method fires the move event.
15732      * @param {Number} x The new x position
15733      * @param {Number} y The new y position
15734      * @returns {Roo.BoxComponent} this
15735      */
15736     setPagePosition : function(x, y){
15737         this.pageX = x;
15738         this.pageY = y;
15739         if(!this.boxReady){
15740             return;
15741         }
15742         if(x === undefined || y === undefined){ // cannot translate undefined points
15743             return;
15744         }
15745         var p = this.el.translatePoints(x, y);
15746         this.setPosition(p.left, p.top);
15747         return this;
15748     },
15749
15750     // private
15751     onRender : function(ct, position){
15752         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15753         if(this.resizeEl){
15754             this.resizeEl = Roo.get(this.resizeEl);
15755         }
15756         if(this.positionEl){
15757             this.positionEl = Roo.get(this.positionEl);
15758         }
15759     },
15760
15761     // private
15762     afterRender : function(){
15763         Roo.BoxComponent.superclass.afterRender.call(this);
15764         this.boxReady = true;
15765         this.setSize(this.width, this.height);
15766         if(this.x || this.y){
15767             this.setPosition(this.x, this.y);
15768         }
15769         if(this.pageX || this.pageY){
15770             this.setPagePosition(this.pageX, this.pageY);
15771         }
15772     },
15773
15774     /**
15775      * Force the component's size to recalculate based on the underlying element's current height and width.
15776      * @returns {Roo.BoxComponent} this
15777      */
15778     syncSize : function(){
15779         delete this.lastSize;
15780         this.setSize(this.el.getWidth(), this.el.getHeight());
15781         return this;
15782     },
15783
15784     /**
15785      * Called after the component is resized, this method is empty by default but can be implemented by any
15786      * subclass that needs to perform custom logic after a resize occurs.
15787      * @param {Number} adjWidth The box-adjusted width that was set
15788      * @param {Number} adjHeight The box-adjusted height that was set
15789      * @param {Number} rawWidth The width that was originally specified
15790      * @param {Number} rawHeight The height that was originally specified
15791      */
15792     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15793
15794     },
15795
15796     /**
15797      * Called after the component is moved, this method is empty by default but can be implemented by any
15798      * subclass that needs to perform custom logic after a move occurs.
15799      * @param {Number} x The new x position
15800      * @param {Number} y The new y position
15801      */
15802     onPosition : function(x, y){
15803
15804     },
15805
15806     // private
15807     adjustSize : function(w, h){
15808         if(this.autoWidth){
15809             w = 'auto';
15810         }
15811         if(this.autoHeight){
15812             h = 'auto';
15813         }
15814         return {width : w, height: h};
15815     },
15816
15817     // private
15818     adjustPosition : function(x, y){
15819         return {x : x, y: y};
15820     }
15821 });/*
15822  * Original code for Roojs - LGPL
15823  * <script type="text/javascript">
15824  */
15825  
15826 /**
15827  * @class Roo.XComponent
15828  * A delayed Element creator...
15829  * Or a way to group chunks of interface together.
15830  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15831  *  used in conjunction with XComponent.build() it will create an instance of each element,
15832  *  then call addxtype() to build the User interface.
15833  * 
15834  * Mypart.xyx = new Roo.XComponent({
15835
15836     parent : 'Mypart.xyz', // empty == document.element.!!
15837     order : '001',
15838     name : 'xxxx'
15839     region : 'xxxx'
15840     disabled : function() {} 
15841      
15842     tree : function() { // return an tree of xtype declared components
15843         var MODULE = this;
15844         return 
15845         {
15846             xtype : 'NestedLayoutPanel',
15847             // technicall
15848         }
15849      ]
15850  *})
15851  *
15852  *
15853  * It can be used to build a big heiracy, with parent etc.
15854  * or you can just use this to render a single compoent to a dom element
15855  * MYPART.render(Roo.Element | String(id) | dom_element )
15856  *
15857  *
15858  * Usage patterns.
15859  *
15860  * Classic Roo
15861  *
15862  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15863  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15864  *
15865  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15866  *
15867  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15868  * - if mulitple topModules exist, the last one is defined as the top module.
15869  *
15870  * Embeded Roo
15871  * 
15872  * When the top level or multiple modules are to embedded into a existing HTML page,
15873  * the parent element can container '#id' of the element where the module will be drawn.
15874  *
15875  * Bootstrap Roo
15876  *
15877  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15878  * it relies more on a include mechanism, where sub modules are included into an outer page.
15879  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15880  * 
15881  * Bootstrap Roo Included elements
15882  *
15883  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15884  * hence confusing the component builder as it thinks there are multiple top level elements. 
15885  *
15886  * 
15887  * 
15888  * @extends Roo.util.Observable
15889  * @constructor
15890  * @param cfg {Object} configuration of component
15891  * 
15892  */
15893 Roo.XComponent = function(cfg) {
15894     Roo.apply(this, cfg);
15895     this.addEvents({ 
15896         /**
15897              * @event built
15898              * Fires when this the componnt is built
15899              * @param {Roo.XComponent} c the component
15900              */
15901         'built' : true
15902         
15903     });
15904     this.region = this.region || 'center'; // default..
15905     Roo.XComponent.register(this);
15906     this.modules = false;
15907     this.el = false; // where the layout goes..
15908     
15909     
15910 }
15911 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15912     /**
15913      * @property el
15914      * The created element (with Roo.factory())
15915      * @type {Roo.Layout}
15916      */
15917     el  : false,
15918     
15919     /**
15920      * @property el
15921      * for BC  - use el in new code
15922      * @type {Roo.Layout}
15923      */
15924     panel : false,
15925     
15926     /**
15927      * @property layout
15928      * for BC  - use el in new code
15929      * @type {Roo.Layout}
15930      */
15931     layout : false,
15932     
15933      /**
15934      * @cfg {Function|boolean} disabled
15935      * If this module is disabled by some rule, return true from the funtion
15936      */
15937     disabled : false,
15938     
15939     /**
15940      * @cfg {String} parent 
15941      * Name of parent element which it get xtype added to..
15942      */
15943     parent: false,
15944     
15945     /**
15946      * @cfg {String} order
15947      * Used to set the order in which elements are created (usefull for multiple tabs)
15948      */
15949     
15950     order : false,
15951     /**
15952      * @cfg {String} name
15953      * String to display while loading.
15954      */
15955     name : false,
15956     /**
15957      * @cfg {String} region
15958      * Region to render component to (defaults to center)
15959      */
15960     region : 'center',
15961     
15962     /**
15963      * @cfg {Array} items
15964      * A single item array - the first element is the root of the tree..
15965      * It's done this way to stay compatible with the Xtype system...
15966      */
15967     items : false,
15968     
15969     /**
15970      * @property _tree
15971      * The method that retuns the tree of parts that make up this compoennt 
15972      * @type {function}
15973      */
15974     _tree  : false,
15975     
15976      /**
15977      * render
15978      * render element to dom or tree
15979      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15980      */
15981     
15982     render : function(el)
15983     {
15984         
15985         el = el || false;
15986         var hp = this.parent ? 1 : 0;
15987         Roo.debug &&  Roo.log(this);
15988         
15989         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15990             // if parent is a '#.....' string, then let's use that..
15991             var ename = this.parent.substr(1);
15992             this.parent = false;
15993             Roo.debug && Roo.log(ename);
15994             switch (ename) {
15995                 case 'bootstrap-body' :
15996                     if (typeof(Roo.bootstrap.Body) != 'undefined') {
15997                         this.parent = { el :  new  Roo.bootstrap.Body() };
15998                         Roo.debug && Roo.log("setting el to doc body");
15999                          
16000                     } else {
16001                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16002                     }
16003                     break;
16004                 case 'bootstrap':
16005                     this.parent = { el : true};
16006                     // fall through
16007                 default:
16008                     el = Roo.get(ename);
16009                     break;
16010             }
16011                 
16012             
16013             if (!el && !this.parent) {
16014                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16015                 return;
16016             }
16017         }
16018         Roo.debug && Roo.log("EL:");
16019         Roo.debug && Roo.log(el);
16020         Roo.debug && Roo.log("this.parent.el:");
16021         Roo.debug && Roo.log(this.parent.el);
16022         
16023         var tree = this._tree ? this._tree() : this.tree();
16024
16025         // altertive root elements ??? - we need a better way to indicate these.
16026         var is_alt = (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16027                         (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16028         
16029         if (!this.parent && is_alt) {
16030             //el = Roo.get(document.body);
16031             this.parent = { el : true };
16032         }
16033             
16034             
16035         
16036         if (!this.parent) {
16037             
16038             Roo.debug && Roo.log("no parent - creating one");
16039             
16040             el = el ? Roo.get(el) : false;      
16041             
16042             // it's a top level one..
16043             this.parent =  {
16044                 el : new Roo.BorderLayout(el || document.body, {
16045                 
16046                      center: {
16047                          titlebar: false,
16048                          autoScroll:false,
16049                          closeOnTab: true,
16050                          tabPosition: 'top',
16051                           //resizeTabs: true,
16052                          alwaysShowTabs: el && hp? false :  true,
16053                          hideTabs: el || !hp ? true :  false,
16054                          minTabWidth: 140
16055                      }
16056                  })
16057             }
16058         }
16059         
16060         if (!this.parent.el) {
16061                 // probably an old style ctor, which has been disabled.
16062                 return;
16063
16064         }
16065                 // The 'tree' method is  '_tree now' 
16066             
16067         tree.region = tree.region || this.region;
16068         
16069         if (this.parent.el === true) {
16070             // bootstrap... - body..
16071             this.parent.el = Roo.factory(tree);
16072         }
16073         
16074         this.el = this.parent.el.addxtype(tree);
16075         this.fireEvent('built', this);
16076         
16077         this.panel = this.el;
16078         this.layout = this.panel.layout;
16079         this.parentLayout = this.parent.layout  || false;  
16080          
16081     }
16082     
16083 });
16084
16085 Roo.apply(Roo.XComponent, {
16086     /**
16087      * @property  hideProgress
16088      * true to disable the building progress bar.. usefull on single page renders.
16089      * @type Boolean
16090      */
16091     hideProgress : false,
16092     /**
16093      * @property  buildCompleted
16094      * True when the builder has completed building the interface.
16095      * @type Boolean
16096      */
16097     buildCompleted : false,
16098      
16099     /**
16100      * @property  topModule
16101      * the upper most module - uses document.element as it's constructor.
16102      * @type Object
16103      */
16104      
16105     topModule  : false,
16106       
16107     /**
16108      * @property  modules
16109      * array of modules to be created by registration system.
16110      * @type {Array} of Roo.XComponent
16111      */
16112     
16113     modules : [],
16114     /**
16115      * @property  elmodules
16116      * array of modules to be created by which use #ID 
16117      * @type {Array} of Roo.XComponent
16118      */
16119      
16120     elmodules : [],
16121
16122      /**
16123      * @property  build_from_html
16124      * Build elements from html - used by bootstrap HTML stuff 
16125      *    - this is cleared after build is completed
16126      * @type {boolean} true  (default false)
16127      */
16128      
16129     build_from_html : false,
16130
16131     /**
16132      * Register components to be built later.
16133      *
16134      * This solves the following issues
16135      * - Building is not done on page load, but after an authentication process has occured.
16136      * - Interface elements are registered on page load
16137      * - Parent Interface elements may not be loaded before child, so this handles that..
16138      * 
16139      *
16140      * example:
16141      * 
16142      * MyApp.register({
16143           order : '000001',
16144           module : 'Pman.Tab.projectMgr',
16145           region : 'center',
16146           parent : 'Pman.layout',
16147           disabled : false,  // or use a function..
16148         })
16149      
16150      * * @param {Object} details about module
16151      */
16152     register : function(obj) {
16153                 
16154         Roo.XComponent.event.fireEvent('register', obj);
16155         switch(typeof(obj.disabled) ) {
16156                 
16157             case 'undefined':
16158                 break;
16159             
16160             case 'function':
16161                 if ( obj.disabled() ) {
16162                         return;
16163                 }
16164                 break;
16165             
16166             default:
16167                 if (obj.disabled) {
16168                         return;
16169                 }
16170                 break;
16171         }
16172                 
16173         this.modules.push(obj);
16174          
16175     },
16176     /**
16177      * convert a string to an object..
16178      * eg. 'AAA.BBB' -> finds AAA.BBB
16179
16180      */
16181     
16182     toObject : function(str)
16183     {
16184         if (!str || typeof(str) == 'object') {
16185             return str;
16186         }
16187         if (str.substring(0,1) == '#') {
16188             return str;
16189         }
16190
16191         var ar = str.split('.');
16192         var rt, o;
16193         rt = ar.shift();
16194             /** eval:var:o */
16195         try {
16196             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16197         } catch (e) {
16198             throw "Module not found : " + str;
16199         }
16200         
16201         if (o === false) {
16202             throw "Module not found : " + str;
16203         }
16204         Roo.each(ar, function(e) {
16205             if (typeof(o[e]) == 'undefined') {
16206                 throw "Module not found : " + str;
16207             }
16208             o = o[e];
16209         });
16210         
16211         return o;
16212         
16213     },
16214     
16215     
16216     /**
16217      * move modules into their correct place in the tree..
16218      * 
16219      */
16220     preBuild : function ()
16221     {
16222         var _t = this;
16223         Roo.each(this.modules , function (obj)
16224         {
16225             Roo.XComponent.event.fireEvent('beforebuild', obj);
16226             
16227             var opar = obj.parent;
16228             try { 
16229                 obj.parent = this.toObject(opar);
16230             } catch(e) {
16231                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16232                 return;
16233             }
16234             
16235             if (!obj.parent) {
16236                 Roo.debug && Roo.log("GOT top level module");
16237                 Roo.debug && Roo.log(obj);
16238                 obj.modules = new Roo.util.MixedCollection(false, 
16239                     function(o) { return o.order + '' }
16240                 );
16241                 this.topModule = obj;
16242                 return;
16243             }
16244                         // parent is a string (usually a dom element name..)
16245             if (typeof(obj.parent) == 'string') {
16246                 this.elmodules.push(obj);
16247                 return;
16248             }
16249             if (obj.parent.constructor != Roo.XComponent) {
16250                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16251             }
16252             if (!obj.parent.modules) {
16253                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16254                     function(o) { return o.order + '' }
16255                 );
16256             }
16257             if (obj.parent.disabled) {
16258                 obj.disabled = true;
16259             }
16260             obj.parent.modules.add(obj);
16261         }, this);
16262     },
16263     
16264      /**
16265      * make a list of modules to build.
16266      * @return {Array} list of modules. 
16267      */ 
16268     
16269     buildOrder : function()
16270     {
16271         var _this = this;
16272         var cmp = function(a,b) {   
16273             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16274         };
16275         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16276             throw "No top level modules to build";
16277         }
16278         
16279         // make a flat list in order of modules to build.
16280         var mods = this.topModule ? [ this.topModule ] : [];
16281                 
16282         
16283         // elmodules (is a list of DOM based modules )
16284         Roo.each(this.elmodules, function(e) {
16285             mods.push(e);
16286             if (!this.topModule &&
16287                 typeof(e.parent) == 'string' &&
16288                 e.parent.substring(0,1) == '#' &&
16289                 Roo.get(e.parent.substr(1))
16290                ) {
16291                 
16292                 _this.topModule = e;
16293             }
16294             
16295         });
16296
16297         
16298         // add modules to their parents..
16299         var addMod = function(m) {
16300             Roo.debug && Roo.log("build Order: add: " + m.name);
16301                 
16302             mods.push(m);
16303             if (m.modules && !m.disabled) {
16304                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16305                 m.modules.keySort('ASC',  cmp );
16306                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16307     
16308                 m.modules.each(addMod);
16309             } else {
16310                 Roo.debug && Roo.log("build Order: no child modules");
16311             }
16312             // not sure if this is used any more..
16313             if (m.finalize) {
16314                 m.finalize.name = m.name + " (clean up) ";
16315                 mods.push(m.finalize);
16316             }
16317             
16318         }
16319         if (this.topModule && this.topModule.modules) { 
16320             this.topModule.modules.keySort('ASC',  cmp );
16321             this.topModule.modules.each(addMod);
16322         } 
16323         return mods;
16324     },
16325     
16326      /**
16327      * Build the registered modules.
16328      * @param {Object} parent element.
16329      * @param {Function} optional method to call after module has been added.
16330      * 
16331      */ 
16332    
16333     build : function(opts) 
16334     {
16335         
16336         if (typeof(opts) != 'undefined') {
16337             Roo.apply(this,opts);
16338         }
16339         
16340         this.preBuild();
16341         var mods = this.buildOrder();
16342       
16343         //this.allmods = mods;
16344         //Roo.debug && Roo.log(mods);
16345         //return;
16346         if (!mods.length) { // should not happen
16347             throw "NO modules!!!";
16348         }
16349         
16350         
16351         var msg = "Building Interface...";
16352         // flash it up as modal - so we store the mask!?
16353         if (!this.hideProgress && Roo.MessageBox) {
16354             Roo.MessageBox.show({ title: 'loading' });
16355             Roo.MessageBox.show({
16356                title: "Please wait...",
16357                msg: msg,
16358                width:450,
16359                progress:true,
16360                closable:false,
16361                modal: false
16362               
16363             });
16364         }
16365         var total = mods.length;
16366         
16367         var _this = this;
16368         var progressRun = function() {
16369             if (!mods.length) {
16370                 Roo.debug && Roo.log('hide?');
16371                 if (!this.hideProgress && Roo.MessageBox) {
16372                     Roo.MessageBox.hide();
16373                 }
16374                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16375                 
16376                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16377                 
16378                 // THE END...
16379                 return false;   
16380             }
16381             
16382             var m = mods.shift();
16383             
16384             
16385             Roo.debug && Roo.log(m);
16386             // not sure if this is supported any more.. - modules that are are just function
16387             if (typeof(m) == 'function') { 
16388                 m.call(this);
16389                 return progressRun.defer(10, _this);
16390             } 
16391             
16392             
16393             msg = "Building Interface " + (total  - mods.length) + 
16394                     " of " + total + 
16395                     (m.name ? (' - ' + m.name) : '');
16396                         Roo.debug && Roo.log(msg);
16397             if (!this.hideProgress &&  Roo.MessageBox) { 
16398                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16399             }
16400             
16401          
16402             // is the module disabled?
16403             var disabled = (typeof(m.disabled) == 'function') ?
16404                 m.disabled.call(m.module.disabled) : m.disabled;    
16405             
16406             
16407             if (disabled) {
16408                 return progressRun(); // we do not update the display!
16409             }
16410             
16411             // now build 
16412             
16413                         
16414                         
16415             m.render();
16416             // it's 10 on top level, and 1 on others??? why...
16417             return progressRun.defer(10, _this);
16418              
16419         }
16420         progressRun.defer(1, _this);
16421      
16422         
16423         
16424     },
16425         
16426         
16427         /**
16428          * Event Object.
16429          *
16430          *
16431          */
16432         event: false, 
16433     /**
16434          * wrapper for event.on - aliased later..  
16435          * Typically use to register a event handler for register:
16436          *
16437          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16438          *
16439          */
16440     on : false
16441    
16442     
16443     
16444 });
16445
16446 Roo.XComponent.event = new Roo.util.Observable({
16447                 events : { 
16448                         /**
16449                          * @event register
16450                          * Fires when an Component is registered,
16451                          * set the disable property on the Component to stop registration.
16452                          * @param {Roo.XComponent} c the component being registerd.
16453                          * 
16454                          */
16455                         'register' : true,
16456             /**
16457                          * @event beforebuild
16458                          * Fires before each Component is built
16459                          * can be used to apply permissions.
16460                          * @param {Roo.XComponent} c the component being registerd.
16461                          * 
16462                          */
16463                         'beforebuild' : true,
16464                         /**
16465                          * @event buildcomplete
16466                          * Fires on the top level element when all elements have been built
16467                          * @param {Roo.XComponent} the top level component.
16468                          */
16469                         'buildcomplete' : true
16470                         
16471                 }
16472 });
16473
16474 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16475  /*
16476  * Based on:
16477  * Ext JS Library 1.1.1
16478  * Copyright(c) 2006-2007, Ext JS, LLC.
16479  *
16480  * Originally Released Under LGPL - original licence link has changed is not relivant.
16481  *
16482  * Fork - LGPL
16483  * <script type="text/javascript">
16484  */
16485
16486
16487
16488 /*
16489  * These classes are derivatives of the similarly named classes in the YUI Library.
16490  * The original license:
16491  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16492  * Code licensed under the BSD License:
16493  * http://developer.yahoo.net/yui/license.txt
16494  */
16495
16496 (function() {
16497
16498 var Event=Roo.EventManager;
16499 var Dom=Roo.lib.Dom;
16500
16501 /**
16502  * @class Roo.dd.DragDrop
16503  * @extends Roo.util.Observable
16504  * Defines the interface and base operation of items that that can be
16505  * dragged or can be drop targets.  It was designed to be extended, overriding
16506  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16507  * Up to three html elements can be associated with a DragDrop instance:
16508  * <ul>
16509  * <li>linked element: the element that is passed into the constructor.
16510  * This is the element which defines the boundaries for interaction with
16511  * other DragDrop objects.</li>
16512  * <li>handle element(s): The drag operation only occurs if the element that
16513  * was clicked matches a handle element.  By default this is the linked
16514  * element, but there are times that you will want only a portion of the
16515  * linked element to initiate the drag operation, and the setHandleElId()
16516  * method provides a way to define this.</li>
16517  * <li>drag element: this represents the element that would be moved along
16518  * with the cursor during a drag operation.  By default, this is the linked
16519  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16520  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16521  * </li>
16522  * </ul>
16523  * This class should not be instantiated until the onload event to ensure that
16524  * the associated elements are available.
16525  * The following would define a DragDrop obj that would interact with any
16526  * other DragDrop obj in the "group1" group:
16527  * <pre>
16528  *  dd = new Roo.dd.DragDrop("div1", "group1");
16529  * </pre>
16530  * Since none of the event handlers have been implemented, nothing would
16531  * actually happen if you were to run the code above.  Normally you would
16532  * override this class or one of the default implementations, but you can
16533  * also override the methods you want on an instance of the class...
16534  * <pre>
16535  *  dd.onDragDrop = function(e, id) {
16536  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16537  *  }
16538  * </pre>
16539  * @constructor
16540  * @param {String} id of the element that is linked to this instance
16541  * @param {String} sGroup the group of related DragDrop objects
16542  * @param {object} config an object containing configurable attributes
16543  *                Valid properties for DragDrop:
16544  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16545  */
16546 Roo.dd.DragDrop = function(id, sGroup, config) {
16547     if (id) {
16548         this.init(id, sGroup, config);
16549     }
16550     
16551 };
16552
16553 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16554
16555     /**
16556      * The id of the element associated with this object.  This is what we
16557      * refer to as the "linked element" because the size and position of
16558      * this element is used to determine when the drag and drop objects have
16559      * interacted.
16560      * @property id
16561      * @type String
16562      */
16563     id: null,
16564
16565     /**
16566      * Configuration attributes passed into the constructor
16567      * @property config
16568      * @type object
16569      */
16570     config: null,
16571
16572     /**
16573      * The id of the element that will be dragged.  By default this is same
16574      * as the linked element , but could be changed to another element. Ex:
16575      * Roo.dd.DDProxy
16576      * @property dragElId
16577      * @type String
16578      * @private
16579      */
16580     dragElId: null,
16581
16582     /**
16583      * the id of the element that initiates the drag operation.  By default
16584      * this is the linked element, but could be changed to be a child of this
16585      * element.  This lets us do things like only starting the drag when the
16586      * header element within the linked html element is clicked.
16587      * @property handleElId
16588      * @type String
16589      * @private
16590      */
16591     handleElId: null,
16592
16593     /**
16594      * An associative array of HTML tags that will be ignored if clicked.
16595      * @property invalidHandleTypes
16596      * @type {string: string}
16597      */
16598     invalidHandleTypes: null,
16599
16600     /**
16601      * An associative array of ids for elements that will be ignored if clicked
16602      * @property invalidHandleIds
16603      * @type {string: string}
16604      */
16605     invalidHandleIds: null,
16606
16607     /**
16608      * An indexted array of css class names for elements that will be ignored
16609      * if clicked.
16610      * @property invalidHandleClasses
16611      * @type string[]
16612      */
16613     invalidHandleClasses: null,
16614
16615     /**
16616      * The linked element's absolute X position at the time the drag was
16617      * started
16618      * @property startPageX
16619      * @type int
16620      * @private
16621      */
16622     startPageX: 0,
16623
16624     /**
16625      * The linked element's absolute X position at the time the drag was
16626      * started
16627      * @property startPageY
16628      * @type int
16629      * @private
16630      */
16631     startPageY: 0,
16632
16633     /**
16634      * The group defines a logical collection of DragDrop objects that are
16635      * related.  Instances only get events when interacting with other
16636      * DragDrop object in the same group.  This lets us define multiple
16637      * groups using a single DragDrop subclass if we want.
16638      * @property groups
16639      * @type {string: string}
16640      */
16641     groups: null,
16642
16643     /**
16644      * Individual drag/drop instances can be locked.  This will prevent
16645      * onmousedown start drag.
16646      * @property locked
16647      * @type boolean
16648      * @private
16649      */
16650     locked: false,
16651
16652     /**
16653      * Lock this instance
16654      * @method lock
16655      */
16656     lock: function() { this.locked = true; },
16657
16658     /**
16659      * Unlock this instace
16660      * @method unlock
16661      */
16662     unlock: function() { this.locked = false; },
16663
16664     /**
16665      * By default, all insances can be a drop target.  This can be disabled by
16666      * setting isTarget to false.
16667      * @method isTarget
16668      * @type boolean
16669      */
16670     isTarget: true,
16671
16672     /**
16673      * The padding configured for this drag and drop object for calculating
16674      * the drop zone intersection with this object.
16675      * @method padding
16676      * @type int[]
16677      */
16678     padding: null,
16679
16680     /**
16681      * Cached reference to the linked element
16682      * @property _domRef
16683      * @private
16684      */
16685     _domRef: null,
16686
16687     /**
16688      * Internal typeof flag
16689      * @property __ygDragDrop
16690      * @private
16691      */
16692     __ygDragDrop: true,
16693
16694     /**
16695      * Set to true when horizontal contraints are applied
16696      * @property constrainX
16697      * @type boolean
16698      * @private
16699      */
16700     constrainX: false,
16701
16702     /**
16703      * Set to true when vertical contraints are applied
16704      * @property constrainY
16705      * @type boolean
16706      * @private
16707      */
16708     constrainY: false,
16709
16710     /**
16711      * The left constraint
16712      * @property minX
16713      * @type int
16714      * @private
16715      */
16716     minX: 0,
16717
16718     /**
16719      * The right constraint
16720      * @property maxX
16721      * @type int
16722      * @private
16723      */
16724     maxX: 0,
16725
16726     /**
16727      * The up constraint
16728      * @property minY
16729      * @type int
16730      * @type int
16731      * @private
16732      */
16733     minY: 0,
16734
16735     /**
16736      * The down constraint
16737      * @property maxY
16738      * @type int
16739      * @private
16740      */
16741     maxY: 0,
16742
16743     /**
16744      * Maintain offsets when we resetconstraints.  Set to true when you want
16745      * the position of the element relative to its parent to stay the same
16746      * when the page changes
16747      *
16748      * @property maintainOffset
16749      * @type boolean
16750      */
16751     maintainOffset: false,
16752
16753     /**
16754      * Array of pixel locations the element will snap to if we specified a
16755      * horizontal graduation/interval.  This array is generated automatically
16756      * when you define a tick interval.
16757      * @property xTicks
16758      * @type int[]
16759      */
16760     xTicks: null,
16761
16762     /**
16763      * Array of pixel locations the element will snap to if we specified a
16764      * vertical graduation/interval.  This array is generated automatically
16765      * when you define a tick interval.
16766      * @property yTicks
16767      * @type int[]
16768      */
16769     yTicks: null,
16770
16771     /**
16772      * By default the drag and drop instance will only respond to the primary
16773      * button click (left button for a right-handed mouse).  Set to true to
16774      * allow drag and drop to start with any mouse click that is propogated
16775      * by the browser
16776      * @property primaryButtonOnly
16777      * @type boolean
16778      */
16779     primaryButtonOnly: true,
16780
16781     /**
16782      * The availabe property is false until the linked dom element is accessible.
16783      * @property available
16784      * @type boolean
16785      */
16786     available: false,
16787
16788     /**
16789      * By default, drags can only be initiated if the mousedown occurs in the
16790      * region the linked element is.  This is done in part to work around a
16791      * bug in some browsers that mis-report the mousedown if the previous
16792      * mouseup happened outside of the window.  This property is set to true
16793      * if outer handles are defined.
16794      *
16795      * @property hasOuterHandles
16796      * @type boolean
16797      * @default false
16798      */
16799     hasOuterHandles: false,
16800
16801     /**
16802      * Code that executes immediately before the startDrag event
16803      * @method b4StartDrag
16804      * @private
16805      */
16806     b4StartDrag: function(x, y) { },
16807
16808     /**
16809      * Abstract method called after a drag/drop object is clicked
16810      * and the drag or mousedown time thresholds have beeen met.
16811      * @method startDrag
16812      * @param {int} X click location
16813      * @param {int} Y click location
16814      */
16815     startDrag: function(x, y) { /* override this */ },
16816
16817     /**
16818      * Code that executes immediately before the onDrag event
16819      * @method b4Drag
16820      * @private
16821      */
16822     b4Drag: function(e) { },
16823
16824     /**
16825      * Abstract method called during the onMouseMove event while dragging an
16826      * object.
16827      * @method onDrag
16828      * @param {Event} e the mousemove event
16829      */
16830     onDrag: function(e) { /* override this */ },
16831
16832     /**
16833      * Abstract method called when this element fist begins hovering over
16834      * another DragDrop obj
16835      * @method onDragEnter
16836      * @param {Event} e the mousemove event
16837      * @param {String|DragDrop[]} id In POINT mode, the element
16838      * id this is hovering over.  In INTERSECT mode, an array of one or more
16839      * dragdrop items being hovered over.
16840      */
16841     onDragEnter: function(e, id) { /* override this */ },
16842
16843     /**
16844      * Code that executes immediately before the onDragOver event
16845      * @method b4DragOver
16846      * @private
16847      */
16848     b4DragOver: function(e) { },
16849
16850     /**
16851      * Abstract method called when this element is hovering over another
16852      * DragDrop obj
16853      * @method onDragOver
16854      * @param {Event} e the mousemove event
16855      * @param {String|DragDrop[]} id In POINT mode, the element
16856      * id this is hovering over.  In INTERSECT mode, an array of dd items
16857      * being hovered over.
16858      */
16859     onDragOver: function(e, id) { /* override this */ },
16860
16861     /**
16862      * Code that executes immediately before the onDragOut event
16863      * @method b4DragOut
16864      * @private
16865      */
16866     b4DragOut: function(e) { },
16867
16868     /**
16869      * Abstract method called when we are no longer hovering over an element
16870      * @method onDragOut
16871      * @param {Event} e the mousemove event
16872      * @param {String|DragDrop[]} id In POINT mode, the element
16873      * id this was hovering over.  In INTERSECT mode, an array of dd items
16874      * that the mouse is no longer over.
16875      */
16876     onDragOut: function(e, id) { /* override this */ },
16877
16878     /**
16879      * Code that executes immediately before the onDragDrop event
16880      * @method b4DragDrop
16881      * @private
16882      */
16883     b4DragDrop: function(e) { },
16884
16885     /**
16886      * Abstract method called when this item is dropped on another DragDrop
16887      * obj
16888      * @method onDragDrop
16889      * @param {Event} e the mouseup event
16890      * @param {String|DragDrop[]} id In POINT mode, the element
16891      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16892      * was dropped on.
16893      */
16894     onDragDrop: function(e, id) { /* override this */ },
16895
16896     /**
16897      * Abstract method called when this item is dropped on an area with no
16898      * drop target
16899      * @method onInvalidDrop
16900      * @param {Event} e the mouseup event
16901      */
16902     onInvalidDrop: function(e) { /* override this */ },
16903
16904     /**
16905      * Code that executes immediately before the endDrag event
16906      * @method b4EndDrag
16907      * @private
16908      */
16909     b4EndDrag: function(e) { },
16910
16911     /**
16912      * Fired when we are done dragging the object
16913      * @method endDrag
16914      * @param {Event} e the mouseup event
16915      */
16916     endDrag: function(e) { /* override this */ },
16917
16918     /**
16919      * Code executed immediately before the onMouseDown event
16920      * @method b4MouseDown
16921      * @param {Event} e the mousedown event
16922      * @private
16923      */
16924     b4MouseDown: function(e) {  },
16925
16926     /**
16927      * Event handler that fires when a drag/drop obj gets a mousedown
16928      * @method onMouseDown
16929      * @param {Event} e the mousedown event
16930      */
16931     onMouseDown: function(e) { /* override this */ },
16932
16933     /**
16934      * Event handler that fires when a drag/drop obj gets a mouseup
16935      * @method onMouseUp
16936      * @param {Event} e the mouseup event
16937      */
16938     onMouseUp: function(e) { /* override this */ },
16939
16940     /**
16941      * Override the onAvailable method to do what is needed after the initial
16942      * position was determined.
16943      * @method onAvailable
16944      */
16945     onAvailable: function () {
16946     },
16947
16948     /*
16949      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16950      * @type Object
16951      */
16952     defaultPadding : {left:0, right:0, top:0, bottom:0},
16953
16954     /*
16955      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16956  *
16957  * Usage:
16958  <pre><code>
16959  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16960                 { dragElId: "existingProxyDiv" });
16961  dd.startDrag = function(){
16962      this.constrainTo("parent-id");
16963  };
16964  </code></pre>
16965  * Or you can initalize it using the {@link Roo.Element} object:
16966  <pre><code>
16967  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16968      startDrag : function(){
16969          this.constrainTo("parent-id");
16970      }
16971  });
16972  </code></pre>
16973      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16974      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16975      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16976      * an object containing the sides to pad. For example: {right:10, bottom:10}
16977      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16978      */
16979     constrainTo : function(constrainTo, pad, inContent){
16980         if(typeof pad == "number"){
16981             pad = {left: pad, right:pad, top:pad, bottom:pad};
16982         }
16983         pad = pad || this.defaultPadding;
16984         var b = Roo.get(this.getEl()).getBox();
16985         var ce = Roo.get(constrainTo);
16986         var s = ce.getScroll();
16987         var c, cd = ce.dom;
16988         if(cd == document.body){
16989             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16990         }else{
16991             xy = ce.getXY();
16992             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16993         }
16994
16995
16996         var topSpace = b.y - c.y;
16997         var leftSpace = b.x - c.x;
16998
16999         this.resetConstraints();
17000         this.setXConstraint(leftSpace - (pad.left||0), // left
17001                 c.width - leftSpace - b.width - (pad.right||0) //right
17002         );
17003         this.setYConstraint(topSpace - (pad.top||0), //top
17004                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
17005         );
17006     },
17007
17008     /**
17009      * Returns a reference to the linked element
17010      * @method getEl
17011      * @return {HTMLElement} the html element
17012      */
17013     getEl: function() {
17014         if (!this._domRef) {
17015             this._domRef = Roo.getDom(this.id);
17016         }
17017
17018         return this._domRef;
17019     },
17020
17021     /**
17022      * Returns a reference to the actual element to drag.  By default this is
17023      * the same as the html element, but it can be assigned to another
17024      * element. An example of this can be found in Roo.dd.DDProxy
17025      * @method getDragEl
17026      * @return {HTMLElement} the html element
17027      */
17028     getDragEl: function() {
17029         return Roo.getDom(this.dragElId);
17030     },
17031
17032     /**
17033      * Sets up the DragDrop object.  Must be called in the constructor of any
17034      * Roo.dd.DragDrop subclass
17035      * @method init
17036      * @param id the id of the linked element
17037      * @param {String} sGroup the group of related items
17038      * @param {object} config configuration attributes
17039      */
17040     init: function(id, sGroup, config) {
17041         this.initTarget(id, sGroup, config);
17042         if (!Roo.isTouch) {
17043             Event.on(this.id, "mousedown", this.handleMouseDown, this);
17044         }
17045         Event.on(this.id, "touchstart", this.handleMouseDown, this);
17046         // Event.on(this.id, "selectstart", Event.preventDefault);
17047     },
17048
17049     /**
17050      * Initializes Targeting functionality only... the object does not
17051      * get a mousedown handler.
17052      * @method initTarget
17053      * @param id the id of the linked element
17054      * @param {String} sGroup the group of related items
17055      * @param {object} config configuration attributes
17056      */
17057     initTarget: function(id, sGroup, config) {
17058
17059         // configuration attributes
17060         this.config = config || {};
17061
17062         // create a local reference to the drag and drop manager
17063         this.DDM = Roo.dd.DDM;
17064         // initialize the groups array
17065         this.groups = {};
17066
17067         // assume that we have an element reference instead of an id if the
17068         // parameter is not a string
17069         if (typeof id !== "string") {
17070             id = Roo.id(id);
17071         }
17072
17073         // set the id
17074         this.id = id;
17075
17076         // add to an interaction group
17077         this.addToGroup((sGroup) ? sGroup : "default");
17078
17079         // We don't want to register this as the handle with the manager
17080         // so we just set the id rather than calling the setter.
17081         this.handleElId = id;
17082
17083         // the linked element is the element that gets dragged by default
17084         this.setDragElId(id);
17085
17086         // by default, clicked anchors will not start drag operations.
17087         this.invalidHandleTypes = { A: "A" };
17088         this.invalidHandleIds = {};
17089         this.invalidHandleClasses = [];
17090
17091         this.applyConfig();
17092
17093         this.handleOnAvailable();
17094     },
17095
17096     /**
17097      * Applies the configuration parameters that were passed into the constructor.
17098      * This is supposed to happen at each level through the inheritance chain.  So
17099      * a DDProxy implentation will execute apply config on DDProxy, DD, and
17100      * DragDrop in order to get all of the parameters that are available in
17101      * each object.
17102      * @method applyConfig
17103      */
17104     applyConfig: function() {
17105
17106         // configurable properties:
17107         //    padding, isTarget, maintainOffset, primaryButtonOnly
17108         this.padding           = this.config.padding || [0, 0, 0, 0];
17109         this.isTarget          = (this.config.isTarget !== false);
17110         this.maintainOffset    = (this.config.maintainOffset);
17111         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
17112
17113     },
17114
17115     /**
17116      * Executed when the linked element is available
17117      * @method handleOnAvailable
17118      * @private
17119      */
17120     handleOnAvailable: function() {
17121         this.available = true;
17122         this.resetConstraints();
17123         this.onAvailable();
17124     },
17125
17126      /**
17127      * Configures the padding for the target zone in px.  Effectively expands
17128      * (or reduces) the virtual object size for targeting calculations.
17129      * Supports css-style shorthand; if only one parameter is passed, all sides
17130      * will have that padding, and if only two are passed, the top and bottom
17131      * will have the first param, the left and right the second.
17132      * @method setPadding
17133      * @param {int} iTop    Top pad
17134      * @param {int} iRight  Right pad
17135      * @param {int} iBot    Bot pad
17136      * @param {int} iLeft   Left pad
17137      */
17138     setPadding: function(iTop, iRight, iBot, iLeft) {
17139         // this.padding = [iLeft, iRight, iTop, iBot];
17140         if (!iRight && 0 !== iRight) {
17141             this.padding = [iTop, iTop, iTop, iTop];
17142         } else if (!iBot && 0 !== iBot) {
17143             this.padding = [iTop, iRight, iTop, iRight];
17144         } else {
17145             this.padding = [iTop, iRight, iBot, iLeft];
17146         }
17147     },
17148
17149     /**
17150      * Stores the initial placement of the linked element.
17151      * @method setInitialPosition
17152      * @param {int} diffX   the X offset, default 0
17153      * @param {int} diffY   the Y offset, default 0
17154      */
17155     setInitPosition: function(diffX, diffY) {
17156         var el = this.getEl();
17157
17158         if (!this.DDM.verifyEl(el)) {
17159             return;
17160         }
17161
17162         var dx = diffX || 0;
17163         var dy = diffY || 0;
17164
17165         var p = Dom.getXY( el );
17166
17167         this.initPageX = p[0] - dx;
17168         this.initPageY = p[1] - dy;
17169
17170         this.lastPageX = p[0];
17171         this.lastPageY = p[1];
17172
17173
17174         this.setStartPosition(p);
17175     },
17176
17177     /**
17178      * Sets the start position of the element.  This is set when the obj
17179      * is initialized, the reset when a drag is started.
17180      * @method setStartPosition
17181      * @param pos current position (from previous lookup)
17182      * @private
17183      */
17184     setStartPosition: function(pos) {
17185         var p = pos || Dom.getXY( this.getEl() );
17186         this.deltaSetXY = null;
17187
17188         this.startPageX = p[0];
17189         this.startPageY = p[1];
17190     },
17191
17192     /**
17193      * Add this instance to a group of related drag/drop objects.  All
17194      * instances belong to at least one group, and can belong to as many
17195      * groups as needed.
17196      * @method addToGroup
17197      * @param sGroup {string} the name of the group
17198      */
17199     addToGroup: function(sGroup) {
17200         this.groups[sGroup] = true;
17201         this.DDM.regDragDrop(this, sGroup);
17202     },
17203
17204     /**
17205      * Remove's this instance from the supplied interaction group
17206      * @method removeFromGroup
17207      * @param {string}  sGroup  The group to drop
17208      */
17209     removeFromGroup: function(sGroup) {
17210         if (this.groups[sGroup]) {
17211             delete this.groups[sGroup];
17212         }
17213
17214         this.DDM.removeDDFromGroup(this, sGroup);
17215     },
17216
17217     /**
17218      * Allows you to specify that an element other than the linked element
17219      * will be moved with the cursor during a drag
17220      * @method setDragElId
17221      * @param id {string} the id of the element that will be used to initiate the drag
17222      */
17223     setDragElId: function(id) {
17224         this.dragElId = id;
17225     },
17226
17227     /**
17228      * Allows you to specify a child of the linked element that should be
17229      * used to initiate the drag operation.  An example of this would be if
17230      * you have a content div with text and links.  Clicking anywhere in the
17231      * content area would normally start the drag operation.  Use this method
17232      * to specify that an element inside of the content div is the element
17233      * that starts the drag operation.
17234      * @method setHandleElId
17235      * @param id {string} the id of the element that will be used to
17236      * initiate the drag.
17237      */
17238     setHandleElId: function(id) {
17239         if (typeof id !== "string") {
17240             id = Roo.id(id);
17241         }
17242         this.handleElId = id;
17243         this.DDM.regHandle(this.id, id);
17244     },
17245
17246     /**
17247      * Allows you to set an element outside of the linked element as a drag
17248      * handle
17249      * @method setOuterHandleElId
17250      * @param id the id of the element that will be used to initiate the drag
17251      */
17252     setOuterHandleElId: function(id) {
17253         if (typeof id !== "string") {
17254             id = Roo.id(id);
17255         }
17256         Event.on(id, "mousedown",
17257                 this.handleMouseDown, this);
17258         this.setHandleElId(id);
17259
17260         this.hasOuterHandles = true;
17261     },
17262
17263     /**
17264      * Remove all drag and drop hooks for this element
17265      * @method unreg
17266      */
17267     unreg: function() {
17268         Event.un(this.id, "mousedown",
17269                 this.handleMouseDown);
17270         Event.un(this.id, "touchstart",
17271                 this.handleMouseDown);
17272         this._domRef = null;
17273         this.DDM._remove(this);
17274     },
17275
17276     destroy : function(){
17277         this.unreg();
17278     },
17279
17280     /**
17281      * Returns true if this instance is locked, or the drag drop mgr is locked
17282      * (meaning that all drag/drop is disabled on the page.)
17283      * @method isLocked
17284      * @return {boolean} true if this obj or all drag/drop is locked, else
17285      * false
17286      */
17287     isLocked: function() {
17288         return (this.DDM.isLocked() || this.locked);
17289     },
17290
17291     /**
17292      * Fired when this object is clicked
17293      * @method handleMouseDown
17294      * @param {Event} e
17295      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17296      * @private
17297      */
17298     handleMouseDown: function(e, oDD){
17299      
17300         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17301             //Roo.log('not touch/ button !=0');
17302             return;
17303         }
17304         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17305             return; // double touch..
17306         }
17307         
17308
17309         if (this.isLocked()) {
17310             //Roo.log('locked');
17311             return;
17312         }
17313
17314         this.DDM.refreshCache(this.groups);
17315 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17316         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17317         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17318             //Roo.log('no outer handes or not over target');
17319                 // do nothing.
17320         } else {
17321 //            Roo.log('check validator');
17322             if (this.clickValidator(e)) {
17323 //                Roo.log('validate success');
17324                 // set the initial element position
17325                 this.setStartPosition();
17326
17327
17328                 this.b4MouseDown(e);
17329                 this.onMouseDown(e);
17330
17331                 this.DDM.handleMouseDown(e, this);
17332
17333                 this.DDM.stopEvent(e);
17334             } else {
17335
17336
17337             }
17338         }
17339     },
17340
17341     clickValidator: function(e) {
17342         var target = e.getTarget();
17343         return ( this.isValidHandleChild(target) &&
17344                     (this.id == this.handleElId ||
17345                         this.DDM.handleWasClicked(target, this.id)) );
17346     },
17347
17348     /**
17349      * Allows you to specify a tag name that should not start a drag operation
17350      * when clicked.  This is designed to facilitate embedding links within a
17351      * drag handle that do something other than start the drag.
17352      * @method addInvalidHandleType
17353      * @param {string} tagName the type of element to exclude
17354      */
17355     addInvalidHandleType: function(tagName) {
17356         var type = tagName.toUpperCase();
17357         this.invalidHandleTypes[type] = type;
17358     },
17359
17360     /**
17361      * Lets you to specify an element id for a child of a drag handle
17362      * that should not initiate a drag
17363      * @method addInvalidHandleId
17364      * @param {string} id the element id of the element you wish to ignore
17365      */
17366     addInvalidHandleId: function(id) {
17367         if (typeof id !== "string") {
17368             id = Roo.id(id);
17369         }
17370         this.invalidHandleIds[id] = id;
17371     },
17372
17373     /**
17374      * Lets you specify a css class of elements that will not initiate a drag
17375      * @method addInvalidHandleClass
17376      * @param {string} cssClass the class of the elements you wish to ignore
17377      */
17378     addInvalidHandleClass: function(cssClass) {
17379         this.invalidHandleClasses.push(cssClass);
17380     },
17381
17382     /**
17383      * Unsets an excluded tag name set by addInvalidHandleType
17384      * @method removeInvalidHandleType
17385      * @param {string} tagName the type of element to unexclude
17386      */
17387     removeInvalidHandleType: function(tagName) {
17388         var type = tagName.toUpperCase();
17389         // this.invalidHandleTypes[type] = null;
17390         delete this.invalidHandleTypes[type];
17391     },
17392
17393     /**
17394      * Unsets an invalid handle id
17395      * @method removeInvalidHandleId
17396      * @param {string} id the id of the element to re-enable
17397      */
17398     removeInvalidHandleId: function(id) {
17399         if (typeof id !== "string") {
17400             id = Roo.id(id);
17401         }
17402         delete this.invalidHandleIds[id];
17403     },
17404
17405     /**
17406      * Unsets an invalid css class
17407      * @method removeInvalidHandleClass
17408      * @param {string} cssClass the class of the element(s) you wish to
17409      * re-enable
17410      */
17411     removeInvalidHandleClass: function(cssClass) {
17412         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17413             if (this.invalidHandleClasses[i] == cssClass) {
17414                 delete this.invalidHandleClasses[i];
17415             }
17416         }
17417     },
17418
17419     /**
17420      * Checks the tag exclusion list to see if this click should be ignored
17421      * @method isValidHandleChild
17422      * @param {HTMLElement} node the HTMLElement to evaluate
17423      * @return {boolean} true if this is a valid tag type, false if not
17424      */
17425     isValidHandleChild: function(node) {
17426
17427         var valid = true;
17428         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17429         var nodeName;
17430         try {
17431             nodeName = node.nodeName.toUpperCase();
17432         } catch(e) {
17433             nodeName = node.nodeName;
17434         }
17435         valid = valid && !this.invalidHandleTypes[nodeName];
17436         valid = valid && !this.invalidHandleIds[node.id];
17437
17438         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17439             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17440         }
17441
17442
17443         return valid;
17444
17445     },
17446
17447     /**
17448      * Create the array of horizontal tick marks if an interval was specified
17449      * in setXConstraint().
17450      * @method setXTicks
17451      * @private
17452      */
17453     setXTicks: function(iStartX, iTickSize) {
17454         this.xTicks = [];
17455         this.xTickSize = iTickSize;
17456
17457         var tickMap = {};
17458
17459         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17460             if (!tickMap[i]) {
17461                 this.xTicks[this.xTicks.length] = i;
17462                 tickMap[i] = true;
17463             }
17464         }
17465
17466         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17467             if (!tickMap[i]) {
17468                 this.xTicks[this.xTicks.length] = i;
17469                 tickMap[i] = true;
17470             }
17471         }
17472
17473         this.xTicks.sort(this.DDM.numericSort) ;
17474     },
17475
17476     /**
17477      * Create the array of vertical tick marks if an interval was specified in
17478      * setYConstraint().
17479      * @method setYTicks
17480      * @private
17481      */
17482     setYTicks: function(iStartY, iTickSize) {
17483         this.yTicks = [];
17484         this.yTickSize = iTickSize;
17485
17486         var tickMap = {};
17487
17488         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17489             if (!tickMap[i]) {
17490                 this.yTicks[this.yTicks.length] = i;
17491                 tickMap[i] = true;
17492             }
17493         }
17494
17495         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17496             if (!tickMap[i]) {
17497                 this.yTicks[this.yTicks.length] = i;
17498                 tickMap[i] = true;
17499             }
17500         }
17501
17502         this.yTicks.sort(this.DDM.numericSort) ;
17503     },
17504
17505     /**
17506      * By default, the element can be dragged any place on the screen.  Use
17507      * this method to limit the horizontal travel of the element.  Pass in
17508      * 0,0 for the parameters if you want to lock the drag to the y axis.
17509      * @method setXConstraint
17510      * @param {int} iLeft the number of pixels the element can move to the left
17511      * @param {int} iRight the number of pixels the element can move to the
17512      * right
17513      * @param {int} iTickSize optional parameter for specifying that the
17514      * element
17515      * should move iTickSize pixels at a time.
17516      */
17517     setXConstraint: function(iLeft, iRight, iTickSize) {
17518         this.leftConstraint = iLeft;
17519         this.rightConstraint = iRight;
17520
17521         this.minX = this.initPageX - iLeft;
17522         this.maxX = this.initPageX + iRight;
17523         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17524
17525         this.constrainX = true;
17526     },
17527
17528     /**
17529      * Clears any constraints applied to this instance.  Also clears ticks
17530      * since they can't exist independent of a constraint at this time.
17531      * @method clearConstraints
17532      */
17533     clearConstraints: function() {
17534         this.constrainX = false;
17535         this.constrainY = false;
17536         this.clearTicks();
17537     },
17538
17539     /**
17540      * Clears any tick interval defined for this instance
17541      * @method clearTicks
17542      */
17543     clearTicks: function() {
17544         this.xTicks = null;
17545         this.yTicks = null;
17546         this.xTickSize = 0;
17547         this.yTickSize = 0;
17548     },
17549
17550     /**
17551      * By default, the element can be dragged any place on the screen.  Set
17552      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17553      * parameters if you want to lock the drag to the x axis.
17554      * @method setYConstraint
17555      * @param {int} iUp the number of pixels the element can move up
17556      * @param {int} iDown the number of pixels the element can move down
17557      * @param {int} iTickSize optional parameter for specifying that the
17558      * element should move iTickSize pixels at a time.
17559      */
17560     setYConstraint: function(iUp, iDown, iTickSize) {
17561         this.topConstraint = iUp;
17562         this.bottomConstraint = iDown;
17563
17564         this.minY = this.initPageY - iUp;
17565         this.maxY = this.initPageY + iDown;
17566         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17567
17568         this.constrainY = true;
17569
17570     },
17571
17572     /**
17573      * resetConstraints must be called if you manually reposition a dd element.
17574      * @method resetConstraints
17575      * @param {boolean} maintainOffset
17576      */
17577     resetConstraints: function() {
17578
17579
17580         // Maintain offsets if necessary
17581         if (this.initPageX || this.initPageX === 0) {
17582             // figure out how much this thing has moved
17583             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17584             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17585
17586             this.setInitPosition(dx, dy);
17587
17588         // This is the first time we have detected the element's position
17589         } else {
17590             this.setInitPosition();
17591         }
17592
17593         if (this.constrainX) {
17594             this.setXConstraint( this.leftConstraint,
17595                                  this.rightConstraint,
17596                                  this.xTickSize        );
17597         }
17598
17599         if (this.constrainY) {
17600             this.setYConstraint( this.topConstraint,
17601                                  this.bottomConstraint,
17602                                  this.yTickSize         );
17603         }
17604     },
17605
17606     /**
17607      * Normally the drag element is moved pixel by pixel, but we can specify
17608      * that it move a number of pixels at a time.  This method resolves the
17609      * location when we have it set up like this.
17610      * @method getTick
17611      * @param {int} val where we want to place the object
17612      * @param {int[]} tickArray sorted array of valid points
17613      * @return {int} the closest tick
17614      * @private
17615      */
17616     getTick: function(val, tickArray) {
17617
17618         if (!tickArray) {
17619             // If tick interval is not defined, it is effectively 1 pixel,
17620             // so we return the value passed to us.
17621             return val;
17622         } else if (tickArray[0] >= val) {
17623             // The value is lower than the first tick, so we return the first
17624             // tick.
17625             return tickArray[0];
17626         } else {
17627             for (var i=0, len=tickArray.length; i<len; ++i) {
17628                 var next = i + 1;
17629                 if (tickArray[next] && tickArray[next] >= val) {
17630                     var diff1 = val - tickArray[i];
17631                     var diff2 = tickArray[next] - val;
17632                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17633                 }
17634             }
17635
17636             // The value is larger than the last tick, so we return the last
17637             // tick.
17638             return tickArray[tickArray.length - 1];
17639         }
17640     },
17641
17642     /**
17643      * toString method
17644      * @method toString
17645      * @return {string} string representation of the dd obj
17646      */
17647     toString: function() {
17648         return ("DragDrop " + this.id);
17649     }
17650
17651 });
17652
17653 })();
17654 /*
17655  * Based on:
17656  * Ext JS Library 1.1.1
17657  * Copyright(c) 2006-2007, Ext JS, LLC.
17658  *
17659  * Originally Released Under LGPL - original licence link has changed is not relivant.
17660  *
17661  * Fork - LGPL
17662  * <script type="text/javascript">
17663  */
17664
17665
17666 /**
17667  * The drag and drop utility provides a framework for building drag and drop
17668  * applications.  In addition to enabling drag and drop for specific elements,
17669  * the drag and drop elements are tracked by the manager class, and the
17670  * interactions between the various elements are tracked during the drag and
17671  * the implementing code is notified about these important moments.
17672  */
17673
17674 // Only load the library once.  Rewriting the manager class would orphan
17675 // existing drag and drop instances.
17676 if (!Roo.dd.DragDropMgr) {
17677
17678 /**
17679  * @class Roo.dd.DragDropMgr
17680  * DragDropMgr is a singleton that tracks the element interaction for
17681  * all DragDrop items in the window.  Generally, you will not call
17682  * this class directly, but it does have helper methods that could
17683  * be useful in your DragDrop implementations.
17684  * @singleton
17685  */
17686 Roo.dd.DragDropMgr = function() {
17687
17688     var Event = Roo.EventManager;
17689
17690     return {
17691
17692         /**
17693          * Two dimensional Array of registered DragDrop objects.  The first
17694          * dimension is the DragDrop item group, the second the DragDrop
17695          * object.
17696          * @property ids
17697          * @type {string: string}
17698          * @private
17699          * @static
17700          */
17701         ids: {},
17702
17703         /**
17704          * Array of element ids defined as drag handles.  Used to determine
17705          * if the element that generated the mousedown event is actually the
17706          * handle and not the html element itself.
17707          * @property handleIds
17708          * @type {string: string}
17709          * @private
17710          * @static
17711          */
17712         handleIds: {},
17713
17714         /**
17715          * the DragDrop object that is currently being dragged
17716          * @property dragCurrent
17717          * @type DragDrop
17718          * @private
17719          * @static
17720          **/
17721         dragCurrent: null,
17722
17723         /**
17724          * the DragDrop object(s) that are being hovered over
17725          * @property dragOvers
17726          * @type Array
17727          * @private
17728          * @static
17729          */
17730         dragOvers: {},
17731
17732         /**
17733          * the X distance between the cursor and the object being dragged
17734          * @property deltaX
17735          * @type int
17736          * @private
17737          * @static
17738          */
17739         deltaX: 0,
17740
17741         /**
17742          * the Y distance between the cursor and the object being dragged
17743          * @property deltaY
17744          * @type int
17745          * @private
17746          * @static
17747          */
17748         deltaY: 0,
17749
17750         /**
17751          * Flag to determine if we should prevent the default behavior of the
17752          * events we define. By default this is true, but this can be set to
17753          * false if you need the default behavior (not recommended)
17754          * @property preventDefault
17755          * @type boolean
17756          * @static
17757          */
17758         preventDefault: true,
17759
17760         /**
17761          * Flag to determine if we should stop the propagation of the events
17762          * we generate. This is true by default but you may want to set it to
17763          * false if the html element contains other features that require the
17764          * mouse click.
17765          * @property stopPropagation
17766          * @type boolean
17767          * @static
17768          */
17769         stopPropagation: true,
17770
17771         /**
17772          * Internal flag that is set to true when drag and drop has been
17773          * intialized
17774          * @property initialized
17775          * @private
17776          * @static
17777          */
17778         initalized: false,
17779
17780         /**
17781          * All drag and drop can be disabled.
17782          * @property locked
17783          * @private
17784          * @static
17785          */
17786         locked: false,
17787
17788         /**
17789          * Called the first time an element is registered.
17790          * @method init
17791          * @private
17792          * @static
17793          */
17794         init: function() {
17795             this.initialized = true;
17796         },
17797
17798         /**
17799          * In point mode, drag and drop interaction is defined by the
17800          * location of the cursor during the drag/drop
17801          * @property POINT
17802          * @type int
17803          * @static
17804          */
17805         POINT: 0,
17806
17807         /**
17808          * In intersect mode, drag and drop interactio nis defined by the
17809          * overlap of two or more drag and drop objects.
17810          * @property INTERSECT
17811          * @type int
17812          * @static
17813          */
17814         INTERSECT: 1,
17815
17816         /**
17817          * The current drag and drop mode.  Default: POINT
17818          * @property mode
17819          * @type int
17820          * @static
17821          */
17822         mode: 0,
17823
17824         /**
17825          * Runs method on all drag and drop objects
17826          * @method _execOnAll
17827          * @private
17828          * @static
17829          */
17830         _execOnAll: function(sMethod, args) {
17831             for (var i in this.ids) {
17832                 for (var j in this.ids[i]) {
17833                     var oDD = this.ids[i][j];
17834                     if (! this.isTypeOfDD(oDD)) {
17835                         continue;
17836                     }
17837                     oDD[sMethod].apply(oDD, args);
17838                 }
17839             }
17840         },
17841
17842         /**
17843          * Drag and drop initialization.  Sets up the global event handlers
17844          * @method _onLoad
17845          * @private
17846          * @static
17847          */
17848         _onLoad: function() {
17849
17850             this.init();
17851
17852             if (!Roo.isTouch) {
17853                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17854                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17855             }
17856             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17857             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17858             
17859             Event.on(window,   "unload",    this._onUnload, this, true);
17860             Event.on(window,   "resize",    this._onResize, this, true);
17861             // Event.on(window,   "mouseout",    this._test);
17862
17863         },
17864
17865         /**
17866          * Reset constraints on all drag and drop objs
17867          * @method _onResize
17868          * @private
17869          * @static
17870          */
17871         _onResize: function(e) {
17872             this._execOnAll("resetConstraints", []);
17873         },
17874
17875         /**
17876          * Lock all drag and drop functionality
17877          * @method lock
17878          * @static
17879          */
17880         lock: function() { this.locked = true; },
17881
17882         /**
17883          * Unlock all drag and drop functionality
17884          * @method unlock
17885          * @static
17886          */
17887         unlock: function() { this.locked = false; },
17888
17889         /**
17890          * Is drag and drop locked?
17891          * @method isLocked
17892          * @return {boolean} True if drag and drop is locked, false otherwise.
17893          * @static
17894          */
17895         isLocked: function() { return this.locked; },
17896
17897         /**
17898          * Location cache that is set for all drag drop objects when a drag is
17899          * initiated, cleared when the drag is finished.
17900          * @property locationCache
17901          * @private
17902          * @static
17903          */
17904         locationCache: {},
17905
17906         /**
17907          * Set useCache to false if you want to force object the lookup of each
17908          * drag and drop linked element constantly during a drag.
17909          * @property useCache
17910          * @type boolean
17911          * @static
17912          */
17913         useCache: true,
17914
17915         /**
17916          * The number of pixels that the mouse needs to move after the
17917          * mousedown before the drag is initiated.  Default=3;
17918          * @property clickPixelThresh
17919          * @type int
17920          * @static
17921          */
17922         clickPixelThresh: 3,
17923
17924         /**
17925          * The number of milliseconds after the mousedown event to initiate the
17926          * drag if we don't get a mouseup event. Default=1000
17927          * @property clickTimeThresh
17928          * @type int
17929          * @static
17930          */
17931         clickTimeThresh: 350,
17932
17933         /**
17934          * Flag that indicates that either the drag pixel threshold or the
17935          * mousdown time threshold has been met
17936          * @property dragThreshMet
17937          * @type boolean
17938          * @private
17939          * @static
17940          */
17941         dragThreshMet: false,
17942
17943         /**
17944          * Timeout used for the click time threshold
17945          * @property clickTimeout
17946          * @type Object
17947          * @private
17948          * @static
17949          */
17950         clickTimeout: null,
17951
17952         /**
17953          * The X position of the mousedown event stored for later use when a
17954          * drag threshold is met.
17955          * @property startX
17956          * @type int
17957          * @private
17958          * @static
17959          */
17960         startX: 0,
17961
17962         /**
17963          * The Y position of the mousedown event stored for later use when a
17964          * drag threshold is met.
17965          * @property startY
17966          * @type int
17967          * @private
17968          * @static
17969          */
17970         startY: 0,
17971
17972         /**
17973          * Each DragDrop instance must be registered with the DragDropMgr.
17974          * This is executed in DragDrop.init()
17975          * @method regDragDrop
17976          * @param {DragDrop} oDD the DragDrop object to register
17977          * @param {String} sGroup the name of the group this element belongs to
17978          * @static
17979          */
17980         regDragDrop: function(oDD, sGroup) {
17981             if (!this.initialized) { this.init(); }
17982
17983             if (!this.ids[sGroup]) {
17984                 this.ids[sGroup] = {};
17985             }
17986             this.ids[sGroup][oDD.id] = oDD;
17987         },
17988
17989         /**
17990          * Removes the supplied dd instance from the supplied group. Executed
17991          * by DragDrop.removeFromGroup, so don't call this function directly.
17992          * @method removeDDFromGroup
17993          * @private
17994          * @static
17995          */
17996         removeDDFromGroup: function(oDD, sGroup) {
17997             if (!this.ids[sGroup]) {
17998                 this.ids[sGroup] = {};
17999             }
18000
18001             var obj = this.ids[sGroup];
18002             if (obj && obj[oDD.id]) {
18003                 delete obj[oDD.id];
18004             }
18005         },
18006
18007         /**
18008          * Unregisters a drag and drop item.  This is executed in
18009          * DragDrop.unreg, use that method instead of calling this directly.
18010          * @method _remove
18011          * @private
18012          * @static
18013          */
18014         _remove: function(oDD) {
18015             for (var g in oDD.groups) {
18016                 if (g && this.ids[g][oDD.id]) {
18017                     delete this.ids[g][oDD.id];
18018                 }
18019             }
18020             delete this.handleIds[oDD.id];
18021         },
18022
18023         /**
18024          * Each DragDrop handle element must be registered.  This is done
18025          * automatically when executing DragDrop.setHandleElId()
18026          * @method regHandle
18027          * @param {String} sDDId the DragDrop id this element is a handle for
18028          * @param {String} sHandleId the id of the element that is the drag
18029          * handle
18030          * @static
18031          */
18032         regHandle: function(sDDId, sHandleId) {
18033             if (!this.handleIds[sDDId]) {
18034                 this.handleIds[sDDId] = {};
18035             }
18036             this.handleIds[sDDId][sHandleId] = sHandleId;
18037         },
18038
18039         /**
18040          * Utility function to determine if a given element has been
18041          * registered as a drag drop item.
18042          * @method isDragDrop
18043          * @param {String} id the element id to check
18044          * @return {boolean} true if this element is a DragDrop item,
18045          * false otherwise
18046          * @static
18047          */
18048         isDragDrop: function(id) {
18049             return ( this.getDDById(id) ) ? true : false;
18050         },
18051
18052         /**
18053          * Returns the drag and drop instances that are in all groups the
18054          * passed in instance belongs to.
18055          * @method getRelated
18056          * @param {DragDrop} p_oDD the obj to get related data for
18057          * @param {boolean} bTargetsOnly if true, only return targetable objs
18058          * @return {DragDrop[]} the related instances
18059          * @static
18060          */
18061         getRelated: function(p_oDD, bTargetsOnly) {
18062             var oDDs = [];
18063             for (var i in p_oDD.groups) {
18064                 for (j in this.ids[i]) {
18065                     var dd = this.ids[i][j];
18066                     if (! this.isTypeOfDD(dd)) {
18067                         continue;
18068                     }
18069                     if (!bTargetsOnly || dd.isTarget) {
18070                         oDDs[oDDs.length] = dd;
18071                     }
18072                 }
18073             }
18074
18075             return oDDs;
18076         },
18077
18078         /**
18079          * Returns true if the specified dd target is a legal target for
18080          * the specifice drag obj
18081          * @method isLegalTarget
18082          * @param {DragDrop} the drag obj
18083          * @param {DragDrop} the target
18084          * @return {boolean} true if the target is a legal target for the
18085          * dd obj
18086          * @static
18087          */
18088         isLegalTarget: function (oDD, oTargetDD) {
18089             var targets = this.getRelated(oDD, true);
18090             for (var i=0, len=targets.length;i<len;++i) {
18091                 if (targets[i].id == oTargetDD.id) {
18092                     return true;
18093                 }
18094             }
18095
18096             return false;
18097         },
18098
18099         /**
18100          * My goal is to be able to transparently determine if an object is
18101          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
18102          * returns "object", oDD.constructor.toString() always returns
18103          * "DragDrop" and not the name of the subclass.  So for now it just
18104          * evaluates a well-known variable in DragDrop.
18105          * @method isTypeOfDD
18106          * @param {Object} the object to evaluate
18107          * @return {boolean} true if typeof oDD = DragDrop
18108          * @static
18109          */
18110         isTypeOfDD: function (oDD) {
18111             return (oDD && oDD.__ygDragDrop);
18112         },
18113
18114         /**
18115          * Utility function to determine if a given element has been
18116          * registered as a drag drop handle for the given Drag Drop object.
18117          * @method isHandle
18118          * @param {String} id the element id to check
18119          * @return {boolean} true if this element is a DragDrop handle, false
18120          * otherwise
18121          * @static
18122          */
18123         isHandle: function(sDDId, sHandleId) {
18124             return ( this.handleIds[sDDId] &&
18125                             this.handleIds[sDDId][sHandleId] );
18126         },
18127
18128         /**
18129          * Returns the DragDrop instance for a given id
18130          * @method getDDById
18131          * @param {String} id the id of the DragDrop object
18132          * @return {DragDrop} the drag drop object, null if it is not found
18133          * @static
18134          */
18135         getDDById: function(id) {
18136             for (var i in this.ids) {
18137                 if (this.ids[i][id]) {
18138                     return this.ids[i][id];
18139                 }
18140             }
18141             return null;
18142         },
18143
18144         /**
18145          * Fired after a registered DragDrop object gets the mousedown event.
18146          * Sets up the events required to track the object being dragged
18147          * @method handleMouseDown
18148          * @param {Event} e the event
18149          * @param oDD the DragDrop object being dragged
18150          * @private
18151          * @static
18152          */
18153         handleMouseDown: function(e, oDD) {
18154             if(Roo.QuickTips){
18155                 Roo.QuickTips.disable();
18156             }
18157             this.currentTarget = e.getTarget();
18158
18159             this.dragCurrent = oDD;
18160
18161             var el = oDD.getEl();
18162
18163             // track start position
18164             this.startX = e.getPageX();
18165             this.startY = e.getPageY();
18166
18167             this.deltaX = this.startX - el.offsetLeft;
18168             this.deltaY = this.startY - el.offsetTop;
18169
18170             this.dragThreshMet = false;
18171
18172             this.clickTimeout = setTimeout(
18173                     function() {
18174                         var DDM = Roo.dd.DDM;
18175                         DDM.startDrag(DDM.startX, DDM.startY);
18176                     },
18177                     this.clickTimeThresh );
18178         },
18179
18180         /**
18181          * Fired when either the drag pixel threshol or the mousedown hold
18182          * time threshold has been met.
18183          * @method startDrag
18184          * @param x {int} the X position of the original mousedown
18185          * @param y {int} the Y position of the original mousedown
18186          * @static
18187          */
18188         startDrag: function(x, y) {
18189             clearTimeout(this.clickTimeout);
18190             if (this.dragCurrent) {
18191                 this.dragCurrent.b4StartDrag(x, y);
18192                 this.dragCurrent.startDrag(x, y);
18193             }
18194             this.dragThreshMet = true;
18195         },
18196
18197         /**
18198          * Internal function to handle the mouseup event.  Will be invoked
18199          * from the context of the document.
18200          * @method handleMouseUp
18201          * @param {Event} e the event
18202          * @private
18203          * @static
18204          */
18205         handleMouseUp: function(e) {
18206
18207             if(Roo.QuickTips){
18208                 Roo.QuickTips.enable();
18209             }
18210             if (! this.dragCurrent) {
18211                 return;
18212             }
18213
18214             clearTimeout(this.clickTimeout);
18215
18216             if (this.dragThreshMet) {
18217                 this.fireEvents(e, true);
18218             } else {
18219             }
18220
18221             this.stopDrag(e);
18222
18223             this.stopEvent(e);
18224         },
18225
18226         /**
18227          * Utility to stop event propagation and event default, if these
18228          * features are turned on.
18229          * @method stopEvent
18230          * @param {Event} e the event as returned by this.getEvent()
18231          * @static
18232          */
18233         stopEvent: function(e){
18234             if(this.stopPropagation) {
18235                 e.stopPropagation();
18236             }
18237
18238             if (this.preventDefault) {
18239                 e.preventDefault();
18240             }
18241         },
18242
18243         /**
18244          * Internal function to clean up event handlers after the drag
18245          * operation is complete
18246          * @method stopDrag
18247          * @param {Event} e the event
18248          * @private
18249          * @static
18250          */
18251         stopDrag: function(e) {
18252             // Fire the drag end event for the item that was dragged
18253             if (this.dragCurrent) {
18254                 if (this.dragThreshMet) {
18255                     this.dragCurrent.b4EndDrag(e);
18256                     this.dragCurrent.endDrag(e);
18257                 }
18258
18259                 this.dragCurrent.onMouseUp(e);
18260             }
18261
18262             this.dragCurrent = null;
18263             this.dragOvers = {};
18264         },
18265
18266         /**
18267          * Internal function to handle the mousemove event.  Will be invoked
18268          * from the context of the html element.
18269          *
18270          * @TODO figure out what we can do about mouse events lost when the
18271          * user drags objects beyond the window boundary.  Currently we can
18272          * detect this in internet explorer by verifying that the mouse is
18273          * down during the mousemove event.  Firefox doesn't give us the
18274          * button state on the mousemove event.
18275          * @method handleMouseMove
18276          * @param {Event} e the event
18277          * @private
18278          * @static
18279          */
18280         handleMouseMove: function(e) {
18281             if (! this.dragCurrent) {
18282                 return true;
18283             }
18284
18285             // var button = e.which || e.button;
18286
18287             // check for IE mouseup outside of page boundary
18288             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18289                 this.stopEvent(e);
18290                 return this.handleMouseUp(e);
18291             }
18292
18293             if (!this.dragThreshMet) {
18294                 var diffX = Math.abs(this.startX - e.getPageX());
18295                 var diffY = Math.abs(this.startY - e.getPageY());
18296                 if (diffX > this.clickPixelThresh ||
18297                             diffY > this.clickPixelThresh) {
18298                     this.startDrag(this.startX, this.startY);
18299                 }
18300             }
18301
18302             if (this.dragThreshMet) {
18303                 this.dragCurrent.b4Drag(e);
18304                 this.dragCurrent.onDrag(e);
18305                 if(!this.dragCurrent.moveOnly){
18306                     this.fireEvents(e, false);
18307                 }
18308             }
18309
18310             this.stopEvent(e);
18311
18312             return true;
18313         },
18314
18315         /**
18316          * Iterates over all of the DragDrop elements to find ones we are
18317          * hovering over or dropping on
18318          * @method fireEvents
18319          * @param {Event} e the event
18320          * @param {boolean} isDrop is this a drop op or a mouseover op?
18321          * @private
18322          * @static
18323          */
18324         fireEvents: function(e, isDrop) {
18325             var dc = this.dragCurrent;
18326
18327             // If the user did the mouse up outside of the window, we could
18328             // get here even though we have ended the drag.
18329             if (!dc || dc.isLocked()) {
18330                 return;
18331             }
18332
18333             var pt = e.getPoint();
18334
18335             // cache the previous dragOver array
18336             var oldOvers = [];
18337
18338             var outEvts   = [];
18339             var overEvts  = [];
18340             var dropEvts  = [];
18341             var enterEvts = [];
18342
18343             // Check to see if the object(s) we were hovering over is no longer
18344             // being hovered over so we can fire the onDragOut event
18345             for (var i in this.dragOvers) {
18346
18347                 var ddo = this.dragOvers[i];
18348
18349                 if (! this.isTypeOfDD(ddo)) {
18350                     continue;
18351                 }
18352
18353                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18354                     outEvts.push( ddo );
18355                 }
18356
18357                 oldOvers[i] = true;
18358                 delete this.dragOvers[i];
18359             }
18360
18361             for (var sGroup in dc.groups) {
18362
18363                 if ("string" != typeof sGroup) {
18364                     continue;
18365                 }
18366
18367                 for (i in this.ids[sGroup]) {
18368                     var oDD = this.ids[sGroup][i];
18369                     if (! this.isTypeOfDD(oDD)) {
18370                         continue;
18371                     }
18372
18373                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18374                         if (this.isOverTarget(pt, oDD, this.mode)) {
18375                             // look for drop interactions
18376                             if (isDrop) {
18377                                 dropEvts.push( oDD );
18378                             // look for drag enter and drag over interactions
18379                             } else {
18380
18381                                 // initial drag over: dragEnter fires
18382                                 if (!oldOvers[oDD.id]) {
18383                                     enterEvts.push( oDD );
18384                                 // subsequent drag overs: dragOver fires
18385                                 } else {
18386                                     overEvts.push( oDD );
18387                                 }
18388
18389                                 this.dragOvers[oDD.id] = oDD;
18390                             }
18391                         }
18392                     }
18393                 }
18394             }
18395
18396             if (this.mode) {
18397                 if (outEvts.length) {
18398                     dc.b4DragOut(e, outEvts);
18399                     dc.onDragOut(e, outEvts);
18400                 }
18401
18402                 if (enterEvts.length) {
18403                     dc.onDragEnter(e, enterEvts);
18404                 }
18405
18406                 if (overEvts.length) {
18407                     dc.b4DragOver(e, overEvts);
18408                     dc.onDragOver(e, overEvts);
18409                 }
18410
18411                 if (dropEvts.length) {
18412                     dc.b4DragDrop(e, dropEvts);
18413                     dc.onDragDrop(e, dropEvts);
18414                 }
18415
18416             } else {
18417                 // fire dragout events
18418                 var len = 0;
18419                 for (i=0, len=outEvts.length; i<len; ++i) {
18420                     dc.b4DragOut(e, outEvts[i].id);
18421                     dc.onDragOut(e, outEvts[i].id);
18422                 }
18423
18424                 // fire enter events
18425                 for (i=0,len=enterEvts.length; i<len; ++i) {
18426                     // dc.b4DragEnter(e, oDD.id);
18427                     dc.onDragEnter(e, enterEvts[i].id);
18428                 }
18429
18430                 // fire over events
18431                 for (i=0,len=overEvts.length; i<len; ++i) {
18432                     dc.b4DragOver(e, overEvts[i].id);
18433                     dc.onDragOver(e, overEvts[i].id);
18434                 }
18435
18436                 // fire drop events
18437                 for (i=0, len=dropEvts.length; i<len; ++i) {
18438                     dc.b4DragDrop(e, dropEvts[i].id);
18439                     dc.onDragDrop(e, dropEvts[i].id);
18440                 }
18441
18442             }
18443
18444             // notify about a drop that did not find a target
18445             if (isDrop && !dropEvts.length) {
18446                 dc.onInvalidDrop(e);
18447             }
18448
18449         },
18450
18451         /**
18452          * Helper function for getting the best match from the list of drag
18453          * and drop objects returned by the drag and drop events when we are
18454          * in INTERSECT mode.  It returns either the first object that the
18455          * cursor is over, or the object that has the greatest overlap with
18456          * the dragged element.
18457          * @method getBestMatch
18458          * @param  {DragDrop[]} dds The array of drag and drop objects
18459          * targeted
18460          * @return {DragDrop}       The best single match
18461          * @static
18462          */
18463         getBestMatch: function(dds) {
18464             var winner = null;
18465             // Return null if the input is not what we expect
18466             //if (!dds || !dds.length || dds.length == 0) {
18467                // winner = null;
18468             // If there is only one item, it wins
18469             //} else if (dds.length == 1) {
18470
18471             var len = dds.length;
18472
18473             if (len == 1) {
18474                 winner = dds[0];
18475             } else {
18476                 // Loop through the targeted items
18477                 for (var i=0; i<len; ++i) {
18478                     var dd = dds[i];
18479                     // If the cursor is over the object, it wins.  If the
18480                     // cursor is over multiple matches, the first one we come
18481                     // to wins.
18482                     if (dd.cursorIsOver) {
18483                         winner = dd;
18484                         break;
18485                     // Otherwise the object with the most overlap wins
18486                     } else {
18487                         if (!winner ||
18488                             winner.overlap.getArea() < dd.overlap.getArea()) {
18489                             winner = dd;
18490                         }
18491                     }
18492                 }
18493             }
18494
18495             return winner;
18496         },
18497
18498         /**
18499          * Refreshes the cache of the top-left and bottom-right points of the
18500          * drag and drop objects in the specified group(s).  This is in the
18501          * format that is stored in the drag and drop instance, so typical
18502          * usage is:
18503          * <code>
18504          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18505          * </code>
18506          * Alternatively:
18507          * <code>
18508          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18509          * </code>
18510          * @TODO this really should be an indexed array.  Alternatively this
18511          * method could accept both.
18512          * @method refreshCache
18513          * @param {Object} groups an associative array of groups to refresh
18514          * @static
18515          */
18516         refreshCache: function(groups) {
18517             for (var sGroup in groups) {
18518                 if ("string" != typeof sGroup) {
18519                     continue;
18520                 }
18521                 for (var i in this.ids[sGroup]) {
18522                     var oDD = this.ids[sGroup][i];
18523
18524                     if (this.isTypeOfDD(oDD)) {
18525                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18526                         var loc = this.getLocation(oDD);
18527                         if (loc) {
18528                             this.locationCache[oDD.id] = loc;
18529                         } else {
18530                             delete this.locationCache[oDD.id];
18531                             // this will unregister the drag and drop object if
18532                             // the element is not in a usable state
18533                             // oDD.unreg();
18534                         }
18535                     }
18536                 }
18537             }
18538         },
18539
18540         /**
18541          * This checks to make sure an element exists and is in the DOM.  The
18542          * main purpose is to handle cases where innerHTML is used to remove
18543          * drag and drop objects from the DOM.  IE provides an 'unspecified
18544          * error' when trying to access the offsetParent of such an element
18545          * @method verifyEl
18546          * @param {HTMLElement} el the element to check
18547          * @return {boolean} true if the element looks usable
18548          * @static
18549          */
18550         verifyEl: function(el) {
18551             if (el) {
18552                 var parent;
18553                 if(Roo.isIE){
18554                     try{
18555                         parent = el.offsetParent;
18556                     }catch(e){}
18557                 }else{
18558                     parent = el.offsetParent;
18559                 }
18560                 if (parent) {
18561                     return true;
18562                 }
18563             }
18564
18565             return false;
18566         },
18567
18568         /**
18569          * Returns a Region object containing the drag and drop element's position
18570          * and size, including the padding configured for it
18571          * @method getLocation
18572          * @param {DragDrop} oDD the drag and drop object to get the
18573          *                       location for
18574          * @return {Roo.lib.Region} a Region object representing the total area
18575          *                             the element occupies, including any padding
18576          *                             the instance is configured for.
18577          * @static
18578          */
18579         getLocation: function(oDD) {
18580             if (! this.isTypeOfDD(oDD)) {
18581                 return null;
18582             }
18583
18584             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18585
18586             try {
18587                 pos= Roo.lib.Dom.getXY(el);
18588             } catch (e) { }
18589
18590             if (!pos) {
18591                 return null;
18592             }
18593
18594             x1 = pos[0];
18595             x2 = x1 + el.offsetWidth;
18596             y1 = pos[1];
18597             y2 = y1 + el.offsetHeight;
18598
18599             t = y1 - oDD.padding[0];
18600             r = x2 + oDD.padding[1];
18601             b = y2 + oDD.padding[2];
18602             l = x1 - oDD.padding[3];
18603
18604             return new Roo.lib.Region( t, r, b, l );
18605         },
18606
18607         /**
18608          * Checks the cursor location to see if it over the target
18609          * @method isOverTarget
18610          * @param {Roo.lib.Point} pt The point to evaluate
18611          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18612          * @return {boolean} true if the mouse is over the target
18613          * @private
18614          * @static
18615          */
18616         isOverTarget: function(pt, oTarget, intersect) {
18617             // use cache if available
18618             var loc = this.locationCache[oTarget.id];
18619             if (!loc || !this.useCache) {
18620                 loc = this.getLocation(oTarget);
18621                 this.locationCache[oTarget.id] = loc;
18622
18623             }
18624
18625             if (!loc) {
18626                 return false;
18627             }
18628
18629             oTarget.cursorIsOver = loc.contains( pt );
18630
18631             // DragDrop is using this as a sanity check for the initial mousedown
18632             // in this case we are done.  In POINT mode, if the drag obj has no
18633             // contraints, we are also done. Otherwise we need to evaluate the
18634             // location of the target as related to the actual location of the
18635             // dragged element.
18636             var dc = this.dragCurrent;
18637             if (!dc || !dc.getTargetCoord ||
18638                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18639                 return oTarget.cursorIsOver;
18640             }
18641
18642             oTarget.overlap = null;
18643
18644             // Get the current location of the drag element, this is the
18645             // location of the mouse event less the delta that represents
18646             // where the original mousedown happened on the element.  We
18647             // need to consider constraints and ticks as well.
18648             var pos = dc.getTargetCoord(pt.x, pt.y);
18649
18650             var el = dc.getDragEl();
18651             var curRegion = new Roo.lib.Region( pos.y,
18652                                                    pos.x + el.offsetWidth,
18653                                                    pos.y + el.offsetHeight,
18654                                                    pos.x );
18655
18656             var overlap = curRegion.intersect(loc);
18657
18658             if (overlap) {
18659                 oTarget.overlap = overlap;
18660                 return (intersect) ? true : oTarget.cursorIsOver;
18661             } else {
18662                 return false;
18663             }
18664         },
18665
18666         /**
18667          * unload event handler
18668          * @method _onUnload
18669          * @private
18670          * @static
18671          */
18672         _onUnload: function(e, me) {
18673             Roo.dd.DragDropMgr.unregAll();
18674         },
18675
18676         /**
18677          * Cleans up the drag and drop events and objects.
18678          * @method unregAll
18679          * @private
18680          * @static
18681          */
18682         unregAll: function() {
18683
18684             if (this.dragCurrent) {
18685                 this.stopDrag();
18686                 this.dragCurrent = null;
18687             }
18688
18689             this._execOnAll("unreg", []);
18690
18691             for (i in this.elementCache) {
18692                 delete this.elementCache[i];
18693             }
18694
18695             this.elementCache = {};
18696             this.ids = {};
18697         },
18698
18699         /**
18700          * A cache of DOM elements
18701          * @property elementCache
18702          * @private
18703          * @static
18704          */
18705         elementCache: {},
18706
18707         /**
18708          * Get the wrapper for the DOM element specified
18709          * @method getElWrapper
18710          * @param {String} id the id of the element to get
18711          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18712          * @private
18713          * @deprecated This wrapper isn't that useful
18714          * @static
18715          */
18716         getElWrapper: function(id) {
18717             var oWrapper = this.elementCache[id];
18718             if (!oWrapper || !oWrapper.el) {
18719                 oWrapper = this.elementCache[id] =
18720                     new this.ElementWrapper(Roo.getDom(id));
18721             }
18722             return oWrapper;
18723         },
18724
18725         /**
18726          * Returns the actual DOM element
18727          * @method getElement
18728          * @param {String} id the id of the elment to get
18729          * @return {Object} The element
18730          * @deprecated use Roo.getDom instead
18731          * @static
18732          */
18733         getElement: function(id) {
18734             return Roo.getDom(id);
18735         },
18736
18737         /**
18738          * Returns the style property for the DOM element (i.e.,
18739          * document.getElById(id).style)
18740          * @method getCss
18741          * @param {String} id the id of the elment to get
18742          * @return {Object} The style property of the element
18743          * @deprecated use Roo.getDom instead
18744          * @static
18745          */
18746         getCss: function(id) {
18747             var el = Roo.getDom(id);
18748             return (el) ? el.style : null;
18749         },
18750
18751         /**
18752          * Inner class for cached elements
18753          * @class DragDropMgr.ElementWrapper
18754          * @for DragDropMgr
18755          * @private
18756          * @deprecated
18757          */
18758         ElementWrapper: function(el) {
18759                 /**
18760                  * The element
18761                  * @property el
18762                  */
18763                 this.el = el || null;
18764                 /**
18765                  * The element id
18766                  * @property id
18767                  */
18768                 this.id = this.el && el.id;
18769                 /**
18770                  * A reference to the style property
18771                  * @property css
18772                  */
18773                 this.css = this.el && el.style;
18774             },
18775
18776         /**
18777          * Returns the X position of an html element
18778          * @method getPosX
18779          * @param el the element for which to get the position
18780          * @return {int} the X coordinate
18781          * @for DragDropMgr
18782          * @deprecated use Roo.lib.Dom.getX instead
18783          * @static
18784          */
18785         getPosX: function(el) {
18786             return Roo.lib.Dom.getX(el);
18787         },
18788
18789         /**
18790          * Returns the Y position of an html element
18791          * @method getPosY
18792          * @param el the element for which to get the position
18793          * @return {int} the Y coordinate
18794          * @deprecated use Roo.lib.Dom.getY instead
18795          * @static
18796          */
18797         getPosY: function(el) {
18798             return Roo.lib.Dom.getY(el);
18799         },
18800
18801         /**
18802          * Swap two nodes.  In IE, we use the native method, for others we
18803          * emulate the IE behavior
18804          * @method swapNode
18805          * @param n1 the first node to swap
18806          * @param n2 the other node to swap
18807          * @static
18808          */
18809         swapNode: function(n1, n2) {
18810             if (n1.swapNode) {
18811                 n1.swapNode(n2);
18812             } else {
18813                 var p = n2.parentNode;
18814                 var s = n2.nextSibling;
18815
18816                 if (s == n1) {
18817                     p.insertBefore(n1, n2);
18818                 } else if (n2 == n1.nextSibling) {
18819                     p.insertBefore(n2, n1);
18820                 } else {
18821                     n1.parentNode.replaceChild(n2, n1);
18822                     p.insertBefore(n1, s);
18823                 }
18824             }
18825         },
18826
18827         /**
18828          * Returns the current scroll position
18829          * @method getScroll
18830          * @private
18831          * @static
18832          */
18833         getScroll: function () {
18834             var t, l, dde=document.documentElement, db=document.body;
18835             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18836                 t = dde.scrollTop;
18837                 l = dde.scrollLeft;
18838             } else if (db) {
18839                 t = db.scrollTop;
18840                 l = db.scrollLeft;
18841             } else {
18842
18843             }
18844             return { top: t, left: l };
18845         },
18846
18847         /**
18848          * Returns the specified element style property
18849          * @method getStyle
18850          * @param {HTMLElement} el          the element
18851          * @param {string}      styleProp   the style property
18852          * @return {string} The value of the style property
18853          * @deprecated use Roo.lib.Dom.getStyle
18854          * @static
18855          */
18856         getStyle: function(el, styleProp) {
18857             return Roo.fly(el).getStyle(styleProp);
18858         },
18859
18860         /**
18861          * Gets the scrollTop
18862          * @method getScrollTop
18863          * @return {int} the document's scrollTop
18864          * @static
18865          */
18866         getScrollTop: function () { return this.getScroll().top; },
18867
18868         /**
18869          * Gets the scrollLeft
18870          * @method getScrollLeft
18871          * @return {int} the document's scrollTop
18872          * @static
18873          */
18874         getScrollLeft: function () { return this.getScroll().left; },
18875
18876         /**
18877          * Sets the x/y position of an element to the location of the
18878          * target element.
18879          * @method moveToEl
18880          * @param {HTMLElement} moveEl      The element to move
18881          * @param {HTMLElement} targetEl    The position reference element
18882          * @static
18883          */
18884         moveToEl: function (moveEl, targetEl) {
18885             var aCoord = Roo.lib.Dom.getXY(targetEl);
18886             Roo.lib.Dom.setXY(moveEl, aCoord);
18887         },
18888
18889         /**
18890          * Numeric array sort function
18891          * @method numericSort
18892          * @static
18893          */
18894         numericSort: function(a, b) { return (a - b); },
18895
18896         /**
18897          * Internal counter
18898          * @property _timeoutCount
18899          * @private
18900          * @static
18901          */
18902         _timeoutCount: 0,
18903
18904         /**
18905          * Trying to make the load order less important.  Without this we get
18906          * an error if this file is loaded before the Event Utility.
18907          * @method _addListeners
18908          * @private
18909          * @static
18910          */
18911         _addListeners: function() {
18912             var DDM = Roo.dd.DDM;
18913             if ( Roo.lib.Event && document ) {
18914                 DDM._onLoad();
18915             } else {
18916                 if (DDM._timeoutCount > 2000) {
18917                 } else {
18918                     setTimeout(DDM._addListeners, 10);
18919                     if (document && document.body) {
18920                         DDM._timeoutCount += 1;
18921                     }
18922                 }
18923             }
18924         },
18925
18926         /**
18927          * Recursively searches the immediate parent and all child nodes for
18928          * the handle element in order to determine wheter or not it was
18929          * clicked.
18930          * @method handleWasClicked
18931          * @param node the html element to inspect
18932          * @static
18933          */
18934         handleWasClicked: function(node, id) {
18935             if (this.isHandle(id, node.id)) {
18936                 return true;
18937             } else {
18938                 // check to see if this is a text node child of the one we want
18939                 var p = node.parentNode;
18940
18941                 while (p) {
18942                     if (this.isHandle(id, p.id)) {
18943                         return true;
18944                     } else {
18945                         p = p.parentNode;
18946                     }
18947                 }
18948             }
18949
18950             return false;
18951         }
18952
18953     };
18954
18955 }();
18956
18957 // shorter alias, save a few bytes
18958 Roo.dd.DDM = Roo.dd.DragDropMgr;
18959 Roo.dd.DDM._addListeners();
18960
18961 }/*
18962  * Based on:
18963  * Ext JS Library 1.1.1
18964  * Copyright(c) 2006-2007, Ext JS, LLC.
18965  *
18966  * Originally Released Under LGPL - original licence link has changed is not relivant.
18967  *
18968  * Fork - LGPL
18969  * <script type="text/javascript">
18970  */
18971
18972 /**
18973  * @class Roo.dd.DD
18974  * A DragDrop implementation where the linked element follows the
18975  * mouse cursor during a drag.
18976  * @extends Roo.dd.DragDrop
18977  * @constructor
18978  * @param {String} id the id of the linked element
18979  * @param {String} sGroup the group of related DragDrop items
18980  * @param {object} config an object containing configurable attributes
18981  *                Valid properties for DD:
18982  *                    scroll
18983  */
18984 Roo.dd.DD = function(id, sGroup, config) {
18985     if (id) {
18986         this.init(id, sGroup, config);
18987     }
18988 };
18989
18990 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18991
18992     /**
18993      * When set to true, the utility automatically tries to scroll the browser
18994      * window wehn a drag and drop element is dragged near the viewport boundary.
18995      * Defaults to true.
18996      * @property scroll
18997      * @type boolean
18998      */
18999     scroll: true,
19000
19001     /**
19002      * Sets the pointer offset to the distance between the linked element's top
19003      * left corner and the location the element was clicked
19004      * @method autoOffset
19005      * @param {int} iPageX the X coordinate of the click
19006      * @param {int} iPageY the Y coordinate of the click
19007      */
19008     autoOffset: function(iPageX, iPageY) {
19009         var x = iPageX - this.startPageX;
19010         var y = iPageY - this.startPageY;
19011         this.setDelta(x, y);
19012     },
19013
19014     /**
19015      * Sets the pointer offset.  You can call this directly to force the
19016      * offset to be in a particular location (e.g., pass in 0,0 to set it
19017      * to the center of the object)
19018      * @method setDelta
19019      * @param {int} iDeltaX the distance from the left
19020      * @param {int} iDeltaY the distance from the top
19021      */
19022     setDelta: function(iDeltaX, iDeltaY) {
19023         this.deltaX = iDeltaX;
19024         this.deltaY = iDeltaY;
19025     },
19026
19027     /**
19028      * Sets the drag element to the location of the mousedown or click event,
19029      * maintaining the cursor location relative to the location on the element
19030      * that was clicked.  Override this if you want to place the element in a
19031      * location other than where the cursor is.
19032      * @method setDragElPos
19033      * @param {int} iPageX the X coordinate of the mousedown or drag event
19034      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19035      */
19036     setDragElPos: function(iPageX, iPageY) {
19037         // the first time we do this, we are going to check to make sure
19038         // the element has css positioning
19039
19040         var el = this.getDragEl();
19041         this.alignElWithMouse(el, iPageX, iPageY);
19042     },
19043
19044     /**
19045      * Sets the element to the location of the mousedown or click event,
19046      * maintaining the cursor location relative to the location on the element
19047      * that was clicked.  Override this if you want to place the element in a
19048      * location other than where the cursor is.
19049      * @method alignElWithMouse
19050      * @param {HTMLElement} el the element to move
19051      * @param {int} iPageX the X coordinate of the mousedown or drag event
19052      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19053      */
19054     alignElWithMouse: function(el, iPageX, iPageY) {
19055         var oCoord = this.getTargetCoord(iPageX, iPageY);
19056         var fly = el.dom ? el : Roo.fly(el);
19057         if (!this.deltaSetXY) {
19058             var aCoord = [oCoord.x, oCoord.y];
19059             fly.setXY(aCoord);
19060             var newLeft = fly.getLeft(true);
19061             var newTop  = fly.getTop(true);
19062             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
19063         } else {
19064             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
19065         }
19066
19067         this.cachePosition(oCoord.x, oCoord.y);
19068         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
19069         return oCoord;
19070     },
19071
19072     /**
19073      * Saves the most recent position so that we can reset the constraints and
19074      * tick marks on-demand.  We need to know this so that we can calculate the
19075      * number of pixels the element is offset from its original position.
19076      * @method cachePosition
19077      * @param iPageX the current x position (optional, this just makes it so we
19078      * don't have to look it up again)
19079      * @param iPageY the current y position (optional, this just makes it so we
19080      * don't have to look it up again)
19081      */
19082     cachePosition: function(iPageX, iPageY) {
19083         if (iPageX) {
19084             this.lastPageX = iPageX;
19085             this.lastPageY = iPageY;
19086         } else {
19087             var aCoord = Roo.lib.Dom.getXY(this.getEl());
19088             this.lastPageX = aCoord[0];
19089             this.lastPageY = aCoord[1];
19090         }
19091     },
19092
19093     /**
19094      * Auto-scroll the window if the dragged object has been moved beyond the
19095      * visible window boundary.
19096      * @method autoScroll
19097      * @param {int} x the drag element's x position
19098      * @param {int} y the drag element's y position
19099      * @param {int} h the height of the drag element
19100      * @param {int} w the width of the drag element
19101      * @private
19102      */
19103     autoScroll: function(x, y, h, w) {
19104
19105         if (this.scroll) {
19106             // The client height
19107             var clientH = Roo.lib.Dom.getViewWidth();
19108
19109             // The client width
19110             var clientW = Roo.lib.Dom.getViewHeight();
19111
19112             // The amt scrolled down
19113             var st = this.DDM.getScrollTop();
19114
19115             // The amt scrolled right
19116             var sl = this.DDM.getScrollLeft();
19117
19118             // Location of the bottom of the element
19119             var bot = h + y;
19120
19121             // Location of the right of the element
19122             var right = w + x;
19123
19124             // The distance from the cursor to the bottom of the visible area,
19125             // adjusted so that we don't scroll if the cursor is beyond the
19126             // element drag constraints
19127             var toBot = (clientH + st - y - this.deltaY);
19128
19129             // The distance from the cursor to the right of the visible area
19130             var toRight = (clientW + sl - x - this.deltaX);
19131
19132
19133             // How close to the edge the cursor must be before we scroll
19134             // var thresh = (document.all) ? 100 : 40;
19135             var thresh = 40;
19136
19137             // How many pixels to scroll per autoscroll op.  This helps to reduce
19138             // clunky scrolling. IE is more sensitive about this ... it needs this
19139             // value to be higher.
19140             var scrAmt = (document.all) ? 80 : 30;
19141
19142             // Scroll down if we are near the bottom of the visible page and the
19143             // obj extends below the crease
19144             if ( bot > clientH && toBot < thresh ) {
19145                 window.scrollTo(sl, st + scrAmt);
19146             }
19147
19148             // Scroll up if the window is scrolled down and the top of the object
19149             // goes above the top border
19150             if ( y < st && st > 0 && y - st < thresh ) {
19151                 window.scrollTo(sl, st - scrAmt);
19152             }
19153
19154             // Scroll right if the obj is beyond the right border and the cursor is
19155             // near the border.
19156             if ( right > clientW && toRight < thresh ) {
19157                 window.scrollTo(sl + scrAmt, st);
19158             }
19159
19160             // Scroll left if the window has been scrolled to the right and the obj
19161             // extends past the left border
19162             if ( x < sl && sl > 0 && x - sl < thresh ) {
19163                 window.scrollTo(sl - scrAmt, st);
19164             }
19165         }
19166     },
19167
19168     /**
19169      * Finds the location the element should be placed if we want to move
19170      * it to where the mouse location less the click offset would place us.
19171      * @method getTargetCoord
19172      * @param {int} iPageX the X coordinate of the click
19173      * @param {int} iPageY the Y coordinate of the click
19174      * @return an object that contains the coordinates (Object.x and Object.y)
19175      * @private
19176      */
19177     getTargetCoord: function(iPageX, iPageY) {
19178
19179
19180         var x = iPageX - this.deltaX;
19181         var y = iPageY - this.deltaY;
19182
19183         if (this.constrainX) {
19184             if (x < this.minX) { x = this.minX; }
19185             if (x > this.maxX) { x = this.maxX; }
19186         }
19187
19188         if (this.constrainY) {
19189             if (y < this.minY) { y = this.minY; }
19190             if (y > this.maxY) { y = this.maxY; }
19191         }
19192
19193         x = this.getTick(x, this.xTicks);
19194         y = this.getTick(y, this.yTicks);
19195
19196
19197         return {x:x, y:y};
19198     },
19199
19200     /*
19201      * Sets up config options specific to this class. Overrides
19202      * Roo.dd.DragDrop, but all versions of this method through the
19203      * inheritance chain are called
19204      */
19205     applyConfig: function() {
19206         Roo.dd.DD.superclass.applyConfig.call(this);
19207         this.scroll = (this.config.scroll !== false);
19208     },
19209
19210     /*
19211      * Event that fires prior to the onMouseDown event.  Overrides
19212      * Roo.dd.DragDrop.
19213      */
19214     b4MouseDown: function(e) {
19215         // this.resetConstraints();
19216         this.autoOffset(e.getPageX(),
19217                             e.getPageY());
19218     },
19219
19220     /*
19221      * Event that fires prior to the onDrag event.  Overrides
19222      * Roo.dd.DragDrop.
19223      */
19224     b4Drag: function(e) {
19225         this.setDragElPos(e.getPageX(),
19226                             e.getPageY());
19227     },
19228
19229     toString: function() {
19230         return ("DD " + this.id);
19231     }
19232
19233     //////////////////////////////////////////////////////////////////////////
19234     // Debugging ygDragDrop events that can be overridden
19235     //////////////////////////////////////////////////////////////////////////
19236     /*
19237     startDrag: function(x, y) {
19238     },
19239
19240     onDrag: function(e) {
19241     },
19242
19243     onDragEnter: function(e, id) {
19244     },
19245
19246     onDragOver: function(e, id) {
19247     },
19248
19249     onDragOut: function(e, id) {
19250     },
19251
19252     onDragDrop: function(e, id) {
19253     },
19254
19255     endDrag: function(e) {
19256     }
19257
19258     */
19259
19260 });/*
19261  * Based on:
19262  * Ext JS Library 1.1.1
19263  * Copyright(c) 2006-2007, Ext JS, LLC.
19264  *
19265  * Originally Released Under LGPL - original licence link has changed is not relivant.
19266  *
19267  * Fork - LGPL
19268  * <script type="text/javascript">
19269  */
19270
19271 /**
19272  * @class Roo.dd.DDProxy
19273  * A DragDrop implementation that inserts an empty, bordered div into
19274  * the document that follows the cursor during drag operations.  At the time of
19275  * the click, the frame div is resized to the dimensions of the linked html
19276  * element, and moved to the exact location of the linked element.
19277  *
19278  * References to the "frame" element refer to the single proxy element that
19279  * was created to be dragged in place of all DDProxy elements on the
19280  * page.
19281  *
19282  * @extends Roo.dd.DD
19283  * @constructor
19284  * @param {String} id the id of the linked html element
19285  * @param {String} sGroup the group of related DragDrop objects
19286  * @param {object} config an object containing configurable attributes
19287  *                Valid properties for DDProxy in addition to those in DragDrop:
19288  *                   resizeFrame, centerFrame, dragElId
19289  */
19290 Roo.dd.DDProxy = function(id, sGroup, config) {
19291     if (id) {
19292         this.init(id, sGroup, config);
19293         this.initFrame();
19294     }
19295 };
19296
19297 /**
19298  * The default drag frame div id
19299  * @property Roo.dd.DDProxy.dragElId
19300  * @type String
19301  * @static
19302  */
19303 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19304
19305 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19306
19307     /**
19308      * By default we resize the drag frame to be the same size as the element
19309      * we want to drag (this is to get the frame effect).  We can turn it off
19310      * if we want a different behavior.
19311      * @property resizeFrame
19312      * @type boolean
19313      */
19314     resizeFrame: true,
19315
19316     /**
19317      * By default the frame is positioned exactly where the drag element is, so
19318      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19319      * you do not have constraints on the obj is to have the drag frame centered
19320      * around the cursor.  Set centerFrame to true for this effect.
19321      * @property centerFrame
19322      * @type boolean
19323      */
19324     centerFrame: false,
19325
19326     /**
19327      * Creates the proxy element if it does not yet exist
19328      * @method createFrame
19329      */
19330     createFrame: function() {
19331         var self = this;
19332         var body = document.body;
19333
19334         if (!body || !body.firstChild) {
19335             setTimeout( function() { self.createFrame(); }, 50 );
19336             return;
19337         }
19338
19339         var div = this.getDragEl();
19340
19341         if (!div) {
19342             div    = document.createElement("div");
19343             div.id = this.dragElId;
19344             var s  = div.style;
19345
19346             s.position   = "absolute";
19347             s.visibility = "hidden";
19348             s.cursor     = "move";
19349             s.border     = "2px solid #aaa";
19350             s.zIndex     = 999;
19351
19352             // appendChild can blow up IE if invoked prior to the window load event
19353             // while rendering a table.  It is possible there are other scenarios
19354             // that would cause this to happen as well.
19355             body.insertBefore(div, body.firstChild);
19356         }
19357     },
19358
19359     /**
19360      * Initialization for the drag frame element.  Must be called in the
19361      * constructor of all subclasses
19362      * @method initFrame
19363      */
19364     initFrame: function() {
19365         this.createFrame();
19366     },
19367
19368     applyConfig: function() {
19369         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19370
19371         this.resizeFrame = (this.config.resizeFrame !== false);
19372         this.centerFrame = (this.config.centerFrame);
19373         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19374     },
19375
19376     /**
19377      * Resizes the drag frame to the dimensions of the clicked object, positions
19378      * it over the object, and finally displays it
19379      * @method showFrame
19380      * @param {int} iPageX X click position
19381      * @param {int} iPageY Y click position
19382      * @private
19383      */
19384     showFrame: function(iPageX, iPageY) {
19385         var el = this.getEl();
19386         var dragEl = this.getDragEl();
19387         var s = dragEl.style;
19388
19389         this._resizeProxy();
19390
19391         if (this.centerFrame) {
19392             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19393                            Math.round(parseInt(s.height, 10)/2) );
19394         }
19395
19396         this.setDragElPos(iPageX, iPageY);
19397
19398         Roo.fly(dragEl).show();
19399     },
19400
19401     /**
19402      * The proxy is automatically resized to the dimensions of the linked
19403      * element when a drag is initiated, unless resizeFrame is set to false
19404      * @method _resizeProxy
19405      * @private
19406      */
19407     _resizeProxy: function() {
19408         if (this.resizeFrame) {
19409             var el = this.getEl();
19410             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19411         }
19412     },
19413
19414     // overrides Roo.dd.DragDrop
19415     b4MouseDown: function(e) {
19416         var x = e.getPageX();
19417         var y = e.getPageY();
19418         this.autoOffset(x, y);
19419         this.setDragElPos(x, y);
19420     },
19421
19422     // overrides Roo.dd.DragDrop
19423     b4StartDrag: function(x, y) {
19424         // show the drag frame
19425         this.showFrame(x, y);
19426     },
19427
19428     // overrides Roo.dd.DragDrop
19429     b4EndDrag: function(e) {
19430         Roo.fly(this.getDragEl()).hide();
19431     },
19432
19433     // overrides Roo.dd.DragDrop
19434     // By default we try to move the element to the last location of the frame.
19435     // This is so that the default behavior mirrors that of Roo.dd.DD.
19436     endDrag: function(e) {
19437
19438         var lel = this.getEl();
19439         var del = this.getDragEl();
19440
19441         // Show the drag frame briefly so we can get its position
19442         del.style.visibility = "";
19443
19444         this.beforeMove();
19445         // Hide the linked element before the move to get around a Safari
19446         // rendering bug.
19447         lel.style.visibility = "hidden";
19448         Roo.dd.DDM.moveToEl(lel, del);
19449         del.style.visibility = "hidden";
19450         lel.style.visibility = "";
19451
19452         this.afterDrag();
19453     },
19454
19455     beforeMove : function(){
19456
19457     },
19458
19459     afterDrag : function(){
19460
19461     },
19462
19463     toString: function() {
19464         return ("DDProxy " + this.id);
19465     }
19466
19467 });
19468 /*
19469  * Based on:
19470  * Ext JS Library 1.1.1
19471  * Copyright(c) 2006-2007, Ext JS, LLC.
19472  *
19473  * Originally Released Under LGPL - original licence link has changed is not relivant.
19474  *
19475  * Fork - LGPL
19476  * <script type="text/javascript">
19477  */
19478
19479  /**
19480  * @class Roo.dd.DDTarget
19481  * A DragDrop implementation that does not move, but can be a drop
19482  * target.  You would get the same result by simply omitting implementation
19483  * for the event callbacks, but this way we reduce the processing cost of the
19484  * event listener and the callbacks.
19485  * @extends Roo.dd.DragDrop
19486  * @constructor
19487  * @param {String} id the id of the element that is a drop target
19488  * @param {String} sGroup the group of related DragDrop objects
19489  * @param {object} config an object containing configurable attributes
19490  *                 Valid properties for DDTarget in addition to those in
19491  *                 DragDrop:
19492  *                    none
19493  */
19494 Roo.dd.DDTarget = function(id, sGroup, config) {
19495     if (id) {
19496         this.initTarget(id, sGroup, config);
19497     }
19498     if (config.listeners || config.events) { 
19499        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19500             listeners : config.listeners || {}, 
19501             events : config.events || {} 
19502         });    
19503     }
19504 };
19505
19506 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19507 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19508     toString: function() {
19509         return ("DDTarget " + this.id);
19510     }
19511 });
19512 /*
19513  * Based on:
19514  * Ext JS Library 1.1.1
19515  * Copyright(c) 2006-2007, Ext JS, LLC.
19516  *
19517  * Originally Released Under LGPL - original licence link has changed is not relivant.
19518  *
19519  * Fork - LGPL
19520  * <script type="text/javascript">
19521  */
19522  
19523
19524 /**
19525  * @class Roo.dd.ScrollManager
19526  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19527  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19528  * @singleton
19529  */
19530 Roo.dd.ScrollManager = function(){
19531     var ddm = Roo.dd.DragDropMgr;
19532     var els = {};
19533     var dragEl = null;
19534     var proc = {};
19535     
19536     
19537     
19538     var onStop = function(e){
19539         dragEl = null;
19540         clearProc();
19541     };
19542     
19543     var triggerRefresh = function(){
19544         if(ddm.dragCurrent){
19545              ddm.refreshCache(ddm.dragCurrent.groups);
19546         }
19547     };
19548     
19549     var doScroll = function(){
19550         if(ddm.dragCurrent){
19551             var dds = Roo.dd.ScrollManager;
19552             if(!dds.animate){
19553                 if(proc.el.scroll(proc.dir, dds.increment)){
19554                     triggerRefresh();
19555                 }
19556             }else{
19557                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19558             }
19559         }
19560     };
19561     
19562     var clearProc = function(){
19563         if(proc.id){
19564             clearInterval(proc.id);
19565         }
19566         proc.id = 0;
19567         proc.el = null;
19568         proc.dir = "";
19569     };
19570     
19571     var startProc = function(el, dir){
19572          Roo.log('scroll startproc');
19573         clearProc();
19574         proc.el = el;
19575         proc.dir = dir;
19576         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19577     };
19578     
19579     var onFire = function(e, isDrop){
19580        
19581         if(isDrop || !ddm.dragCurrent){ return; }
19582         var dds = Roo.dd.ScrollManager;
19583         if(!dragEl || dragEl != ddm.dragCurrent){
19584             dragEl = ddm.dragCurrent;
19585             // refresh regions on drag start
19586             dds.refreshCache();
19587         }
19588         
19589         var xy = Roo.lib.Event.getXY(e);
19590         var pt = new Roo.lib.Point(xy[0], xy[1]);
19591         for(var id in els){
19592             var el = els[id], r = el._region;
19593             if(r && r.contains(pt) && el.isScrollable()){
19594                 if(r.bottom - pt.y <= dds.thresh){
19595                     if(proc.el != el){
19596                         startProc(el, "down");
19597                     }
19598                     return;
19599                 }else if(r.right - pt.x <= dds.thresh){
19600                     if(proc.el != el){
19601                         startProc(el, "left");
19602                     }
19603                     return;
19604                 }else if(pt.y - r.top <= dds.thresh){
19605                     if(proc.el != el){
19606                         startProc(el, "up");
19607                     }
19608                     return;
19609                 }else if(pt.x - r.left <= dds.thresh){
19610                     if(proc.el != el){
19611                         startProc(el, "right");
19612                     }
19613                     return;
19614                 }
19615             }
19616         }
19617         clearProc();
19618     };
19619     
19620     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19621     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19622     
19623     return {
19624         /**
19625          * Registers new overflow element(s) to auto scroll
19626          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19627          */
19628         register : function(el){
19629             if(el instanceof Array){
19630                 for(var i = 0, len = el.length; i < len; i++) {
19631                         this.register(el[i]);
19632                 }
19633             }else{
19634                 el = Roo.get(el);
19635                 els[el.id] = el;
19636             }
19637             Roo.dd.ScrollManager.els = els;
19638         },
19639         
19640         /**
19641          * Unregisters overflow element(s) so they are no longer scrolled
19642          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19643          */
19644         unregister : function(el){
19645             if(el instanceof Array){
19646                 for(var i = 0, len = el.length; i < len; i++) {
19647                         this.unregister(el[i]);
19648                 }
19649             }else{
19650                 el = Roo.get(el);
19651                 delete els[el.id];
19652             }
19653         },
19654         
19655         /**
19656          * The number of pixels from the edge of a container the pointer needs to be to 
19657          * trigger scrolling (defaults to 25)
19658          * @type Number
19659          */
19660         thresh : 25,
19661         
19662         /**
19663          * The number of pixels to scroll in each scroll increment (defaults to 50)
19664          * @type Number
19665          */
19666         increment : 100,
19667         
19668         /**
19669          * The frequency of scrolls in milliseconds (defaults to 500)
19670          * @type Number
19671          */
19672         frequency : 500,
19673         
19674         /**
19675          * True to animate the scroll (defaults to true)
19676          * @type Boolean
19677          */
19678         animate: true,
19679         
19680         /**
19681          * The animation duration in seconds - 
19682          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19683          * @type Number
19684          */
19685         animDuration: .4,
19686         
19687         /**
19688          * Manually trigger a cache refresh.
19689          */
19690         refreshCache : function(){
19691             for(var id in els){
19692                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19693                     els[id]._region = els[id].getRegion();
19694                 }
19695             }
19696         }
19697     };
19698 }();/*
19699  * Based on:
19700  * Ext JS Library 1.1.1
19701  * Copyright(c) 2006-2007, Ext JS, LLC.
19702  *
19703  * Originally Released Under LGPL - original licence link has changed is not relivant.
19704  *
19705  * Fork - LGPL
19706  * <script type="text/javascript">
19707  */
19708  
19709
19710 /**
19711  * @class Roo.dd.Registry
19712  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19713  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19714  * @singleton
19715  */
19716 Roo.dd.Registry = function(){
19717     var elements = {}; 
19718     var handles = {}; 
19719     var autoIdSeed = 0;
19720
19721     var getId = function(el, autogen){
19722         if(typeof el == "string"){
19723             return el;
19724         }
19725         var id = el.id;
19726         if(!id && autogen !== false){
19727             id = "roodd-" + (++autoIdSeed);
19728             el.id = id;
19729         }
19730         return id;
19731     };
19732     
19733     return {
19734     /**
19735      * Register a drag drop element
19736      * @param {String|HTMLElement} element The id or DOM node to register
19737      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19738      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19739      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19740      * populated in the data object (if applicable):
19741      * <pre>
19742 Value      Description<br />
19743 ---------  ------------------------------------------<br />
19744 handles    Array of DOM nodes that trigger dragging<br />
19745            for the element being registered<br />
19746 isHandle   True if the element passed in triggers<br />
19747            dragging itself, else false
19748 </pre>
19749      */
19750         register : function(el, data){
19751             data = data || {};
19752             if(typeof el == "string"){
19753                 el = document.getElementById(el);
19754             }
19755             data.ddel = el;
19756             elements[getId(el)] = data;
19757             if(data.isHandle !== false){
19758                 handles[data.ddel.id] = data;
19759             }
19760             if(data.handles){
19761                 var hs = data.handles;
19762                 for(var i = 0, len = hs.length; i < len; i++){
19763                         handles[getId(hs[i])] = data;
19764                 }
19765             }
19766         },
19767
19768     /**
19769      * Unregister a drag drop element
19770      * @param {String|HTMLElement}  element The id or DOM node to unregister
19771      */
19772         unregister : function(el){
19773             var id = getId(el, false);
19774             var data = elements[id];
19775             if(data){
19776                 delete elements[id];
19777                 if(data.handles){
19778                     var hs = data.handles;
19779                     for(var i = 0, len = hs.length; i < len; i++){
19780                         delete handles[getId(hs[i], false)];
19781                     }
19782                 }
19783             }
19784         },
19785
19786     /**
19787      * Returns the handle registered for a DOM Node by id
19788      * @param {String|HTMLElement} id The DOM node or id to look up
19789      * @return {Object} handle The custom handle data
19790      */
19791         getHandle : function(id){
19792             if(typeof id != "string"){ // must be element?
19793                 id = id.id;
19794             }
19795             return handles[id];
19796         },
19797
19798     /**
19799      * Returns the handle that is registered for the DOM node that is the target of the event
19800      * @param {Event} e The event
19801      * @return {Object} handle The custom handle data
19802      */
19803         getHandleFromEvent : function(e){
19804             var t = Roo.lib.Event.getTarget(e);
19805             return t ? handles[t.id] : null;
19806         },
19807
19808     /**
19809      * Returns a custom data object that is registered for a DOM node by id
19810      * @param {String|HTMLElement} id The DOM node or id to look up
19811      * @return {Object} data The custom data
19812      */
19813         getTarget : function(id){
19814             if(typeof id != "string"){ // must be element?
19815                 id = id.id;
19816             }
19817             return elements[id];
19818         },
19819
19820     /**
19821      * Returns a custom data object that is registered for the DOM node that is the target of the event
19822      * @param {Event} e The event
19823      * @return {Object} data The custom data
19824      */
19825         getTargetFromEvent : function(e){
19826             var t = Roo.lib.Event.getTarget(e);
19827             return t ? elements[t.id] || handles[t.id] : null;
19828         }
19829     };
19830 }();/*
19831  * Based on:
19832  * Ext JS Library 1.1.1
19833  * Copyright(c) 2006-2007, Ext JS, LLC.
19834  *
19835  * Originally Released Under LGPL - original licence link has changed is not relivant.
19836  *
19837  * Fork - LGPL
19838  * <script type="text/javascript">
19839  */
19840  
19841
19842 /**
19843  * @class Roo.dd.StatusProxy
19844  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19845  * default drag proxy used by all Roo.dd components.
19846  * @constructor
19847  * @param {Object} config
19848  */
19849 Roo.dd.StatusProxy = function(config){
19850     Roo.apply(this, config);
19851     this.id = this.id || Roo.id();
19852     this.el = new Roo.Layer({
19853         dh: {
19854             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19855                 {tag: "div", cls: "x-dd-drop-icon"},
19856                 {tag: "div", cls: "x-dd-drag-ghost"}
19857             ]
19858         }, 
19859         shadow: !config || config.shadow !== false
19860     });
19861     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19862     this.dropStatus = this.dropNotAllowed;
19863 };
19864
19865 Roo.dd.StatusProxy.prototype = {
19866     /**
19867      * @cfg {String} dropAllowed
19868      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19869      */
19870     dropAllowed : "x-dd-drop-ok",
19871     /**
19872      * @cfg {String} dropNotAllowed
19873      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19874      */
19875     dropNotAllowed : "x-dd-drop-nodrop",
19876
19877     /**
19878      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19879      * over the current target element.
19880      * @param {String} cssClass The css class for the new drop status indicator image
19881      */
19882     setStatus : function(cssClass){
19883         cssClass = cssClass || this.dropNotAllowed;
19884         if(this.dropStatus != cssClass){
19885             this.el.replaceClass(this.dropStatus, cssClass);
19886             this.dropStatus = cssClass;
19887         }
19888     },
19889
19890     /**
19891      * Resets the status indicator to the default dropNotAllowed value
19892      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19893      */
19894     reset : function(clearGhost){
19895         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19896         this.dropStatus = this.dropNotAllowed;
19897         if(clearGhost){
19898             this.ghost.update("");
19899         }
19900     },
19901
19902     /**
19903      * Updates the contents of the ghost element
19904      * @param {String} html The html that will replace the current innerHTML of the ghost element
19905      */
19906     update : function(html){
19907         if(typeof html == "string"){
19908             this.ghost.update(html);
19909         }else{
19910             this.ghost.update("");
19911             html.style.margin = "0";
19912             this.ghost.dom.appendChild(html);
19913         }
19914         // ensure float = none set?? cant remember why though.
19915         var el = this.ghost.dom.firstChild;
19916                 if(el){
19917                         Roo.fly(el).setStyle('float', 'none');
19918                 }
19919     },
19920     
19921     /**
19922      * Returns the underlying proxy {@link Roo.Layer}
19923      * @return {Roo.Layer} el
19924     */
19925     getEl : function(){
19926         return this.el;
19927     },
19928
19929     /**
19930      * Returns the ghost element
19931      * @return {Roo.Element} el
19932      */
19933     getGhost : function(){
19934         return this.ghost;
19935     },
19936
19937     /**
19938      * Hides the proxy
19939      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19940      */
19941     hide : function(clear){
19942         this.el.hide();
19943         if(clear){
19944             this.reset(true);
19945         }
19946     },
19947
19948     /**
19949      * Stops the repair animation if it's currently running
19950      */
19951     stop : function(){
19952         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19953             this.anim.stop();
19954         }
19955     },
19956
19957     /**
19958      * Displays this proxy
19959      */
19960     show : function(){
19961         this.el.show();
19962     },
19963
19964     /**
19965      * Force the Layer to sync its shadow and shim positions to the element
19966      */
19967     sync : function(){
19968         this.el.sync();
19969     },
19970
19971     /**
19972      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19973      * invalid drop operation by the item being dragged.
19974      * @param {Array} xy The XY position of the element ([x, y])
19975      * @param {Function} callback The function to call after the repair is complete
19976      * @param {Object} scope The scope in which to execute the callback
19977      */
19978     repair : function(xy, callback, scope){
19979         this.callback = callback;
19980         this.scope = scope;
19981         if(xy && this.animRepair !== false){
19982             this.el.addClass("x-dd-drag-repair");
19983             this.el.hideUnders(true);
19984             this.anim = this.el.shift({
19985                 duration: this.repairDuration || .5,
19986                 easing: 'easeOut',
19987                 xy: xy,
19988                 stopFx: true,
19989                 callback: this.afterRepair,
19990                 scope: this
19991             });
19992         }else{
19993             this.afterRepair();
19994         }
19995     },
19996
19997     // private
19998     afterRepair : function(){
19999         this.hide(true);
20000         if(typeof this.callback == "function"){
20001             this.callback.call(this.scope || this);
20002         }
20003         this.callback = null;
20004         this.scope = null;
20005     }
20006 };/*
20007  * Based on:
20008  * Ext JS Library 1.1.1
20009  * Copyright(c) 2006-2007, Ext JS, LLC.
20010  *
20011  * Originally Released Under LGPL - original licence link has changed is not relivant.
20012  *
20013  * Fork - LGPL
20014  * <script type="text/javascript">
20015  */
20016
20017 /**
20018  * @class Roo.dd.DragSource
20019  * @extends Roo.dd.DDProxy
20020  * A simple class that provides the basic implementation needed to make any element draggable.
20021  * @constructor
20022  * @param {String/HTMLElement/Element} el The container element
20023  * @param {Object} config
20024  */
20025 Roo.dd.DragSource = function(el, config){
20026     this.el = Roo.get(el);
20027     this.dragData = {};
20028     
20029     Roo.apply(this, config);
20030     
20031     if(!this.proxy){
20032         this.proxy = new Roo.dd.StatusProxy();
20033     }
20034
20035     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
20036           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
20037     
20038     this.dragging = false;
20039 };
20040
20041 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
20042     /**
20043      * @cfg {String} dropAllowed
20044      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20045      */
20046     dropAllowed : "x-dd-drop-ok",
20047     /**
20048      * @cfg {String} dropNotAllowed
20049      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20050      */
20051     dropNotAllowed : "x-dd-drop-nodrop",
20052
20053     /**
20054      * Returns the data object associated with this drag source
20055      * @return {Object} data An object containing arbitrary data
20056      */
20057     getDragData : function(e){
20058         return this.dragData;
20059     },
20060
20061     // private
20062     onDragEnter : function(e, id){
20063         var target = Roo.dd.DragDropMgr.getDDById(id);
20064         this.cachedTarget = target;
20065         if(this.beforeDragEnter(target, e, id) !== false){
20066             if(target.isNotifyTarget){
20067                 var status = target.notifyEnter(this, e, this.dragData);
20068                 this.proxy.setStatus(status);
20069             }else{
20070                 this.proxy.setStatus(this.dropAllowed);
20071             }
20072             
20073             if(this.afterDragEnter){
20074                 /**
20075                  * An empty function by default, but provided so that you can perform a custom action
20076                  * when the dragged item enters the drop target by providing an implementation.
20077                  * @param {Roo.dd.DragDrop} target The drop target
20078                  * @param {Event} e The event object
20079                  * @param {String} id The id of the dragged element
20080                  * @method afterDragEnter
20081                  */
20082                 this.afterDragEnter(target, e, id);
20083             }
20084         }
20085     },
20086
20087     /**
20088      * An empty function by default, but provided so that you can perform a custom action
20089      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
20090      * @param {Roo.dd.DragDrop} target The drop target
20091      * @param {Event} e The event object
20092      * @param {String} id The id of the dragged element
20093      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20094      */
20095     beforeDragEnter : function(target, e, id){
20096         return true;
20097     },
20098
20099     // private
20100     alignElWithMouse: function() {
20101         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
20102         this.proxy.sync();
20103     },
20104
20105     // private
20106     onDragOver : function(e, id){
20107         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20108         if(this.beforeDragOver(target, e, id) !== false){
20109             if(target.isNotifyTarget){
20110                 var status = target.notifyOver(this, e, this.dragData);
20111                 this.proxy.setStatus(status);
20112             }
20113
20114             if(this.afterDragOver){
20115                 /**
20116                  * An empty function by default, but provided so that you can perform a custom action
20117                  * while the dragged item is over the drop target by providing an implementation.
20118                  * @param {Roo.dd.DragDrop} target The drop target
20119                  * @param {Event} e The event object
20120                  * @param {String} id The id of the dragged element
20121                  * @method afterDragOver
20122                  */
20123                 this.afterDragOver(target, e, id);
20124             }
20125         }
20126     },
20127
20128     /**
20129      * An empty function by default, but provided so that you can perform a custom action
20130      * while the dragged item is over the drop target and optionally cancel the onDragOver.
20131      * @param {Roo.dd.DragDrop} target The drop target
20132      * @param {Event} e The event object
20133      * @param {String} id The id of the dragged element
20134      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20135      */
20136     beforeDragOver : function(target, e, id){
20137         return true;
20138     },
20139
20140     // private
20141     onDragOut : function(e, id){
20142         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20143         if(this.beforeDragOut(target, e, id) !== false){
20144             if(target.isNotifyTarget){
20145                 target.notifyOut(this, e, this.dragData);
20146             }
20147             this.proxy.reset();
20148             if(this.afterDragOut){
20149                 /**
20150                  * An empty function by default, but provided so that you can perform a custom action
20151                  * after the dragged item is dragged out of the target without dropping.
20152                  * @param {Roo.dd.DragDrop} target The drop target
20153                  * @param {Event} e The event object
20154                  * @param {String} id The id of the dragged element
20155                  * @method afterDragOut
20156                  */
20157                 this.afterDragOut(target, e, id);
20158             }
20159         }
20160         this.cachedTarget = null;
20161     },
20162
20163     /**
20164      * An empty function by default, but provided so that you can perform a custom action before the dragged
20165      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20166      * @param {Roo.dd.DragDrop} target The drop target
20167      * @param {Event} e The event object
20168      * @param {String} id The id of the dragged element
20169      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20170      */
20171     beforeDragOut : function(target, e, id){
20172         return true;
20173     },
20174     
20175     // private
20176     onDragDrop : function(e, id){
20177         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20178         if(this.beforeDragDrop(target, e, id) !== false){
20179             if(target.isNotifyTarget){
20180                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20181                     this.onValidDrop(target, e, id);
20182                 }else{
20183                     this.onInvalidDrop(target, e, id);
20184                 }
20185             }else{
20186                 this.onValidDrop(target, e, id);
20187             }
20188             
20189             if(this.afterDragDrop){
20190                 /**
20191                  * An empty function by default, but provided so that you can perform a custom action
20192                  * after a valid drag drop has occurred by providing an implementation.
20193                  * @param {Roo.dd.DragDrop} target The drop target
20194                  * @param {Event} e The event object
20195                  * @param {String} id The id of the dropped element
20196                  * @method afterDragDrop
20197                  */
20198                 this.afterDragDrop(target, e, id);
20199             }
20200         }
20201         delete this.cachedTarget;
20202     },
20203
20204     /**
20205      * An empty function by default, but provided so that you can perform a custom action before the dragged
20206      * item is dropped onto the target and optionally cancel the onDragDrop.
20207      * @param {Roo.dd.DragDrop} target The drop target
20208      * @param {Event} e The event object
20209      * @param {String} id The id of the dragged element
20210      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20211      */
20212     beforeDragDrop : function(target, e, id){
20213         return true;
20214     },
20215
20216     // private
20217     onValidDrop : function(target, e, id){
20218         this.hideProxy();
20219         if(this.afterValidDrop){
20220             /**
20221              * An empty function by default, but provided so that you can perform a custom action
20222              * after a valid drop has occurred by providing an implementation.
20223              * @param {Object} target The target DD 
20224              * @param {Event} e The event object
20225              * @param {String} id The id of the dropped element
20226              * @method afterInvalidDrop
20227              */
20228             this.afterValidDrop(target, e, id);
20229         }
20230     },
20231
20232     // private
20233     getRepairXY : function(e, data){
20234         return this.el.getXY();  
20235     },
20236
20237     // private
20238     onInvalidDrop : function(target, e, id){
20239         this.beforeInvalidDrop(target, e, id);
20240         if(this.cachedTarget){
20241             if(this.cachedTarget.isNotifyTarget){
20242                 this.cachedTarget.notifyOut(this, e, this.dragData);
20243             }
20244             this.cacheTarget = null;
20245         }
20246         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20247
20248         if(this.afterInvalidDrop){
20249             /**
20250              * An empty function by default, but provided so that you can perform a custom action
20251              * after an invalid drop has occurred by providing an implementation.
20252              * @param {Event} e The event object
20253              * @param {String} id The id of the dropped element
20254              * @method afterInvalidDrop
20255              */
20256             this.afterInvalidDrop(e, id);
20257         }
20258     },
20259
20260     // private
20261     afterRepair : function(){
20262         if(Roo.enableFx){
20263             this.el.highlight(this.hlColor || "c3daf9");
20264         }
20265         this.dragging = false;
20266     },
20267
20268     /**
20269      * An empty function by default, but provided so that you can perform a custom action after an invalid
20270      * drop has occurred.
20271      * @param {Roo.dd.DragDrop} target The drop target
20272      * @param {Event} e The event object
20273      * @param {String} id The id of the dragged element
20274      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20275      */
20276     beforeInvalidDrop : function(target, e, id){
20277         return true;
20278     },
20279
20280     // private
20281     handleMouseDown : function(e){
20282         if(this.dragging) {
20283             return;
20284         }
20285         var data = this.getDragData(e);
20286         if(data && this.onBeforeDrag(data, e) !== false){
20287             this.dragData = data;
20288             this.proxy.stop();
20289             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20290         } 
20291     },
20292
20293     /**
20294      * An empty function by default, but provided so that you can perform a custom action before the initial
20295      * drag event begins and optionally cancel it.
20296      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20297      * @param {Event} e The event object
20298      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20299      */
20300     onBeforeDrag : function(data, e){
20301         return true;
20302     },
20303
20304     /**
20305      * An empty function by default, but provided so that you can perform a custom action once the initial
20306      * drag event has begun.  The drag cannot be canceled from this function.
20307      * @param {Number} x The x position of the click on the dragged object
20308      * @param {Number} y The y position of the click on the dragged object
20309      */
20310     onStartDrag : Roo.emptyFn,
20311
20312     // private - YUI override
20313     startDrag : function(x, y){
20314         this.proxy.reset();
20315         this.dragging = true;
20316         this.proxy.update("");
20317         this.onInitDrag(x, y);
20318         this.proxy.show();
20319     },
20320
20321     // private
20322     onInitDrag : function(x, y){
20323         var clone = this.el.dom.cloneNode(true);
20324         clone.id = Roo.id(); // prevent duplicate ids
20325         this.proxy.update(clone);
20326         this.onStartDrag(x, y);
20327         return true;
20328     },
20329
20330     /**
20331      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20332      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20333      */
20334     getProxy : function(){
20335         return this.proxy;  
20336     },
20337
20338     /**
20339      * Hides the drag source's {@link Roo.dd.StatusProxy}
20340      */
20341     hideProxy : function(){
20342         this.proxy.hide();  
20343         this.proxy.reset(true);
20344         this.dragging = false;
20345     },
20346
20347     // private
20348     triggerCacheRefresh : function(){
20349         Roo.dd.DDM.refreshCache(this.groups);
20350     },
20351
20352     // private - override to prevent hiding
20353     b4EndDrag: function(e) {
20354     },
20355
20356     // private - override to prevent moving
20357     endDrag : function(e){
20358         this.onEndDrag(this.dragData, e);
20359     },
20360
20361     // private
20362     onEndDrag : function(data, e){
20363     },
20364     
20365     // private - pin to cursor
20366     autoOffset : function(x, y) {
20367         this.setDelta(-12, -20);
20368     }    
20369 });/*
20370  * Based on:
20371  * Ext JS Library 1.1.1
20372  * Copyright(c) 2006-2007, Ext JS, LLC.
20373  *
20374  * Originally Released Under LGPL - original licence link has changed is not relivant.
20375  *
20376  * Fork - LGPL
20377  * <script type="text/javascript">
20378  */
20379
20380
20381 /**
20382  * @class Roo.dd.DropTarget
20383  * @extends Roo.dd.DDTarget
20384  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20385  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20386  * @constructor
20387  * @param {String/HTMLElement/Element} el The container element
20388  * @param {Object} config
20389  */
20390 Roo.dd.DropTarget = function(el, config){
20391     this.el = Roo.get(el);
20392     
20393     var listeners = false; ;
20394     if (config && config.listeners) {
20395         listeners= config.listeners;
20396         delete config.listeners;
20397     }
20398     Roo.apply(this, config);
20399     
20400     if(this.containerScroll){
20401         Roo.dd.ScrollManager.register(this.el);
20402     }
20403     this.addEvents( {
20404          /**
20405          * @scope Roo.dd.DropTarget
20406          */
20407          
20408          /**
20409          * @event enter
20410          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20411          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20412          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20413          * 
20414          * IMPORTANT : it should set this.overClass and this.dropAllowed
20415          * 
20416          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20417          * @param {Event} e The event
20418          * @param {Object} data An object containing arbitrary data supplied by the drag source
20419          */
20420         "enter" : true,
20421         
20422          /**
20423          * @event over
20424          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20425          * This method will be called on every mouse movement while the drag source is over the drop target.
20426          * This default implementation simply returns the dropAllowed config value.
20427          * 
20428          * IMPORTANT : it should set this.dropAllowed
20429          * 
20430          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20431          * @param {Event} e The event
20432          * @param {Object} data An object containing arbitrary data supplied by the drag source
20433          
20434          */
20435         "over" : true,
20436         /**
20437          * @event out
20438          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20439          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20440          * overClass (if any) from the drop element.
20441          * 
20442          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20443          * @param {Event} e The event
20444          * @param {Object} data An object containing arbitrary data supplied by the drag source
20445          */
20446          "out" : true,
20447          
20448         /**
20449          * @event drop
20450          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20451          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20452          * implementation that does something to process the drop event and returns true so that the drag source's
20453          * repair action does not run.
20454          * 
20455          * IMPORTANT : it should set this.success
20456          * 
20457          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20458          * @param {Event} e The event
20459          * @param {Object} data An object containing arbitrary data supplied by the drag source
20460         */
20461          "drop" : true
20462     });
20463             
20464      
20465     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20466         this.el.dom, 
20467         this.ddGroup || this.group,
20468         {
20469             isTarget: true,
20470             listeners : listeners || {} 
20471            
20472         
20473         }
20474     );
20475
20476 };
20477
20478 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20479     /**
20480      * @cfg {String} overClass
20481      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20482      */
20483      /**
20484      * @cfg {String} ddGroup
20485      * The drag drop group to handle drop events for
20486      */
20487      
20488     /**
20489      * @cfg {String} dropAllowed
20490      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20491      */
20492     dropAllowed : "x-dd-drop-ok",
20493     /**
20494      * @cfg {String} dropNotAllowed
20495      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20496      */
20497     dropNotAllowed : "x-dd-drop-nodrop",
20498     /**
20499      * @cfg {boolean} success
20500      * set this after drop listener.. 
20501      */
20502     success : false,
20503     /**
20504      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20505      * if the drop point is valid for over/enter..
20506      */
20507     valid : false,
20508     // private
20509     isTarget : true,
20510
20511     // private
20512     isNotifyTarget : true,
20513     
20514     /**
20515      * @hide
20516      */
20517     notifyEnter : function(dd, e, data)
20518     {
20519         this.valid = true;
20520         this.fireEvent('enter', dd, e, data);
20521         if(this.overClass){
20522             this.el.addClass(this.overClass);
20523         }
20524         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20525             this.valid ? this.dropAllowed : this.dropNotAllowed
20526         );
20527     },
20528
20529     /**
20530      * @hide
20531      */
20532     notifyOver : function(dd, e, data)
20533     {
20534         this.valid = true;
20535         this.fireEvent('over', dd, e, data);
20536         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20537             this.valid ? this.dropAllowed : this.dropNotAllowed
20538         );
20539     },
20540
20541     /**
20542      * @hide
20543      */
20544     notifyOut : function(dd, e, data)
20545     {
20546         this.fireEvent('out', dd, e, data);
20547         if(this.overClass){
20548             this.el.removeClass(this.overClass);
20549         }
20550     },
20551
20552     /**
20553      * @hide
20554      */
20555     notifyDrop : function(dd, e, data)
20556     {
20557         this.success = false;
20558         this.fireEvent('drop', dd, e, data);
20559         return this.success;
20560     }
20561 });/*
20562  * Based on:
20563  * Ext JS Library 1.1.1
20564  * Copyright(c) 2006-2007, Ext JS, LLC.
20565  *
20566  * Originally Released Under LGPL - original licence link has changed is not relivant.
20567  *
20568  * Fork - LGPL
20569  * <script type="text/javascript">
20570  */
20571
20572
20573 /**
20574  * @class Roo.dd.DragZone
20575  * @extends Roo.dd.DragSource
20576  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20577  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20578  * @constructor
20579  * @param {String/HTMLElement/Element} el The container element
20580  * @param {Object} config
20581  */
20582 Roo.dd.DragZone = function(el, config){
20583     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20584     if(this.containerScroll){
20585         Roo.dd.ScrollManager.register(this.el);
20586     }
20587 };
20588
20589 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20590     /**
20591      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20592      * for auto scrolling during drag operations.
20593      */
20594     /**
20595      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20596      * method after a failed drop (defaults to "c3daf9" - light blue)
20597      */
20598
20599     /**
20600      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20601      * for a valid target to drag based on the mouse down. Override this method
20602      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20603      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20604      * @param {EventObject} e The mouse down event
20605      * @return {Object} The dragData
20606      */
20607     getDragData : function(e){
20608         return Roo.dd.Registry.getHandleFromEvent(e);
20609     },
20610     
20611     /**
20612      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20613      * this.dragData.ddel
20614      * @param {Number} x The x position of the click on the dragged object
20615      * @param {Number} y The y position of the click on the dragged object
20616      * @return {Boolean} true to continue the drag, false to cancel
20617      */
20618     onInitDrag : function(x, y){
20619         this.proxy.update(this.dragData.ddel.cloneNode(true));
20620         this.onStartDrag(x, y);
20621         return true;
20622     },
20623     
20624     /**
20625      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20626      */
20627     afterRepair : function(){
20628         if(Roo.enableFx){
20629             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20630         }
20631         this.dragging = false;
20632     },
20633
20634     /**
20635      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20636      * the XY of this.dragData.ddel
20637      * @param {EventObject} e The mouse up event
20638      * @return {Array} The xy location (e.g. [100, 200])
20639      */
20640     getRepairXY : function(e){
20641         return Roo.Element.fly(this.dragData.ddel).getXY();  
20642     }
20643 });/*
20644  * Based on:
20645  * Ext JS Library 1.1.1
20646  * Copyright(c) 2006-2007, Ext JS, LLC.
20647  *
20648  * Originally Released Under LGPL - original licence link has changed is not relivant.
20649  *
20650  * Fork - LGPL
20651  * <script type="text/javascript">
20652  */
20653 /**
20654  * @class Roo.dd.DropZone
20655  * @extends Roo.dd.DropTarget
20656  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20657  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20658  * @constructor
20659  * @param {String/HTMLElement/Element} el The container element
20660  * @param {Object} config
20661  */
20662 Roo.dd.DropZone = function(el, config){
20663     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20664 };
20665
20666 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20667     /**
20668      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20669      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20670      * provide your own custom lookup.
20671      * @param {Event} e The event
20672      * @return {Object} data The custom data
20673      */
20674     getTargetFromEvent : function(e){
20675         return Roo.dd.Registry.getTargetFromEvent(e);
20676     },
20677
20678     /**
20679      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20680      * that it has registered.  This method has no default implementation and should be overridden to provide
20681      * node-specific processing if necessary.
20682      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20683      * {@link #getTargetFromEvent} for this node)
20684      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20685      * @param {Event} e The event
20686      * @param {Object} data An object containing arbitrary data supplied by the drag source
20687      */
20688     onNodeEnter : function(n, dd, e, data){
20689         
20690     },
20691
20692     /**
20693      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20694      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20695      * overridden to provide the proper feedback.
20696      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20697      * {@link #getTargetFromEvent} for this node)
20698      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20699      * @param {Event} e The event
20700      * @param {Object} data An object containing arbitrary data supplied by the drag source
20701      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20702      * underlying {@link Roo.dd.StatusProxy} can be updated
20703      */
20704     onNodeOver : function(n, dd, e, data){
20705         return this.dropAllowed;
20706     },
20707
20708     /**
20709      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20710      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20711      * node-specific processing if necessary.
20712      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20713      * {@link #getTargetFromEvent} for this node)
20714      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20715      * @param {Event} e The event
20716      * @param {Object} data An object containing arbitrary data supplied by the drag source
20717      */
20718     onNodeOut : function(n, dd, e, data){
20719         
20720     },
20721
20722     /**
20723      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20724      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20725      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20726      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20727      * {@link #getTargetFromEvent} for this node)
20728      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20729      * @param {Event} e The event
20730      * @param {Object} data An object containing arbitrary data supplied by the drag source
20731      * @return {Boolean} True if the drop was valid, else false
20732      */
20733     onNodeDrop : function(n, dd, e, data){
20734         return false;
20735     },
20736
20737     /**
20738      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20739      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20740      * it should be overridden to provide the proper feedback if necessary.
20741      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20742      * @param {Event} e The event
20743      * @param {Object} data An object containing arbitrary data supplied by the drag source
20744      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20745      * underlying {@link Roo.dd.StatusProxy} can be updated
20746      */
20747     onContainerOver : function(dd, e, data){
20748         return this.dropNotAllowed;
20749     },
20750
20751     /**
20752      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20753      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20754      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20755      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20756      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20757      * @param {Event} e The event
20758      * @param {Object} data An object containing arbitrary data supplied by the drag source
20759      * @return {Boolean} True if the drop was valid, else false
20760      */
20761     onContainerDrop : function(dd, e, data){
20762         return false;
20763     },
20764
20765     /**
20766      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20767      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20768      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20769      * you should override this method and provide a custom implementation.
20770      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20771      * @param {Event} e The event
20772      * @param {Object} data An object containing arbitrary data supplied by the drag source
20773      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20774      * underlying {@link Roo.dd.StatusProxy} can be updated
20775      */
20776     notifyEnter : function(dd, e, data){
20777         return this.dropNotAllowed;
20778     },
20779
20780     /**
20781      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20782      * This method will be called on every mouse movement while the drag source is over the drop zone.
20783      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20784      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20785      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20786      * registered node, it will call {@link #onContainerOver}.
20787      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20788      * @param {Event} e The event
20789      * @param {Object} data An object containing arbitrary data supplied by the drag source
20790      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20791      * underlying {@link Roo.dd.StatusProxy} can be updated
20792      */
20793     notifyOver : function(dd, e, data){
20794         var n = this.getTargetFromEvent(e);
20795         if(!n){ // not over valid drop target
20796             if(this.lastOverNode){
20797                 this.onNodeOut(this.lastOverNode, dd, e, data);
20798                 this.lastOverNode = null;
20799             }
20800             return this.onContainerOver(dd, e, data);
20801         }
20802         if(this.lastOverNode != n){
20803             if(this.lastOverNode){
20804                 this.onNodeOut(this.lastOverNode, dd, e, data);
20805             }
20806             this.onNodeEnter(n, dd, e, data);
20807             this.lastOverNode = n;
20808         }
20809         return this.onNodeOver(n, dd, e, data);
20810     },
20811
20812     /**
20813      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20814      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20815      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20816      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20817      * @param {Event} e The event
20818      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20819      */
20820     notifyOut : function(dd, e, data){
20821         if(this.lastOverNode){
20822             this.onNodeOut(this.lastOverNode, dd, e, data);
20823             this.lastOverNode = null;
20824         }
20825     },
20826
20827     /**
20828      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20829      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20830      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20831      * otherwise it will call {@link #onContainerDrop}.
20832      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20833      * @param {Event} e The event
20834      * @param {Object} data An object containing arbitrary data supplied by the drag source
20835      * @return {Boolean} True if the drop was valid, else false
20836      */
20837     notifyDrop : function(dd, e, data){
20838         if(this.lastOverNode){
20839             this.onNodeOut(this.lastOverNode, dd, e, data);
20840             this.lastOverNode = null;
20841         }
20842         var n = this.getTargetFromEvent(e);
20843         return n ?
20844             this.onNodeDrop(n, dd, e, data) :
20845             this.onContainerDrop(dd, e, data);
20846     },
20847
20848     // private
20849     triggerCacheRefresh : function(){
20850         Roo.dd.DDM.refreshCache(this.groups);
20851     }  
20852 });/*
20853  * Based on:
20854  * Ext JS Library 1.1.1
20855  * Copyright(c) 2006-2007, Ext JS, LLC.
20856  *
20857  * Originally Released Under LGPL - original licence link has changed is not relivant.
20858  *
20859  * Fork - LGPL
20860  * <script type="text/javascript">
20861  */
20862
20863
20864 /**
20865  * @class Roo.data.SortTypes
20866  * @singleton
20867  * Defines the default sorting (casting?) comparison functions used when sorting data.
20868  */
20869 Roo.data.SortTypes = {
20870     /**
20871      * Default sort that does nothing
20872      * @param {Mixed} s The value being converted
20873      * @return {Mixed} The comparison value
20874      */
20875     none : function(s){
20876         return s;
20877     },
20878     
20879     /**
20880      * The regular expression used to strip tags
20881      * @type {RegExp}
20882      * @property
20883      */
20884     stripTagsRE : /<\/?[^>]+>/gi,
20885     
20886     /**
20887      * Strips all HTML tags to sort on text only
20888      * @param {Mixed} s The value being converted
20889      * @return {String} The comparison value
20890      */
20891     asText : function(s){
20892         return String(s).replace(this.stripTagsRE, "");
20893     },
20894     
20895     /**
20896      * Strips all HTML tags to sort on text only - Case insensitive
20897      * @param {Mixed} s The value being converted
20898      * @return {String} The comparison value
20899      */
20900     asUCText : function(s){
20901         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20902     },
20903     
20904     /**
20905      * Case insensitive string
20906      * @param {Mixed} s The value being converted
20907      * @return {String} The comparison value
20908      */
20909     asUCString : function(s) {
20910         return String(s).toUpperCase();
20911     },
20912     
20913     /**
20914      * Date sorting
20915      * @param {Mixed} s The value being converted
20916      * @return {Number} The comparison value
20917      */
20918     asDate : function(s) {
20919         if(!s){
20920             return 0;
20921         }
20922         if(s instanceof Date){
20923             return s.getTime();
20924         }
20925         return Date.parse(String(s));
20926     },
20927     
20928     /**
20929      * Float sorting
20930      * @param {Mixed} s The value being converted
20931      * @return {Float} The comparison value
20932      */
20933     asFloat : function(s) {
20934         var val = parseFloat(String(s).replace(/,/g, ""));
20935         if(isNaN(val)) val = 0;
20936         return val;
20937     },
20938     
20939     /**
20940      * Integer sorting
20941      * @param {Mixed} s The value being converted
20942      * @return {Number} The comparison value
20943      */
20944     asInt : function(s) {
20945         var val = parseInt(String(s).replace(/,/g, ""));
20946         if(isNaN(val)) val = 0;
20947         return val;
20948     }
20949 };/*
20950  * Based on:
20951  * Ext JS Library 1.1.1
20952  * Copyright(c) 2006-2007, Ext JS, LLC.
20953  *
20954  * Originally Released Under LGPL - original licence link has changed is not relivant.
20955  *
20956  * Fork - LGPL
20957  * <script type="text/javascript">
20958  */
20959
20960 /**
20961 * @class Roo.data.Record
20962  * Instances of this class encapsulate both record <em>definition</em> information, and record
20963  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20964  * to access Records cached in an {@link Roo.data.Store} object.<br>
20965  * <p>
20966  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20967  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20968  * objects.<br>
20969  * <p>
20970  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20971  * @constructor
20972  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20973  * {@link #create}. The parameters are the same.
20974  * @param {Array} data An associative Array of data values keyed by the field name.
20975  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20976  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20977  * not specified an integer id is generated.
20978  */
20979 Roo.data.Record = function(data, id){
20980     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20981     this.data = data;
20982 };
20983
20984 /**
20985  * Generate a constructor for a specific record layout.
20986  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20987  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20988  * Each field definition object may contain the following properties: <ul>
20989  * <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,
20990  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20991  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20992  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20993  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20994  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20995  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20996  * this may be omitted.</p></li>
20997  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20998  * <ul><li>auto (Default, implies no conversion)</li>
20999  * <li>string</li>
21000  * <li>int</li>
21001  * <li>float</li>
21002  * <li>boolean</li>
21003  * <li>date</li></ul></p></li>
21004  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
21005  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
21006  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
21007  * by the Reader into an object that will be stored in the Record. It is passed the
21008  * following parameters:<ul>
21009  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
21010  * </ul></p></li>
21011  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
21012  * </ul>
21013  * <br>usage:<br><pre><code>
21014 var TopicRecord = Roo.data.Record.create(
21015     {name: 'title', mapping: 'topic_title'},
21016     {name: 'author', mapping: 'username'},
21017     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
21018     {name: 'lastPost', mapping: 'post_time', type: 'date'},
21019     {name: 'lastPoster', mapping: 'user2'},
21020     {name: 'excerpt', mapping: 'post_text'}
21021 );
21022
21023 var myNewRecord = new TopicRecord({
21024     title: 'Do my job please',
21025     author: 'noobie',
21026     totalPosts: 1,
21027     lastPost: new Date(),
21028     lastPoster: 'Animal',
21029     excerpt: 'No way dude!'
21030 });
21031 myStore.add(myNewRecord);
21032 </code></pre>
21033  * @method create
21034  * @static
21035  */
21036 Roo.data.Record.create = function(o){
21037     var f = function(){
21038         f.superclass.constructor.apply(this, arguments);
21039     };
21040     Roo.extend(f, Roo.data.Record);
21041     var p = f.prototype;
21042     p.fields = new Roo.util.MixedCollection(false, function(field){
21043         return field.name;
21044     });
21045     for(var i = 0, len = o.length; i < len; i++){
21046         p.fields.add(new Roo.data.Field(o[i]));
21047     }
21048     f.getField = function(name){
21049         return p.fields.get(name);  
21050     };
21051     return f;
21052 };
21053
21054 Roo.data.Record.AUTO_ID = 1000;
21055 Roo.data.Record.EDIT = 'edit';
21056 Roo.data.Record.REJECT = 'reject';
21057 Roo.data.Record.COMMIT = 'commit';
21058
21059 Roo.data.Record.prototype = {
21060     /**
21061      * Readonly flag - true if this record has been modified.
21062      * @type Boolean
21063      */
21064     dirty : false,
21065     editing : false,
21066     error: null,
21067     modified: null,
21068
21069     // private
21070     join : function(store){
21071         this.store = store;
21072     },
21073
21074     /**
21075      * Set the named field to the specified value.
21076      * @param {String} name The name of the field to set.
21077      * @param {Object} value The value to set the field to.
21078      */
21079     set : function(name, value){
21080         if(this.data[name] == value){
21081             return;
21082         }
21083         this.dirty = true;
21084         if(!this.modified){
21085             this.modified = {};
21086         }
21087         if(typeof this.modified[name] == 'undefined'){
21088             this.modified[name] = this.data[name];
21089         }
21090         this.data[name] = value;
21091         if(!this.editing && this.store){
21092             this.store.afterEdit(this);
21093         }       
21094     },
21095
21096     /**
21097      * Get the value of the named field.
21098      * @param {String} name The name of the field to get the value of.
21099      * @return {Object} The value of the field.
21100      */
21101     get : function(name){
21102         return this.data[name]; 
21103     },
21104
21105     // private
21106     beginEdit : function(){
21107         this.editing = true;
21108         this.modified = {}; 
21109     },
21110
21111     // private
21112     cancelEdit : function(){
21113         this.editing = false;
21114         delete this.modified;
21115     },
21116
21117     // private
21118     endEdit : function(){
21119         this.editing = false;
21120         if(this.dirty && this.store){
21121             this.store.afterEdit(this);
21122         }
21123     },
21124
21125     /**
21126      * Usually called by the {@link Roo.data.Store} which owns the Record.
21127      * Rejects all changes made to the Record since either creation, or the last commit operation.
21128      * Modified fields are reverted to their original values.
21129      * <p>
21130      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21131      * of reject operations.
21132      */
21133     reject : function(){
21134         var m = this.modified;
21135         for(var n in m){
21136             if(typeof m[n] != "function"){
21137                 this.data[n] = m[n];
21138             }
21139         }
21140         this.dirty = false;
21141         delete this.modified;
21142         this.editing = false;
21143         if(this.store){
21144             this.store.afterReject(this);
21145         }
21146     },
21147
21148     /**
21149      * Usually called by the {@link Roo.data.Store} which owns the Record.
21150      * Commits all changes made to the Record since either creation, or the last commit operation.
21151      * <p>
21152      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21153      * of commit operations.
21154      */
21155     commit : function(){
21156         this.dirty = false;
21157         delete this.modified;
21158         this.editing = false;
21159         if(this.store){
21160             this.store.afterCommit(this);
21161         }
21162     },
21163
21164     // private
21165     hasError : function(){
21166         return this.error != null;
21167     },
21168
21169     // private
21170     clearError : function(){
21171         this.error = null;
21172     },
21173
21174     /**
21175      * Creates a copy of this record.
21176      * @param {String} id (optional) A new record id if you don't want to use this record's id
21177      * @return {Record}
21178      */
21179     copy : function(newId) {
21180         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21181     }
21182 };/*
21183  * Based on:
21184  * Ext JS Library 1.1.1
21185  * Copyright(c) 2006-2007, Ext JS, LLC.
21186  *
21187  * Originally Released Under LGPL - original licence link has changed is not relivant.
21188  *
21189  * Fork - LGPL
21190  * <script type="text/javascript">
21191  */
21192
21193
21194
21195 /**
21196  * @class Roo.data.Store
21197  * @extends Roo.util.Observable
21198  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21199  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21200  * <p>
21201  * 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
21202  * has no knowledge of the format of the data returned by the Proxy.<br>
21203  * <p>
21204  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21205  * instances from the data object. These records are cached and made available through accessor functions.
21206  * @constructor
21207  * Creates a new Store.
21208  * @param {Object} config A config object containing the objects needed for the Store to access data,
21209  * and read the data into Records.
21210  */
21211 Roo.data.Store = function(config){
21212     this.data = new Roo.util.MixedCollection(false);
21213     this.data.getKey = function(o){
21214         return o.id;
21215     };
21216     this.baseParams = {};
21217     // private
21218     this.paramNames = {
21219         "start" : "start",
21220         "limit" : "limit",
21221         "sort" : "sort",
21222         "dir" : "dir",
21223         "multisort" : "_multisort"
21224     };
21225
21226     if(config && config.data){
21227         this.inlineData = config.data;
21228         delete config.data;
21229     }
21230
21231     Roo.apply(this, config);
21232     
21233     if(this.reader){ // reader passed
21234         this.reader = Roo.factory(this.reader, Roo.data);
21235         this.reader.xmodule = this.xmodule || false;
21236         if(!this.recordType){
21237             this.recordType = this.reader.recordType;
21238         }
21239         if(this.reader.onMetaChange){
21240             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21241         }
21242     }
21243
21244     if(this.recordType){
21245         this.fields = this.recordType.prototype.fields;
21246     }
21247     this.modified = [];
21248
21249     this.addEvents({
21250         /**
21251          * @event datachanged
21252          * Fires when the data cache has changed, and a widget which is using this Store
21253          * as a Record cache should refresh its view.
21254          * @param {Store} this
21255          */
21256         datachanged : true,
21257         /**
21258          * @event metachange
21259          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21260          * @param {Store} this
21261          * @param {Object} meta The JSON metadata
21262          */
21263         metachange : true,
21264         /**
21265          * @event add
21266          * Fires when Records have been added to the Store
21267          * @param {Store} this
21268          * @param {Roo.data.Record[]} records The array of Records added
21269          * @param {Number} index The index at which the record(s) were added
21270          */
21271         add : true,
21272         /**
21273          * @event remove
21274          * Fires when a Record has been removed from the Store
21275          * @param {Store} this
21276          * @param {Roo.data.Record} record The Record that was removed
21277          * @param {Number} index The index at which the record was removed
21278          */
21279         remove : true,
21280         /**
21281          * @event update
21282          * Fires when a Record has been updated
21283          * @param {Store} this
21284          * @param {Roo.data.Record} record The Record that was updated
21285          * @param {String} operation The update operation being performed.  Value may be one of:
21286          * <pre><code>
21287  Roo.data.Record.EDIT
21288  Roo.data.Record.REJECT
21289  Roo.data.Record.COMMIT
21290          * </code></pre>
21291          */
21292         update : true,
21293         /**
21294          * @event clear
21295          * Fires when the data cache has been cleared.
21296          * @param {Store} this
21297          */
21298         clear : true,
21299         /**
21300          * @event beforeload
21301          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21302          * the load action will be canceled.
21303          * @param {Store} this
21304          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21305          */
21306         beforeload : true,
21307         /**
21308          * @event beforeloadadd
21309          * Fires after a new set of Records has been loaded.
21310          * @param {Store} this
21311          * @param {Roo.data.Record[]} records The Records that were loaded
21312          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21313          */
21314         beforeloadadd : true,
21315         /**
21316          * @event load
21317          * Fires after a new set of Records has been loaded, before they are added to the store.
21318          * @param {Store} this
21319          * @param {Roo.data.Record[]} records The Records that were loaded
21320          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21321          * @params {Object} return from reader
21322          */
21323         load : true,
21324         /**
21325          * @event loadexception
21326          * Fires if an exception occurs in the Proxy during loading.
21327          * Called with the signature of the Proxy's "loadexception" event.
21328          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21329          * 
21330          * @param {Proxy} 
21331          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21332          * @param {Object} load options 
21333          * @param {Object} jsonData from your request (normally this contains the Exception)
21334          */
21335         loadexception : true
21336     });
21337     
21338     if(this.proxy){
21339         this.proxy = Roo.factory(this.proxy, Roo.data);
21340         this.proxy.xmodule = this.xmodule || false;
21341         this.relayEvents(this.proxy,  ["loadexception"]);
21342     }
21343     this.sortToggle = {};
21344     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21345
21346     Roo.data.Store.superclass.constructor.call(this);
21347
21348     if(this.inlineData){
21349         this.loadData(this.inlineData);
21350         delete this.inlineData;
21351     }
21352 };
21353
21354 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21355      /**
21356     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21357     * without a remote query - used by combo/forms at present.
21358     */
21359     
21360     /**
21361     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21362     */
21363     /**
21364     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21365     */
21366     /**
21367     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21368     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21369     */
21370     /**
21371     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21372     * on any HTTP request
21373     */
21374     /**
21375     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21376     */
21377     /**
21378     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21379     */
21380     multiSort: false,
21381     /**
21382     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21383     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21384     */
21385     remoteSort : false,
21386
21387     /**
21388     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21389      * loaded or when a record is removed. (defaults to false).
21390     */
21391     pruneModifiedRecords : false,
21392
21393     // private
21394     lastOptions : null,
21395
21396     /**
21397      * Add Records to the Store and fires the add event.
21398      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21399      */
21400     add : function(records){
21401         records = [].concat(records);
21402         for(var i = 0, len = records.length; i < len; i++){
21403             records[i].join(this);
21404         }
21405         var index = this.data.length;
21406         this.data.addAll(records);
21407         this.fireEvent("add", this, records, index);
21408     },
21409
21410     /**
21411      * Remove a Record from the Store and fires the remove event.
21412      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21413      */
21414     remove : function(record){
21415         var index = this.data.indexOf(record);
21416         this.data.removeAt(index);
21417         if(this.pruneModifiedRecords){
21418             this.modified.remove(record);
21419         }
21420         this.fireEvent("remove", this, record, index);
21421     },
21422
21423     /**
21424      * Remove all Records from the Store and fires the clear event.
21425      */
21426     removeAll : function(){
21427         this.data.clear();
21428         if(this.pruneModifiedRecords){
21429             this.modified = [];
21430         }
21431         this.fireEvent("clear", this);
21432     },
21433
21434     /**
21435      * Inserts Records to the Store at the given index and fires the add event.
21436      * @param {Number} index The start index at which to insert the passed Records.
21437      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21438      */
21439     insert : function(index, records){
21440         records = [].concat(records);
21441         for(var i = 0, len = records.length; i < len; i++){
21442             this.data.insert(index, records[i]);
21443             records[i].join(this);
21444         }
21445         this.fireEvent("add", this, records, index);
21446     },
21447
21448     /**
21449      * Get the index within the cache of the passed Record.
21450      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21451      * @return {Number} The index of the passed Record. Returns -1 if not found.
21452      */
21453     indexOf : function(record){
21454         return this.data.indexOf(record);
21455     },
21456
21457     /**
21458      * Get the index within the cache of the Record with the passed id.
21459      * @param {String} id The id of the Record to find.
21460      * @return {Number} The index of the Record. Returns -1 if not found.
21461      */
21462     indexOfId : function(id){
21463         return this.data.indexOfKey(id);
21464     },
21465
21466     /**
21467      * Get the Record with the specified id.
21468      * @param {String} id The id of the Record to find.
21469      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21470      */
21471     getById : function(id){
21472         return this.data.key(id);
21473     },
21474
21475     /**
21476      * Get the Record at the specified index.
21477      * @param {Number} index The index of the Record to find.
21478      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21479      */
21480     getAt : function(index){
21481         return this.data.itemAt(index);
21482     },
21483
21484     /**
21485      * Returns a range of Records between specified indices.
21486      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21487      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21488      * @return {Roo.data.Record[]} An array of Records
21489      */
21490     getRange : function(start, end){
21491         return this.data.getRange(start, end);
21492     },
21493
21494     // private
21495     storeOptions : function(o){
21496         o = Roo.apply({}, o);
21497         delete o.callback;
21498         delete o.scope;
21499         this.lastOptions = o;
21500     },
21501
21502     /**
21503      * Loads the Record cache from the configured Proxy using the configured Reader.
21504      * <p>
21505      * If using remote paging, then the first load call must specify the <em>start</em>
21506      * and <em>limit</em> properties in the options.params property to establish the initial
21507      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21508      * <p>
21509      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21510      * and this call will return before the new data has been loaded. Perform any post-processing
21511      * in a callback function, or in a "load" event handler.</strong>
21512      * <p>
21513      * @param {Object} options An object containing properties which control loading options:<ul>
21514      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21515      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21516      * passed the following arguments:<ul>
21517      * <li>r : Roo.data.Record[]</li>
21518      * <li>options: Options object from the load call</li>
21519      * <li>success: Boolean success indicator</li></ul></li>
21520      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21521      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21522      * </ul>
21523      */
21524     load : function(options){
21525         options = options || {};
21526         if(this.fireEvent("beforeload", this, options) !== false){
21527             this.storeOptions(options);
21528             var p = Roo.apply(options.params || {}, this.baseParams);
21529             // if meta was not loaded from remote source.. try requesting it.
21530             if (!this.reader.metaFromRemote) {
21531                 p._requestMeta = 1;
21532             }
21533             if(this.sortInfo && this.remoteSort){
21534                 var pn = this.paramNames;
21535                 p[pn["sort"]] = this.sortInfo.field;
21536                 p[pn["dir"]] = this.sortInfo.direction;
21537             }
21538             if (this.multiSort) {
21539                 var pn = this.paramNames;
21540                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21541             }
21542             
21543             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21544         }
21545     },
21546
21547     /**
21548      * Reloads the Record cache from the configured Proxy using the configured Reader and
21549      * the options from the last load operation performed.
21550      * @param {Object} options (optional) An object containing properties which may override the options
21551      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21552      * the most recently used options are reused).
21553      */
21554     reload : function(options){
21555         this.load(Roo.applyIf(options||{}, this.lastOptions));
21556     },
21557
21558     // private
21559     // Called as a callback by the Reader during a load operation.
21560     loadRecords : function(o, options, success){
21561         if(!o || success === false){
21562             if(success !== false){
21563                 this.fireEvent("load", this, [], options, o);
21564             }
21565             if(options.callback){
21566                 options.callback.call(options.scope || this, [], options, false);
21567             }
21568             return;
21569         }
21570         // if data returned failure - throw an exception.
21571         if (o.success === false) {
21572             // show a message if no listener is registered.
21573             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21574                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21575             }
21576             // loadmask wil be hooked into this..
21577             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21578             return;
21579         }
21580         var r = o.records, t = o.totalRecords || r.length;
21581         
21582         this.fireEvent("beforeloadadd", this, r, options, o);
21583         
21584         if(!options || options.add !== true){
21585             if(this.pruneModifiedRecords){
21586                 this.modified = [];
21587             }
21588             for(var i = 0, len = r.length; i < len; i++){
21589                 r[i].join(this);
21590             }
21591             if(this.snapshot){
21592                 this.data = this.snapshot;
21593                 delete this.snapshot;
21594             }
21595             this.data.clear();
21596             this.data.addAll(r);
21597             this.totalLength = t;
21598             this.applySort();
21599             this.fireEvent("datachanged", this);
21600         }else{
21601             this.totalLength = Math.max(t, this.data.length+r.length);
21602             this.add(r);
21603         }
21604         this.fireEvent("load", this, r, options, o);
21605         if(options.callback){
21606             options.callback.call(options.scope || this, r, options, true);
21607         }
21608     },
21609
21610
21611     /**
21612      * Loads data from a passed data block. A Reader which understands the format of the data
21613      * must have been configured in the constructor.
21614      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21615      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21616      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21617      */
21618     loadData : function(o, append){
21619         var r = this.reader.readRecords(o);
21620         this.loadRecords(r, {add: append}, true);
21621     },
21622
21623     /**
21624      * Gets the number of cached records.
21625      * <p>
21626      * <em>If using paging, this may not be the total size of the dataset. If the data object
21627      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21628      * the data set size</em>
21629      */
21630     getCount : function(){
21631         return this.data.length || 0;
21632     },
21633
21634     /**
21635      * Gets the total number of records in the dataset as returned by the server.
21636      * <p>
21637      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21638      * the dataset size</em>
21639      */
21640     getTotalCount : function(){
21641         return this.totalLength || 0;
21642     },
21643
21644     /**
21645      * Returns the sort state of the Store as an object with two properties:
21646      * <pre><code>
21647  field {String} The name of the field by which the Records are sorted
21648  direction {String} The sort order, "ASC" or "DESC"
21649      * </code></pre>
21650      */
21651     getSortState : function(){
21652         return this.sortInfo;
21653     },
21654
21655     // private
21656     applySort : function(){
21657         if(this.sortInfo && !this.remoteSort){
21658             var s = this.sortInfo, f = s.field;
21659             var st = this.fields.get(f).sortType;
21660             var fn = function(r1, r2){
21661                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21662                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21663             };
21664             this.data.sort(s.direction, fn);
21665             if(this.snapshot && this.snapshot != this.data){
21666                 this.snapshot.sort(s.direction, fn);
21667             }
21668         }
21669     },
21670
21671     /**
21672      * Sets the default sort column and order to be used by the next load operation.
21673      * @param {String} fieldName The name of the field to sort by.
21674      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21675      */
21676     setDefaultSort : function(field, dir){
21677         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21678     },
21679
21680     /**
21681      * Sort the Records.
21682      * If remote sorting is used, the sort is performed on the server, and the cache is
21683      * reloaded. If local sorting is used, the cache is sorted internally.
21684      * @param {String} fieldName The name of the field to sort by.
21685      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21686      */
21687     sort : function(fieldName, dir){
21688         var f = this.fields.get(fieldName);
21689         if(!dir){
21690             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21691             
21692             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21693                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21694             }else{
21695                 dir = f.sortDir;
21696             }
21697         }
21698         this.sortToggle[f.name] = dir;
21699         this.sortInfo = {field: f.name, direction: dir};
21700         if(!this.remoteSort){
21701             this.applySort();
21702             this.fireEvent("datachanged", this);
21703         }else{
21704             this.load(this.lastOptions);
21705         }
21706     },
21707
21708     /**
21709      * Calls the specified function for each of the Records in the cache.
21710      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21711      * Returning <em>false</em> aborts and exits the iteration.
21712      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21713      */
21714     each : function(fn, scope){
21715         this.data.each(fn, scope);
21716     },
21717
21718     /**
21719      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21720      * (e.g., during paging).
21721      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21722      */
21723     getModifiedRecords : function(){
21724         return this.modified;
21725     },
21726
21727     // private
21728     createFilterFn : function(property, value, anyMatch){
21729         if(!value.exec){ // not a regex
21730             value = String(value);
21731             if(value.length == 0){
21732                 return false;
21733             }
21734             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21735         }
21736         return function(r){
21737             return value.test(r.data[property]);
21738         };
21739     },
21740
21741     /**
21742      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21743      * @param {String} property A field on your records
21744      * @param {Number} start The record index to start at (defaults to 0)
21745      * @param {Number} end The last record index to include (defaults to length - 1)
21746      * @return {Number} The sum
21747      */
21748     sum : function(property, start, end){
21749         var rs = this.data.items, v = 0;
21750         start = start || 0;
21751         end = (end || end === 0) ? end : rs.length-1;
21752
21753         for(var i = start; i <= end; i++){
21754             v += (rs[i].data[property] || 0);
21755         }
21756         return v;
21757     },
21758
21759     /**
21760      * Filter the records by a specified property.
21761      * @param {String} field A field on your records
21762      * @param {String/RegExp} value Either a string that the field
21763      * should start with or a RegExp to test against the field
21764      * @param {Boolean} anyMatch True to match any part not just the beginning
21765      */
21766     filter : function(property, value, anyMatch){
21767         var fn = this.createFilterFn(property, value, anyMatch);
21768         return fn ? this.filterBy(fn) : this.clearFilter();
21769     },
21770
21771     /**
21772      * Filter by a function. The specified function will be called with each
21773      * record in this data source. If the function returns true the record is included,
21774      * otherwise it is filtered.
21775      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21776      * @param {Object} scope (optional) The scope of the function (defaults to this)
21777      */
21778     filterBy : function(fn, scope){
21779         this.snapshot = this.snapshot || this.data;
21780         this.data = this.queryBy(fn, scope||this);
21781         this.fireEvent("datachanged", this);
21782     },
21783
21784     /**
21785      * Query the records by a specified property.
21786      * @param {String} field A field on your records
21787      * @param {String/RegExp} value Either a string that the field
21788      * should start with or a RegExp to test against the field
21789      * @param {Boolean} anyMatch True to match any part not just the beginning
21790      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21791      */
21792     query : function(property, value, anyMatch){
21793         var fn = this.createFilterFn(property, value, anyMatch);
21794         return fn ? this.queryBy(fn) : this.data.clone();
21795     },
21796
21797     /**
21798      * Query by a function. The specified function will be called with each
21799      * record in this data source. If the function returns true the record is included
21800      * in the results.
21801      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21802      * @param {Object} scope (optional) The scope of the function (defaults to this)
21803       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21804      **/
21805     queryBy : function(fn, scope){
21806         var data = this.snapshot || this.data;
21807         return data.filterBy(fn, scope||this);
21808     },
21809
21810     /**
21811      * Collects unique values for a particular dataIndex from this store.
21812      * @param {String} dataIndex The property to collect
21813      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21814      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21815      * @return {Array} An array of the unique values
21816      **/
21817     collect : function(dataIndex, allowNull, bypassFilter){
21818         var d = (bypassFilter === true && this.snapshot) ?
21819                 this.snapshot.items : this.data.items;
21820         var v, sv, r = [], l = {};
21821         for(var i = 0, len = d.length; i < len; i++){
21822             v = d[i].data[dataIndex];
21823             sv = String(v);
21824             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21825                 l[sv] = true;
21826                 r[r.length] = v;
21827             }
21828         }
21829         return r;
21830     },
21831
21832     /**
21833      * Revert to a view of the Record cache with no filtering applied.
21834      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21835      */
21836     clearFilter : function(suppressEvent){
21837         if(this.snapshot && this.snapshot != this.data){
21838             this.data = this.snapshot;
21839             delete this.snapshot;
21840             if(suppressEvent !== true){
21841                 this.fireEvent("datachanged", this);
21842             }
21843         }
21844     },
21845
21846     // private
21847     afterEdit : function(record){
21848         if(this.modified.indexOf(record) == -1){
21849             this.modified.push(record);
21850         }
21851         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21852     },
21853     
21854     // private
21855     afterReject : function(record){
21856         this.modified.remove(record);
21857         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21858     },
21859
21860     // private
21861     afterCommit : function(record){
21862         this.modified.remove(record);
21863         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21864     },
21865
21866     /**
21867      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21868      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21869      */
21870     commitChanges : function(){
21871         var m = this.modified.slice(0);
21872         this.modified = [];
21873         for(var i = 0, len = m.length; i < len; i++){
21874             m[i].commit();
21875         }
21876     },
21877
21878     /**
21879      * Cancel outstanding changes on all changed records.
21880      */
21881     rejectChanges : function(){
21882         var m = this.modified.slice(0);
21883         this.modified = [];
21884         for(var i = 0, len = m.length; i < len; i++){
21885             m[i].reject();
21886         }
21887     },
21888
21889     onMetaChange : function(meta, rtype, o){
21890         this.recordType = rtype;
21891         this.fields = rtype.prototype.fields;
21892         delete this.snapshot;
21893         this.sortInfo = meta.sortInfo || this.sortInfo;
21894         this.modified = [];
21895         this.fireEvent('metachange', this, this.reader.meta);
21896     },
21897     
21898     moveIndex : function(data, type)
21899     {
21900         var index = this.indexOf(data);
21901         
21902         var newIndex = index + type;
21903         
21904         this.remove(data);
21905         
21906         this.insert(newIndex, data);
21907         
21908     }
21909 });/*
21910  * Based on:
21911  * Ext JS Library 1.1.1
21912  * Copyright(c) 2006-2007, Ext JS, LLC.
21913  *
21914  * Originally Released Under LGPL - original licence link has changed is not relivant.
21915  *
21916  * Fork - LGPL
21917  * <script type="text/javascript">
21918  */
21919
21920 /**
21921  * @class Roo.data.SimpleStore
21922  * @extends Roo.data.Store
21923  * Small helper class to make creating Stores from Array data easier.
21924  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21925  * @cfg {Array} fields An array of field definition objects, or field name strings.
21926  * @cfg {Array} data The multi-dimensional array of data
21927  * @constructor
21928  * @param {Object} config
21929  */
21930 Roo.data.SimpleStore = function(config){
21931     Roo.data.SimpleStore.superclass.constructor.call(this, {
21932         isLocal : true,
21933         reader: new Roo.data.ArrayReader({
21934                 id: config.id
21935             },
21936             Roo.data.Record.create(config.fields)
21937         ),
21938         proxy : new Roo.data.MemoryProxy(config.data)
21939     });
21940     this.load();
21941 };
21942 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21943  * Based on:
21944  * Ext JS Library 1.1.1
21945  * Copyright(c) 2006-2007, Ext JS, LLC.
21946  *
21947  * Originally Released Under LGPL - original licence link has changed is not relivant.
21948  *
21949  * Fork - LGPL
21950  * <script type="text/javascript">
21951  */
21952
21953 /**
21954 /**
21955  * @extends Roo.data.Store
21956  * @class Roo.data.JsonStore
21957  * Small helper class to make creating Stores for JSON data easier. <br/>
21958 <pre><code>
21959 var store = new Roo.data.JsonStore({
21960     url: 'get-images.php',
21961     root: 'images',
21962     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21963 });
21964 </code></pre>
21965  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21966  * JsonReader and HttpProxy (unless inline data is provided).</b>
21967  * @cfg {Array} fields An array of field definition objects, or field name strings.
21968  * @constructor
21969  * @param {Object} config
21970  */
21971 Roo.data.JsonStore = function(c){
21972     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21973         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21974         reader: new Roo.data.JsonReader(c, c.fields)
21975     }));
21976 };
21977 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21978  * Based on:
21979  * Ext JS Library 1.1.1
21980  * Copyright(c) 2006-2007, Ext JS, LLC.
21981  *
21982  * Originally Released Under LGPL - original licence link has changed is not relivant.
21983  *
21984  * Fork - LGPL
21985  * <script type="text/javascript">
21986  */
21987
21988  
21989 Roo.data.Field = function(config){
21990     if(typeof config == "string"){
21991         config = {name: config};
21992     }
21993     Roo.apply(this, config);
21994     
21995     if(!this.type){
21996         this.type = "auto";
21997     }
21998     
21999     var st = Roo.data.SortTypes;
22000     // named sortTypes are supported, here we look them up
22001     if(typeof this.sortType == "string"){
22002         this.sortType = st[this.sortType];
22003     }
22004     
22005     // set default sortType for strings and dates
22006     if(!this.sortType){
22007         switch(this.type){
22008             case "string":
22009                 this.sortType = st.asUCString;
22010                 break;
22011             case "date":
22012                 this.sortType = st.asDate;
22013                 break;
22014             default:
22015                 this.sortType = st.none;
22016         }
22017     }
22018
22019     // define once
22020     var stripRe = /[\$,%]/g;
22021
22022     // prebuilt conversion function for this field, instead of
22023     // switching every time we're reading a value
22024     if(!this.convert){
22025         var cv, dateFormat = this.dateFormat;
22026         switch(this.type){
22027             case "":
22028             case "auto":
22029             case undefined:
22030                 cv = function(v){ return v; };
22031                 break;
22032             case "string":
22033                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
22034                 break;
22035             case "int":
22036                 cv = function(v){
22037                     return v !== undefined && v !== null && v !== '' ?
22038                            parseInt(String(v).replace(stripRe, ""), 10) : '';
22039                     };
22040                 break;
22041             case "float":
22042                 cv = function(v){
22043                     return v !== undefined && v !== null && v !== '' ?
22044                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
22045                     };
22046                 break;
22047             case "bool":
22048             case "boolean":
22049                 cv = function(v){ return v === true || v === "true" || v == 1; };
22050                 break;
22051             case "date":
22052                 cv = function(v){
22053                     if(!v){
22054                         return '';
22055                     }
22056                     if(v instanceof Date){
22057                         return v;
22058                     }
22059                     if(dateFormat){
22060                         if(dateFormat == "timestamp"){
22061                             return new Date(v*1000);
22062                         }
22063                         return Date.parseDate(v, dateFormat);
22064                     }
22065                     var parsed = Date.parse(v);
22066                     return parsed ? new Date(parsed) : null;
22067                 };
22068              break;
22069             
22070         }
22071         this.convert = cv;
22072     }
22073 };
22074
22075 Roo.data.Field.prototype = {
22076     dateFormat: null,
22077     defaultValue: "",
22078     mapping: null,
22079     sortType : null,
22080     sortDir : "ASC"
22081 };/*
22082  * Based on:
22083  * Ext JS Library 1.1.1
22084  * Copyright(c) 2006-2007, Ext JS, LLC.
22085  *
22086  * Originally Released Under LGPL - original licence link has changed is not relivant.
22087  *
22088  * Fork - LGPL
22089  * <script type="text/javascript">
22090  */
22091  
22092 // Base class for reading structured data from a data source.  This class is intended to be
22093 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
22094
22095 /**
22096  * @class Roo.data.DataReader
22097  * Base class for reading structured data from a data source.  This class is intended to be
22098  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
22099  */
22100
22101 Roo.data.DataReader = function(meta, recordType){
22102     
22103     this.meta = meta;
22104     
22105     this.recordType = recordType instanceof Array ? 
22106         Roo.data.Record.create(recordType) : recordType;
22107 };
22108
22109 Roo.data.DataReader.prototype = {
22110      /**
22111      * Create an empty record
22112      * @param {Object} data (optional) - overlay some values
22113      * @return {Roo.data.Record} record created.
22114      */
22115     newRow :  function(d) {
22116         var da =  {};
22117         this.recordType.prototype.fields.each(function(c) {
22118             switch( c.type) {
22119                 case 'int' : da[c.name] = 0; break;
22120                 case 'date' : da[c.name] = new Date(); break;
22121                 case 'float' : da[c.name] = 0.0; break;
22122                 case 'boolean' : da[c.name] = false; break;
22123                 default : da[c.name] = ""; break;
22124             }
22125             
22126         });
22127         return new this.recordType(Roo.apply(da, d));
22128     }
22129     
22130 };/*
22131  * Based on:
22132  * Ext JS Library 1.1.1
22133  * Copyright(c) 2006-2007, Ext JS, LLC.
22134  *
22135  * Originally Released Under LGPL - original licence link has changed is not relivant.
22136  *
22137  * Fork - LGPL
22138  * <script type="text/javascript">
22139  */
22140
22141 /**
22142  * @class Roo.data.DataProxy
22143  * @extends Roo.data.Observable
22144  * This class is an abstract base class for implementations which provide retrieval of
22145  * unformatted data objects.<br>
22146  * <p>
22147  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
22148  * (of the appropriate type which knows how to parse the data object) to provide a block of
22149  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
22150  * <p>
22151  * Custom implementations must implement the load method as described in
22152  * {@link Roo.data.HttpProxy#load}.
22153  */
22154 Roo.data.DataProxy = function(){
22155     this.addEvents({
22156         /**
22157          * @event beforeload
22158          * Fires before a network request is made to retrieve a data object.
22159          * @param {Object} This DataProxy object.
22160          * @param {Object} params The params parameter to the load function.
22161          */
22162         beforeload : true,
22163         /**
22164          * @event load
22165          * Fires before the load method's callback is called.
22166          * @param {Object} This DataProxy object.
22167          * @param {Object} o The data object.
22168          * @param {Object} arg The callback argument object passed to the load function.
22169          */
22170         load : true,
22171         /**
22172          * @event loadexception
22173          * Fires if an Exception occurs during data retrieval.
22174          * @param {Object} This DataProxy object.
22175          * @param {Object} o The data object.
22176          * @param {Object} arg The callback argument object passed to the load function.
22177          * @param {Object} e The Exception.
22178          */
22179         loadexception : true
22180     });
22181     Roo.data.DataProxy.superclass.constructor.call(this);
22182 };
22183
22184 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22185
22186     /**
22187      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22188      */
22189 /*
22190  * Based on:
22191  * Ext JS Library 1.1.1
22192  * Copyright(c) 2006-2007, Ext JS, LLC.
22193  *
22194  * Originally Released Under LGPL - original licence link has changed is not relivant.
22195  *
22196  * Fork - LGPL
22197  * <script type="text/javascript">
22198  */
22199 /**
22200  * @class Roo.data.MemoryProxy
22201  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22202  * to the Reader when its load method is called.
22203  * @constructor
22204  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22205  */
22206 Roo.data.MemoryProxy = function(data){
22207     if (data.data) {
22208         data = data.data;
22209     }
22210     Roo.data.MemoryProxy.superclass.constructor.call(this);
22211     this.data = data;
22212 };
22213
22214 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22215     /**
22216      * Load data from the requested source (in this case an in-memory
22217      * data object passed to the constructor), read the data object into
22218      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22219      * process that block using the passed callback.
22220      * @param {Object} params This parameter is not used by the MemoryProxy class.
22221      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22222      * object into a block of Roo.data.Records.
22223      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22224      * The function must be passed <ul>
22225      * <li>The Record block object</li>
22226      * <li>The "arg" argument from the load function</li>
22227      * <li>A boolean success indicator</li>
22228      * </ul>
22229      * @param {Object} scope The scope in which to call the callback
22230      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22231      */
22232     load : function(params, reader, callback, scope, arg){
22233         params = params || {};
22234         var result;
22235         try {
22236             result = reader.readRecords(this.data);
22237         }catch(e){
22238             this.fireEvent("loadexception", this, arg, null, e);
22239             callback.call(scope, null, arg, false);
22240             return;
22241         }
22242         callback.call(scope, result, arg, true);
22243     },
22244     
22245     // private
22246     update : function(params, records){
22247         
22248     }
22249 });/*
22250  * Based on:
22251  * Ext JS Library 1.1.1
22252  * Copyright(c) 2006-2007, Ext JS, LLC.
22253  *
22254  * Originally Released Under LGPL - original licence link has changed is not relivant.
22255  *
22256  * Fork - LGPL
22257  * <script type="text/javascript">
22258  */
22259 /**
22260  * @class Roo.data.HttpProxy
22261  * @extends Roo.data.DataProxy
22262  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22263  * configured to reference a certain URL.<br><br>
22264  * <p>
22265  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22266  * from which the running page was served.<br><br>
22267  * <p>
22268  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22269  * <p>
22270  * Be aware that to enable the browser to parse an XML document, the server must set
22271  * the Content-Type header in the HTTP response to "text/xml".
22272  * @constructor
22273  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22274  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22275  * will be used to make the request.
22276  */
22277 Roo.data.HttpProxy = function(conn){
22278     Roo.data.HttpProxy.superclass.constructor.call(this);
22279     // is conn a conn config or a real conn?
22280     this.conn = conn;
22281     this.useAjax = !conn || !conn.events;
22282   
22283 };
22284
22285 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22286     // thse are take from connection...
22287     
22288     /**
22289      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22290      */
22291     /**
22292      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22293      * extra parameters to each request made by this object. (defaults to undefined)
22294      */
22295     /**
22296      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22297      *  to each request made by this object. (defaults to undefined)
22298      */
22299     /**
22300      * @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)
22301      */
22302     /**
22303      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22304      */
22305      /**
22306      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22307      * @type Boolean
22308      */
22309   
22310
22311     /**
22312      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22313      * @type Boolean
22314      */
22315     /**
22316      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22317      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22318      * a finer-grained basis than the DataProxy events.
22319      */
22320     getConnection : function(){
22321         return this.useAjax ? Roo.Ajax : this.conn;
22322     },
22323
22324     /**
22325      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22326      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22327      * process that block using the passed callback.
22328      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22329      * for the request to the remote server.
22330      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22331      * object into a block of Roo.data.Records.
22332      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22333      * The function must be passed <ul>
22334      * <li>The Record block object</li>
22335      * <li>The "arg" argument from the load function</li>
22336      * <li>A boolean success indicator</li>
22337      * </ul>
22338      * @param {Object} scope The scope in which to call the callback
22339      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22340      */
22341     load : function(params, reader, callback, scope, arg){
22342         if(this.fireEvent("beforeload", this, params) !== false){
22343             var  o = {
22344                 params : params || {},
22345                 request: {
22346                     callback : callback,
22347                     scope : scope,
22348                     arg : arg
22349                 },
22350                 reader: reader,
22351                 callback : this.loadResponse,
22352                 scope: this
22353             };
22354             if(this.useAjax){
22355                 Roo.applyIf(o, this.conn);
22356                 if(this.activeRequest){
22357                     Roo.Ajax.abort(this.activeRequest);
22358                 }
22359                 this.activeRequest = Roo.Ajax.request(o);
22360             }else{
22361                 this.conn.request(o);
22362             }
22363         }else{
22364             callback.call(scope||this, null, arg, false);
22365         }
22366     },
22367
22368     // private
22369     loadResponse : function(o, success, response){
22370         delete this.activeRequest;
22371         if(!success){
22372             this.fireEvent("loadexception", this, o, response);
22373             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22374             return;
22375         }
22376         var result;
22377         try {
22378             result = o.reader.read(response);
22379         }catch(e){
22380             this.fireEvent("loadexception", this, o, response, e);
22381             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22382             return;
22383         }
22384         
22385         this.fireEvent("load", this, o, o.request.arg);
22386         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22387     },
22388
22389     // private
22390     update : function(dataSet){
22391
22392     },
22393
22394     // private
22395     updateResponse : function(dataSet){
22396
22397     }
22398 });/*
22399  * Based on:
22400  * Ext JS Library 1.1.1
22401  * Copyright(c) 2006-2007, Ext JS, LLC.
22402  *
22403  * Originally Released Under LGPL - original licence link has changed is not relivant.
22404  *
22405  * Fork - LGPL
22406  * <script type="text/javascript">
22407  */
22408
22409 /**
22410  * @class Roo.data.ScriptTagProxy
22411  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22412  * other than the originating domain of the running page.<br><br>
22413  * <p>
22414  * <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
22415  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22416  * <p>
22417  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22418  * source code that is used as the source inside a &lt;script> tag.<br><br>
22419  * <p>
22420  * In order for the browser to process the returned data, the server must wrap the data object
22421  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22422  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22423  * depending on whether the callback name was passed:
22424  * <p>
22425  * <pre><code>
22426 boolean scriptTag = false;
22427 String cb = request.getParameter("callback");
22428 if (cb != null) {
22429     scriptTag = true;
22430     response.setContentType("text/javascript");
22431 } else {
22432     response.setContentType("application/x-json");
22433 }
22434 Writer out = response.getWriter();
22435 if (scriptTag) {
22436     out.write(cb + "(");
22437 }
22438 out.print(dataBlock.toJsonString());
22439 if (scriptTag) {
22440     out.write(");");
22441 }
22442 </pre></code>
22443  *
22444  * @constructor
22445  * @param {Object} config A configuration object.
22446  */
22447 Roo.data.ScriptTagProxy = function(config){
22448     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22449     Roo.apply(this, config);
22450     this.head = document.getElementsByTagName("head")[0];
22451 };
22452
22453 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22454
22455 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22456     /**
22457      * @cfg {String} url The URL from which to request the data object.
22458      */
22459     /**
22460      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22461      */
22462     timeout : 30000,
22463     /**
22464      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22465      * the server the name of the callback function set up by the load call to process the returned data object.
22466      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22467      * javascript output which calls this named function passing the data object as its only parameter.
22468      */
22469     callbackParam : "callback",
22470     /**
22471      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22472      * name to the request.
22473      */
22474     nocache : true,
22475
22476     /**
22477      * Load data from the configured URL, read the data object into
22478      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22479      * process that block using the passed callback.
22480      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22481      * for the request to the remote server.
22482      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22483      * object into a block of Roo.data.Records.
22484      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22485      * The function must be passed <ul>
22486      * <li>The Record block object</li>
22487      * <li>The "arg" argument from the load function</li>
22488      * <li>A boolean success indicator</li>
22489      * </ul>
22490      * @param {Object} scope The scope in which to call the callback
22491      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22492      */
22493     load : function(params, reader, callback, scope, arg){
22494         if(this.fireEvent("beforeload", this, params) !== false){
22495
22496             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22497
22498             var url = this.url;
22499             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22500             if(this.nocache){
22501                 url += "&_dc=" + (new Date().getTime());
22502             }
22503             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22504             var trans = {
22505                 id : transId,
22506                 cb : "stcCallback"+transId,
22507                 scriptId : "stcScript"+transId,
22508                 params : params,
22509                 arg : arg,
22510                 url : url,
22511                 callback : callback,
22512                 scope : scope,
22513                 reader : reader
22514             };
22515             var conn = this;
22516
22517             window[trans.cb] = function(o){
22518                 conn.handleResponse(o, trans);
22519             };
22520
22521             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22522
22523             if(this.autoAbort !== false){
22524                 this.abort();
22525             }
22526
22527             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22528
22529             var script = document.createElement("script");
22530             script.setAttribute("src", url);
22531             script.setAttribute("type", "text/javascript");
22532             script.setAttribute("id", trans.scriptId);
22533             this.head.appendChild(script);
22534
22535             this.trans = trans;
22536         }else{
22537             callback.call(scope||this, null, arg, false);
22538         }
22539     },
22540
22541     // private
22542     isLoading : function(){
22543         return this.trans ? true : false;
22544     },
22545
22546     /**
22547      * Abort the current server request.
22548      */
22549     abort : function(){
22550         if(this.isLoading()){
22551             this.destroyTrans(this.trans);
22552         }
22553     },
22554
22555     // private
22556     destroyTrans : function(trans, isLoaded){
22557         this.head.removeChild(document.getElementById(trans.scriptId));
22558         clearTimeout(trans.timeoutId);
22559         if(isLoaded){
22560             window[trans.cb] = undefined;
22561             try{
22562                 delete window[trans.cb];
22563             }catch(e){}
22564         }else{
22565             // if hasn't been loaded, wait for load to remove it to prevent script error
22566             window[trans.cb] = function(){
22567                 window[trans.cb] = undefined;
22568                 try{
22569                     delete window[trans.cb];
22570                 }catch(e){}
22571             };
22572         }
22573     },
22574
22575     // private
22576     handleResponse : function(o, trans){
22577         this.trans = false;
22578         this.destroyTrans(trans, true);
22579         var result;
22580         try {
22581             result = trans.reader.readRecords(o);
22582         }catch(e){
22583             this.fireEvent("loadexception", this, o, trans.arg, e);
22584             trans.callback.call(trans.scope||window, null, trans.arg, false);
22585             return;
22586         }
22587         this.fireEvent("load", this, o, trans.arg);
22588         trans.callback.call(trans.scope||window, result, trans.arg, true);
22589     },
22590
22591     // private
22592     handleFailure : function(trans){
22593         this.trans = false;
22594         this.destroyTrans(trans, false);
22595         this.fireEvent("loadexception", this, null, trans.arg);
22596         trans.callback.call(trans.scope||window, null, trans.arg, false);
22597     }
22598 });/*
22599  * Based on:
22600  * Ext JS Library 1.1.1
22601  * Copyright(c) 2006-2007, Ext JS, LLC.
22602  *
22603  * Originally Released Under LGPL - original licence link has changed is not relivant.
22604  *
22605  * Fork - LGPL
22606  * <script type="text/javascript">
22607  */
22608
22609 /**
22610  * @class Roo.data.JsonReader
22611  * @extends Roo.data.DataReader
22612  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22613  * based on mappings in a provided Roo.data.Record constructor.
22614  * 
22615  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22616  * in the reply previously. 
22617  * 
22618  * <p>
22619  * Example code:
22620  * <pre><code>
22621 var RecordDef = Roo.data.Record.create([
22622     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22623     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22624 ]);
22625 var myReader = new Roo.data.JsonReader({
22626     totalProperty: "results",    // The property which contains the total dataset size (optional)
22627     root: "rows",                // The property which contains an Array of row objects
22628     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22629 }, RecordDef);
22630 </code></pre>
22631  * <p>
22632  * This would consume a JSON file like this:
22633  * <pre><code>
22634 { 'results': 2, 'rows': [
22635     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22636     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22637 }
22638 </code></pre>
22639  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22640  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22641  * paged from the remote server.
22642  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22643  * @cfg {String} root name of the property which contains the Array of row objects.
22644  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22645  * @cfg {Array} fields Array of field definition objects
22646  * @constructor
22647  * Create a new JsonReader
22648  * @param {Object} meta Metadata configuration options
22649  * @param {Object} recordType Either an Array of field definition objects,
22650  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22651  */
22652 Roo.data.JsonReader = function(meta, recordType){
22653     
22654     meta = meta || {};
22655     // set some defaults:
22656     Roo.applyIf(meta, {
22657         totalProperty: 'total',
22658         successProperty : 'success',
22659         root : 'data',
22660         id : 'id'
22661     });
22662     
22663     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22664 };
22665 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22666     
22667     /**
22668      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22669      * Used by Store query builder to append _requestMeta to params.
22670      * 
22671      */
22672     metaFromRemote : false,
22673     /**
22674      * This method is only used by a DataProxy which has retrieved data from a remote server.
22675      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22676      * @return {Object} data A data block which is used by an Roo.data.Store object as
22677      * a cache of Roo.data.Records.
22678      */
22679     read : function(response){
22680         var json = response.responseText;
22681        
22682         var o = /* eval:var:o */ eval("("+json+")");
22683         if(!o) {
22684             throw {message: "JsonReader.read: Json object not found"};
22685         }
22686         
22687         if(o.metaData){
22688             
22689             delete this.ef;
22690             this.metaFromRemote = true;
22691             this.meta = o.metaData;
22692             this.recordType = Roo.data.Record.create(o.metaData.fields);
22693             this.onMetaChange(this.meta, this.recordType, o);
22694         }
22695         return this.readRecords(o);
22696     },
22697
22698     // private function a store will implement
22699     onMetaChange : function(meta, recordType, o){
22700
22701     },
22702
22703     /**
22704          * @ignore
22705          */
22706     simpleAccess: function(obj, subsc) {
22707         return obj[subsc];
22708     },
22709
22710         /**
22711          * @ignore
22712          */
22713     getJsonAccessor: function(){
22714         var re = /[\[\.]/;
22715         return function(expr) {
22716             try {
22717                 return(re.test(expr))
22718                     ? new Function("obj", "return obj." + expr)
22719                     : function(obj){
22720                         return obj[expr];
22721                     };
22722             } catch(e){}
22723             return Roo.emptyFn;
22724         };
22725     }(),
22726
22727     /**
22728      * Create a data block containing Roo.data.Records from an XML document.
22729      * @param {Object} o An object which contains an Array of row objects in the property specified
22730      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22731      * which contains the total size of the dataset.
22732      * @return {Object} data A data block which is used by an Roo.data.Store object as
22733      * a cache of Roo.data.Records.
22734      */
22735     readRecords : function(o){
22736         /**
22737          * After any data loads, the raw JSON data is available for further custom processing.
22738          * @type Object
22739          */
22740         this.o = o;
22741         var s = this.meta, Record = this.recordType,
22742             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
22743
22744 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22745         if (!this.ef) {
22746             if(s.totalProperty) {
22747                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22748                 }
22749                 if(s.successProperty) {
22750                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22751                 }
22752                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22753                 if (s.id) {
22754                         var g = this.getJsonAccessor(s.id);
22755                         this.getId = function(rec) {
22756                                 var r = g(rec);  
22757                                 return (r === undefined || r === "") ? null : r;
22758                         };
22759                 } else {
22760                         this.getId = function(){return null;};
22761                 }
22762             this.ef = [];
22763             for(var jj = 0; jj < fl; jj++){
22764                 f = fi[jj];
22765                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22766                 this.ef[jj] = this.getJsonAccessor(map);
22767             }
22768         }
22769
22770         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22771         if(s.totalProperty){
22772             var vt = parseInt(this.getTotal(o), 10);
22773             if(!isNaN(vt)){
22774                 totalRecords = vt;
22775             }
22776         }
22777         if(s.successProperty){
22778             var vs = this.getSuccess(o);
22779             if(vs === false || vs === 'false'){
22780                 success = false;
22781             }
22782         }
22783         var records = [];
22784         for(var i = 0; i < c; i++){
22785                 var n = root[i];
22786             var values = {};
22787             var id = this.getId(n);
22788             for(var j = 0; j < fl; j++){
22789                 f = fi[j];
22790             var v = this.ef[j](n);
22791             if (!f.convert) {
22792                 Roo.log('missing convert for ' + f.name);
22793                 Roo.log(f);
22794                 continue;
22795             }
22796             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22797             }
22798             var record = new Record(values, id);
22799             record.json = n;
22800             records[i] = record;
22801         }
22802         return {
22803             raw : o,
22804             success : success,
22805             records : records,
22806             totalRecords : totalRecords
22807         };
22808     }
22809 });/*
22810  * Based on:
22811  * Ext JS Library 1.1.1
22812  * Copyright(c) 2006-2007, Ext JS, LLC.
22813  *
22814  * Originally Released Under LGPL - original licence link has changed is not relivant.
22815  *
22816  * Fork - LGPL
22817  * <script type="text/javascript">
22818  */
22819
22820 /**
22821  * @class Roo.data.XmlReader
22822  * @extends Roo.data.DataReader
22823  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22824  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22825  * <p>
22826  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22827  * header in the HTTP response must be set to "text/xml".</em>
22828  * <p>
22829  * Example code:
22830  * <pre><code>
22831 var RecordDef = Roo.data.Record.create([
22832    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22833    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22834 ]);
22835 var myReader = new Roo.data.XmlReader({
22836    totalRecords: "results", // The element which contains the total dataset size (optional)
22837    record: "row",           // The repeated element which contains row information
22838    id: "id"                 // The element within the row that provides an ID for the record (optional)
22839 }, RecordDef);
22840 </code></pre>
22841  * <p>
22842  * This would consume an XML file like this:
22843  * <pre><code>
22844 &lt;?xml?>
22845 &lt;dataset>
22846  &lt;results>2&lt;/results>
22847  &lt;row>
22848    &lt;id>1&lt;/id>
22849    &lt;name>Bill&lt;/name>
22850    &lt;occupation>Gardener&lt;/occupation>
22851  &lt;/row>
22852  &lt;row>
22853    &lt;id>2&lt;/id>
22854    &lt;name>Ben&lt;/name>
22855    &lt;occupation>Horticulturalist&lt;/occupation>
22856  &lt;/row>
22857 &lt;/dataset>
22858 </code></pre>
22859  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22860  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22861  * paged from the remote server.
22862  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22863  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22864  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22865  * a record identifier value.
22866  * @constructor
22867  * Create a new XmlReader
22868  * @param {Object} meta Metadata configuration options
22869  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22870  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22871  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22872  */
22873 Roo.data.XmlReader = function(meta, recordType){
22874     meta = meta || {};
22875     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22876 };
22877 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22878     /**
22879      * This method is only used by a DataProxy which has retrieved data from a remote server.
22880          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22881          * to contain a method called 'responseXML' that returns an XML document object.
22882      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22883      * a cache of Roo.data.Records.
22884      */
22885     read : function(response){
22886         var doc = response.responseXML;
22887         if(!doc) {
22888             throw {message: "XmlReader.read: XML Document not available"};
22889         }
22890         return this.readRecords(doc);
22891     },
22892
22893     /**
22894      * Create a data block containing Roo.data.Records from an XML document.
22895          * @param {Object} doc A parsed XML document.
22896      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22897      * a cache of Roo.data.Records.
22898      */
22899     readRecords : function(doc){
22900         /**
22901          * After any data loads/reads, the raw XML Document is available for further custom processing.
22902          * @type XMLDocument
22903          */
22904         this.xmlData = doc;
22905         var root = doc.documentElement || doc;
22906         var q = Roo.DomQuery;
22907         var recordType = this.recordType, fields = recordType.prototype.fields;
22908         var sid = this.meta.id;
22909         var totalRecords = 0, success = true;
22910         if(this.meta.totalRecords){
22911             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22912         }
22913         
22914         if(this.meta.success){
22915             var sv = q.selectValue(this.meta.success, root, true);
22916             success = sv !== false && sv !== 'false';
22917         }
22918         var records = [];
22919         var ns = q.select(this.meta.record, root);
22920         for(var i = 0, len = ns.length; i < len; i++) {
22921                 var n = ns[i];
22922                 var values = {};
22923                 var id = sid ? q.selectValue(sid, n) : undefined;
22924                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22925                     var f = fields.items[j];
22926                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22927                     v = f.convert(v);
22928                     values[f.name] = v;
22929                 }
22930                 var record = new recordType(values, id);
22931                 record.node = n;
22932                 records[records.length] = record;
22933             }
22934
22935             return {
22936                 success : success,
22937                 records : records,
22938                 totalRecords : totalRecords || records.length
22939             };
22940     }
22941 });/*
22942  * Based on:
22943  * Ext JS Library 1.1.1
22944  * Copyright(c) 2006-2007, Ext JS, LLC.
22945  *
22946  * Originally Released Under LGPL - original licence link has changed is not relivant.
22947  *
22948  * Fork - LGPL
22949  * <script type="text/javascript">
22950  */
22951
22952 /**
22953  * @class Roo.data.ArrayReader
22954  * @extends Roo.data.DataReader
22955  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22956  * Each element of that Array represents a row of data fields. The
22957  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22958  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22959  * <p>
22960  * Example code:.
22961  * <pre><code>
22962 var RecordDef = Roo.data.Record.create([
22963     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22964     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22965 ]);
22966 var myReader = new Roo.data.ArrayReader({
22967     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22968 }, RecordDef);
22969 </code></pre>
22970  * <p>
22971  * This would consume an Array like this:
22972  * <pre><code>
22973 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22974   </code></pre>
22975  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22976  * @constructor
22977  * Create a new JsonReader
22978  * @param {Object} meta Metadata configuration options.
22979  * @param {Object} recordType Either an Array of field definition objects
22980  * as specified to {@link Roo.data.Record#create},
22981  * or an {@link Roo.data.Record} object
22982  * created using {@link Roo.data.Record#create}.
22983  */
22984 Roo.data.ArrayReader = function(meta, recordType){
22985     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22986 };
22987
22988 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22989     /**
22990      * Create a data block containing Roo.data.Records from an XML document.
22991      * @param {Object} o An Array of row objects which represents the dataset.
22992      * @return {Object} data A data block which is used by an Roo.data.Store object as
22993      * a cache of Roo.data.Records.
22994      */
22995     readRecords : function(o){
22996         var sid = this.meta ? this.meta.id : null;
22997         var recordType = this.recordType, fields = recordType.prototype.fields;
22998         var records = [];
22999         var root = o;
23000             for(var i = 0; i < root.length; i++){
23001                     var n = root[i];
23002                 var values = {};
23003                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
23004                 for(var j = 0, jlen = fields.length; j < jlen; j++){
23005                 var f = fields.items[j];
23006                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
23007                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
23008                 v = f.convert(v);
23009                 values[f.name] = v;
23010             }
23011                 var record = new recordType(values, id);
23012                 record.json = n;
23013                 records[records.length] = record;
23014             }
23015             return {
23016                 records : records,
23017                 totalRecords : records.length
23018             };
23019     }
23020 });/*
23021  * Based on:
23022  * Ext JS Library 1.1.1
23023  * Copyright(c) 2006-2007, Ext JS, LLC.
23024  *
23025  * Originally Released Under LGPL - original licence link has changed is not relivant.
23026  *
23027  * Fork - LGPL
23028  * <script type="text/javascript">
23029  */
23030
23031
23032 /**
23033  * @class Roo.data.Tree
23034  * @extends Roo.util.Observable
23035  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
23036  * in the tree have most standard DOM functionality.
23037  * @constructor
23038  * @param {Node} root (optional) The root node
23039  */
23040 Roo.data.Tree = function(root){
23041    this.nodeHash = {};
23042    /**
23043     * The root node for this tree
23044     * @type Node
23045     */
23046    this.root = null;
23047    if(root){
23048        this.setRootNode(root);
23049    }
23050    this.addEvents({
23051        /**
23052         * @event append
23053         * Fires when a new child node is appended to a node in this tree.
23054         * @param {Tree} tree The owner tree
23055         * @param {Node} parent The parent node
23056         * @param {Node} node The newly appended node
23057         * @param {Number} index The index of the newly appended node
23058         */
23059        "append" : true,
23060        /**
23061         * @event remove
23062         * Fires when a child node is removed from a node in this tree.
23063         * @param {Tree} tree The owner tree
23064         * @param {Node} parent The parent node
23065         * @param {Node} node The child node removed
23066         */
23067        "remove" : true,
23068        /**
23069         * @event move
23070         * Fires when a node is moved to a new location in the tree
23071         * @param {Tree} tree The owner tree
23072         * @param {Node} node The node moved
23073         * @param {Node} oldParent The old parent of this node
23074         * @param {Node} newParent The new parent of this node
23075         * @param {Number} index The index it was moved to
23076         */
23077        "move" : true,
23078        /**
23079         * @event insert
23080         * Fires when a new child node is inserted in a node in this tree.
23081         * @param {Tree} tree The owner tree
23082         * @param {Node} parent The parent node
23083         * @param {Node} node The child node inserted
23084         * @param {Node} refNode The child node the node was inserted before
23085         */
23086        "insert" : true,
23087        /**
23088         * @event beforeappend
23089         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
23090         * @param {Tree} tree The owner tree
23091         * @param {Node} parent The parent node
23092         * @param {Node} node The child node to be appended
23093         */
23094        "beforeappend" : true,
23095        /**
23096         * @event beforeremove
23097         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
23098         * @param {Tree} tree The owner tree
23099         * @param {Node} parent The parent node
23100         * @param {Node} node The child node to be removed
23101         */
23102        "beforeremove" : true,
23103        /**
23104         * @event beforemove
23105         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
23106         * @param {Tree} tree The owner tree
23107         * @param {Node} node The node being moved
23108         * @param {Node} oldParent The parent of the node
23109         * @param {Node} newParent The new parent the node is moving to
23110         * @param {Number} index The index it is being moved to
23111         */
23112        "beforemove" : true,
23113        /**
23114         * @event beforeinsert
23115         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
23116         * @param {Tree} tree The owner tree
23117         * @param {Node} parent The parent node
23118         * @param {Node} node The child node to be inserted
23119         * @param {Node} refNode The child node the node is being inserted before
23120         */
23121        "beforeinsert" : true
23122    });
23123
23124     Roo.data.Tree.superclass.constructor.call(this);
23125 };
23126
23127 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
23128     pathSeparator: "/",
23129
23130     proxyNodeEvent : function(){
23131         return this.fireEvent.apply(this, arguments);
23132     },
23133
23134     /**
23135      * Returns the root node for this tree.
23136      * @return {Node}
23137      */
23138     getRootNode : function(){
23139         return this.root;
23140     },
23141
23142     /**
23143      * Sets the root node for this tree.
23144      * @param {Node} node
23145      * @return {Node}
23146      */
23147     setRootNode : function(node){
23148         this.root = node;
23149         node.ownerTree = this;
23150         node.isRoot = true;
23151         this.registerNode(node);
23152         return node;
23153     },
23154
23155     /**
23156      * Gets a node in this tree by its id.
23157      * @param {String} id
23158      * @return {Node}
23159      */
23160     getNodeById : function(id){
23161         return this.nodeHash[id];
23162     },
23163
23164     registerNode : function(node){
23165         this.nodeHash[node.id] = node;
23166     },
23167
23168     unregisterNode : function(node){
23169         delete this.nodeHash[node.id];
23170     },
23171
23172     toString : function(){
23173         return "[Tree"+(this.id?" "+this.id:"")+"]";
23174     }
23175 });
23176
23177 /**
23178  * @class Roo.data.Node
23179  * @extends Roo.util.Observable
23180  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23181  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23182  * @constructor
23183  * @param {Object} attributes The attributes/config for the node
23184  */
23185 Roo.data.Node = function(attributes){
23186     /**
23187      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23188      * @type {Object}
23189      */
23190     this.attributes = attributes || {};
23191     this.leaf = this.attributes.leaf;
23192     /**
23193      * The node id. @type String
23194      */
23195     this.id = this.attributes.id;
23196     if(!this.id){
23197         this.id = Roo.id(null, "ynode-");
23198         this.attributes.id = this.id;
23199     }
23200      
23201     
23202     /**
23203      * All child nodes of this node. @type Array
23204      */
23205     this.childNodes = [];
23206     if(!this.childNodes.indexOf){ // indexOf is a must
23207         this.childNodes.indexOf = function(o){
23208             for(var i = 0, len = this.length; i < len; i++){
23209                 if(this[i] == o) {
23210                     return i;
23211                 }
23212             }
23213             return -1;
23214         };
23215     }
23216     /**
23217      * The parent node for this node. @type Node
23218      */
23219     this.parentNode = null;
23220     /**
23221      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23222      */
23223     this.firstChild = null;
23224     /**
23225      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23226      */
23227     this.lastChild = null;
23228     /**
23229      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23230      */
23231     this.previousSibling = null;
23232     /**
23233      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23234      */
23235     this.nextSibling = null;
23236
23237     this.addEvents({
23238        /**
23239         * @event append
23240         * Fires when a new child node is appended
23241         * @param {Tree} tree The owner tree
23242         * @param {Node} this This node
23243         * @param {Node} node The newly appended node
23244         * @param {Number} index The index of the newly appended node
23245         */
23246        "append" : true,
23247        /**
23248         * @event remove
23249         * Fires when a child node is removed
23250         * @param {Tree} tree The owner tree
23251         * @param {Node} this This node
23252         * @param {Node} node The removed node
23253         */
23254        "remove" : true,
23255        /**
23256         * @event move
23257         * Fires when this node is moved to a new location in the tree
23258         * @param {Tree} tree The owner tree
23259         * @param {Node} this This node
23260         * @param {Node} oldParent The old parent of this node
23261         * @param {Node} newParent The new parent of this node
23262         * @param {Number} index The index it was moved to
23263         */
23264        "move" : true,
23265        /**
23266         * @event insert
23267         * Fires when a new child node is inserted.
23268         * @param {Tree} tree The owner tree
23269         * @param {Node} this This node
23270         * @param {Node} node The child node inserted
23271         * @param {Node} refNode The child node the node was inserted before
23272         */
23273        "insert" : true,
23274        /**
23275         * @event beforeappend
23276         * Fires before a new child is appended, return false to cancel the append.
23277         * @param {Tree} tree The owner tree
23278         * @param {Node} this This node
23279         * @param {Node} node The child node to be appended
23280         */
23281        "beforeappend" : true,
23282        /**
23283         * @event beforeremove
23284         * Fires before a child is removed, return false to cancel the remove.
23285         * @param {Tree} tree The owner tree
23286         * @param {Node} this This node
23287         * @param {Node} node The child node to be removed
23288         */
23289        "beforeremove" : true,
23290        /**
23291         * @event beforemove
23292         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23293         * @param {Tree} tree The owner tree
23294         * @param {Node} this This node
23295         * @param {Node} oldParent The parent of this node
23296         * @param {Node} newParent The new parent this node is moving to
23297         * @param {Number} index The index it is being moved to
23298         */
23299        "beforemove" : true,
23300        /**
23301         * @event beforeinsert
23302         * Fires before a new child is inserted, return false to cancel the insert.
23303         * @param {Tree} tree The owner tree
23304         * @param {Node} this This node
23305         * @param {Node} node The child node to be inserted
23306         * @param {Node} refNode The child node the node is being inserted before
23307         */
23308        "beforeinsert" : true
23309    });
23310     this.listeners = this.attributes.listeners;
23311     Roo.data.Node.superclass.constructor.call(this);
23312 };
23313
23314 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23315     fireEvent : function(evtName){
23316         // first do standard event for this node
23317         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23318             return false;
23319         }
23320         // then bubble it up to the tree if the event wasn't cancelled
23321         var ot = this.getOwnerTree();
23322         if(ot){
23323             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23324                 return false;
23325             }
23326         }
23327         return true;
23328     },
23329
23330     /**
23331      * Returns true if this node is a leaf
23332      * @return {Boolean}
23333      */
23334     isLeaf : function(){
23335         return this.leaf === true;
23336     },
23337
23338     // private
23339     setFirstChild : function(node){
23340         this.firstChild = node;
23341     },
23342
23343     //private
23344     setLastChild : function(node){
23345         this.lastChild = node;
23346     },
23347
23348
23349     /**
23350      * Returns true if this node is the last child of its parent
23351      * @return {Boolean}
23352      */
23353     isLast : function(){
23354        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23355     },
23356
23357     /**
23358      * Returns true if this node is the first child of its parent
23359      * @return {Boolean}
23360      */
23361     isFirst : function(){
23362        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23363     },
23364
23365     hasChildNodes : function(){
23366         return !this.isLeaf() && this.childNodes.length > 0;
23367     },
23368
23369     /**
23370      * Insert node(s) as the last child node of this node.
23371      * @param {Node/Array} node The node or Array of nodes to append
23372      * @return {Node} The appended node if single append, or null if an array was passed
23373      */
23374     appendChild : function(node){
23375         var multi = false;
23376         if(node instanceof Array){
23377             multi = node;
23378         }else if(arguments.length > 1){
23379             multi = arguments;
23380         }
23381         // if passed an array or multiple args do them one by one
23382         if(multi){
23383             for(var i = 0, len = multi.length; i < len; i++) {
23384                 this.appendChild(multi[i]);
23385             }
23386         }else{
23387             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23388                 return false;
23389             }
23390             var index = this.childNodes.length;
23391             var oldParent = node.parentNode;
23392             // it's a move, make sure we move it cleanly
23393             if(oldParent){
23394                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23395                     return false;
23396                 }
23397                 oldParent.removeChild(node);
23398             }
23399             index = this.childNodes.length;
23400             if(index == 0){
23401                 this.setFirstChild(node);
23402             }
23403             this.childNodes.push(node);
23404             node.parentNode = this;
23405             var ps = this.childNodes[index-1];
23406             if(ps){
23407                 node.previousSibling = ps;
23408                 ps.nextSibling = node;
23409             }else{
23410                 node.previousSibling = null;
23411             }
23412             node.nextSibling = null;
23413             this.setLastChild(node);
23414             node.setOwnerTree(this.getOwnerTree());
23415             this.fireEvent("append", this.ownerTree, this, node, index);
23416             if(oldParent){
23417                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23418             }
23419             return node;
23420         }
23421     },
23422
23423     /**
23424      * Removes a child node from this node.
23425      * @param {Node} node The node to remove
23426      * @return {Node} The removed node
23427      */
23428     removeChild : function(node){
23429         var index = this.childNodes.indexOf(node);
23430         if(index == -1){
23431             return false;
23432         }
23433         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23434             return false;
23435         }
23436
23437         // remove it from childNodes collection
23438         this.childNodes.splice(index, 1);
23439
23440         // update siblings
23441         if(node.previousSibling){
23442             node.previousSibling.nextSibling = node.nextSibling;
23443         }
23444         if(node.nextSibling){
23445             node.nextSibling.previousSibling = node.previousSibling;
23446         }
23447
23448         // update child refs
23449         if(this.firstChild == node){
23450             this.setFirstChild(node.nextSibling);
23451         }
23452         if(this.lastChild == node){
23453             this.setLastChild(node.previousSibling);
23454         }
23455
23456         node.setOwnerTree(null);
23457         // clear any references from the node
23458         node.parentNode = null;
23459         node.previousSibling = null;
23460         node.nextSibling = null;
23461         this.fireEvent("remove", this.ownerTree, this, node);
23462         return node;
23463     },
23464
23465     /**
23466      * Inserts the first node before the second node in this nodes childNodes collection.
23467      * @param {Node} node The node to insert
23468      * @param {Node} refNode The node to insert before (if null the node is appended)
23469      * @return {Node} The inserted node
23470      */
23471     insertBefore : function(node, refNode){
23472         if(!refNode){ // like standard Dom, refNode can be null for append
23473             return this.appendChild(node);
23474         }
23475         // nothing to do
23476         if(node == refNode){
23477             return false;
23478         }
23479
23480         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23481             return false;
23482         }
23483         var index = this.childNodes.indexOf(refNode);
23484         var oldParent = node.parentNode;
23485         var refIndex = index;
23486
23487         // when moving internally, indexes will change after remove
23488         if(oldParent == this && this.childNodes.indexOf(node) < index){
23489             refIndex--;
23490         }
23491
23492         // it's a move, make sure we move it cleanly
23493         if(oldParent){
23494             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23495                 return false;
23496             }
23497             oldParent.removeChild(node);
23498         }
23499         if(refIndex == 0){
23500             this.setFirstChild(node);
23501         }
23502         this.childNodes.splice(refIndex, 0, node);
23503         node.parentNode = this;
23504         var ps = this.childNodes[refIndex-1];
23505         if(ps){
23506             node.previousSibling = ps;
23507             ps.nextSibling = node;
23508         }else{
23509             node.previousSibling = null;
23510         }
23511         node.nextSibling = refNode;
23512         refNode.previousSibling = node;
23513         node.setOwnerTree(this.getOwnerTree());
23514         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23515         if(oldParent){
23516             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23517         }
23518         return node;
23519     },
23520
23521     /**
23522      * Returns the child node at the specified index.
23523      * @param {Number} index
23524      * @return {Node}
23525      */
23526     item : function(index){
23527         return this.childNodes[index];
23528     },
23529
23530     /**
23531      * Replaces one child node in this node with another.
23532      * @param {Node} newChild The replacement node
23533      * @param {Node} oldChild The node to replace
23534      * @return {Node} The replaced node
23535      */
23536     replaceChild : function(newChild, oldChild){
23537         this.insertBefore(newChild, oldChild);
23538         this.removeChild(oldChild);
23539         return oldChild;
23540     },
23541
23542     /**
23543      * Returns the index of a child node
23544      * @param {Node} node
23545      * @return {Number} The index of the node or -1 if it was not found
23546      */
23547     indexOf : function(child){
23548         return this.childNodes.indexOf(child);
23549     },
23550
23551     /**
23552      * Returns the tree this node is in.
23553      * @return {Tree}
23554      */
23555     getOwnerTree : function(){
23556         // if it doesn't have one, look for one
23557         if(!this.ownerTree){
23558             var p = this;
23559             while(p){
23560                 if(p.ownerTree){
23561                     this.ownerTree = p.ownerTree;
23562                     break;
23563                 }
23564                 p = p.parentNode;
23565             }
23566         }
23567         return this.ownerTree;
23568     },
23569
23570     /**
23571      * Returns depth of this node (the root node has a depth of 0)
23572      * @return {Number}
23573      */
23574     getDepth : function(){
23575         var depth = 0;
23576         var p = this;
23577         while(p.parentNode){
23578             ++depth;
23579             p = p.parentNode;
23580         }
23581         return depth;
23582     },
23583
23584     // private
23585     setOwnerTree : function(tree){
23586         // if it's move, we need to update everyone
23587         if(tree != this.ownerTree){
23588             if(this.ownerTree){
23589                 this.ownerTree.unregisterNode(this);
23590             }
23591             this.ownerTree = tree;
23592             var cs = this.childNodes;
23593             for(var i = 0, len = cs.length; i < len; i++) {
23594                 cs[i].setOwnerTree(tree);
23595             }
23596             if(tree){
23597                 tree.registerNode(this);
23598             }
23599         }
23600     },
23601
23602     /**
23603      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23604      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23605      * @return {String} The path
23606      */
23607     getPath : function(attr){
23608         attr = attr || "id";
23609         var p = this.parentNode;
23610         var b = [this.attributes[attr]];
23611         while(p){
23612             b.unshift(p.attributes[attr]);
23613             p = p.parentNode;
23614         }
23615         var sep = this.getOwnerTree().pathSeparator;
23616         return sep + b.join(sep);
23617     },
23618
23619     /**
23620      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23621      * function call will be the scope provided or the current node. The arguments to the function
23622      * will be the args provided or the current node. If the function returns false at any point,
23623      * the bubble is stopped.
23624      * @param {Function} fn The function to call
23625      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23626      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23627      */
23628     bubble : function(fn, scope, args){
23629         var p = this;
23630         while(p){
23631             if(fn.call(scope || p, args || p) === false){
23632                 break;
23633             }
23634             p = p.parentNode;
23635         }
23636     },
23637
23638     /**
23639      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23640      * function call will be the scope provided or the current node. The arguments to the function
23641      * will be the args provided or the current node. If the function returns false at any point,
23642      * the cascade is stopped on that branch.
23643      * @param {Function} fn The function to call
23644      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23645      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23646      */
23647     cascade : function(fn, scope, args){
23648         if(fn.call(scope || this, args || this) !== false){
23649             var cs = this.childNodes;
23650             for(var i = 0, len = cs.length; i < len; i++) {
23651                 cs[i].cascade(fn, scope, args);
23652             }
23653         }
23654     },
23655
23656     /**
23657      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23658      * function call will be the scope provided or the current node. The arguments to the function
23659      * will be the args provided or the current node. If the function returns false at any point,
23660      * the iteration stops.
23661      * @param {Function} fn The function to call
23662      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23663      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23664      */
23665     eachChild : function(fn, scope, args){
23666         var cs = this.childNodes;
23667         for(var i = 0, len = cs.length; i < len; i++) {
23668                 if(fn.call(scope || this, args || cs[i]) === false){
23669                     break;
23670                 }
23671         }
23672     },
23673
23674     /**
23675      * Finds the first child that has the attribute with the specified value.
23676      * @param {String} attribute The attribute name
23677      * @param {Mixed} value The value to search for
23678      * @return {Node} The found child or null if none was found
23679      */
23680     findChild : function(attribute, value){
23681         var cs = this.childNodes;
23682         for(var i = 0, len = cs.length; i < len; i++) {
23683                 if(cs[i].attributes[attribute] == value){
23684                     return cs[i];
23685                 }
23686         }
23687         return null;
23688     },
23689
23690     /**
23691      * Finds the first child by a custom function. The child matches if the function passed
23692      * returns true.
23693      * @param {Function} fn
23694      * @param {Object} scope (optional)
23695      * @return {Node} The found child or null if none was found
23696      */
23697     findChildBy : function(fn, scope){
23698         var cs = this.childNodes;
23699         for(var i = 0, len = cs.length; i < len; i++) {
23700                 if(fn.call(scope||cs[i], cs[i]) === true){
23701                     return cs[i];
23702                 }
23703         }
23704         return null;
23705     },
23706
23707     /**
23708      * Sorts this nodes children using the supplied sort function
23709      * @param {Function} fn
23710      * @param {Object} scope (optional)
23711      */
23712     sort : function(fn, scope){
23713         var cs = this.childNodes;
23714         var len = cs.length;
23715         if(len > 0){
23716             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23717             cs.sort(sortFn);
23718             for(var i = 0; i < len; i++){
23719                 var n = cs[i];
23720                 n.previousSibling = cs[i-1];
23721                 n.nextSibling = cs[i+1];
23722                 if(i == 0){
23723                     this.setFirstChild(n);
23724                 }
23725                 if(i == len-1){
23726                     this.setLastChild(n);
23727                 }
23728             }
23729         }
23730     },
23731
23732     /**
23733      * Returns true if this node is an ancestor (at any point) of the passed node.
23734      * @param {Node} node
23735      * @return {Boolean}
23736      */
23737     contains : function(node){
23738         return node.isAncestor(this);
23739     },
23740
23741     /**
23742      * Returns true if the passed node is an ancestor (at any point) of this node.
23743      * @param {Node} node
23744      * @return {Boolean}
23745      */
23746     isAncestor : function(node){
23747         var p = this.parentNode;
23748         while(p){
23749             if(p == node){
23750                 return true;
23751             }
23752             p = p.parentNode;
23753         }
23754         return false;
23755     },
23756
23757     toString : function(){
23758         return "[Node"+(this.id?" "+this.id:"")+"]";
23759     }
23760 });/*
23761  * Based on:
23762  * Ext JS Library 1.1.1
23763  * Copyright(c) 2006-2007, Ext JS, LLC.
23764  *
23765  * Originally Released Under LGPL - original licence link has changed is not relivant.
23766  *
23767  * Fork - LGPL
23768  * <script type="text/javascript">
23769  */
23770  (function(){ 
23771 /**
23772  * @class Roo.Layer
23773  * @extends Roo.Element
23774  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23775  * automatic maintaining of shadow/shim positions.
23776  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23777  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23778  * you can pass a string with a CSS class name. False turns off the shadow.
23779  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23780  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23781  * @cfg {String} cls CSS class to add to the element
23782  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23783  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23784  * @constructor
23785  * @param {Object} config An object with config options.
23786  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23787  */
23788
23789 Roo.Layer = function(config, existingEl){
23790     config = config || {};
23791     var dh = Roo.DomHelper;
23792     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23793     if(existingEl){
23794         this.dom = Roo.getDom(existingEl);
23795     }
23796     if(!this.dom){
23797         var o = config.dh || {tag: "div", cls: "x-layer"};
23798         this.dom = dh.append(pel, o);
23799     }
23800     if(config.cls){
23801         this.addClass(config.cls);
23802     }
23803     this.constrain = config.constrain !== false;
23804     this.visibilityMode = Roo.Element.VISIBILITY;
23805     if(config.id){
23806         this.id = this.dom.id = config.id;
23807     }else{
23808         this.id = Roo.id(this.dom);
23809     }
23810     this.zindex = config.zindex || this.getZIndex();
23811     this.position("absolute", this.zindex);
23812     if(config.shadow){
23813         this.shadowOffset = config.shadowOffset || 4;
23814         this.shadow = new Roo.Shadow({
23815             offset : this.shadowOffset,
23816             mode : config.shadow
23817         });
23818     }else{
23819         this.shadowOffset = 0;
23820     }
23821     this.useShim = config.shim !== false && Roo.useShims;
23822     this.useDisplay = config.useDisplay;
23823     this.hide();
23824 };
23825
23826 var supr = Roo.Element.prototype;
23827
23828 // shims are shared among layer to keep from having 100 iframes
23829 var shims = [];
23830
23831 Roo.extend(Roo.Layer, Roo.Element, {
23832
23833     getZIndex : function(){
23834         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23835     },
23836
23837     getShim : function(){
23838         if(!this.useShim){
23839             return null;
23840         }
23841         if(this.shim){
23842             return this.shim;
23843         }
23844         var shim = shims.shift();
23845         if(!shim){
23846             shim = this.createShim();
23847             shim.enableDisplayMode('block');
23848             shim.dom.style.display = 'none';
23849             shim.dom.style.visibility = 'visible';
23850         }
23851         var pn = this.dom.parentNode;
23852         if(shim.dom.parentNode != pn){
23853             pn.insertBefore(shim.dom, this.dom);
23854         }
23855         shim.setStyle('z-index', this.getZIndex()-2);
23856         this.shim = shim;
23857         return shim;
23858     },
23859
23860     hideShim : function(){
23861         if(this.shim){
23862             this.shim.setDisplayed(false);
23863             shims.push(this.shim);
23864             delete this.shim;
23865         }
23866     },
23867
23868     disableShadow : function(){
23869         if(this.shadow){
23870             this.shadowDisabled = true;
23871             this.shadow.hide();
23872             this.lastShadowOffset = this.shadowOffset;
23873             this.shadowOffset = 0;
23874         }
23875     },
23876
23877     enableShadow : function(show){
23878         if(this.shadow){
23879             this.shadowDisabled = false;
23880             this.shadowOffset = this.lastShadowOffset;
23881             delete this.lastShadowOffset;
23882             if(show){
23883                 this.sync(true);
23884             }
23885         }
23886     },
23887
23888     // private
23889     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23890     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23891     sync : function(doShow){
23892         var sw = this.shadow;
23893         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23894             var sh = this.getShim();
23895
23896             var w = this.getWidth(),
23897                 h = this.getHeight();
23898
23899             var l = this.getLeft(true),
23900                 t = this.getTop(true);
23901
23902             if(sw && !this.shadowDisabled){
23903                 if(doShow && !sw.isVisible()){
23904                     sw.show(this);
23905                 }else{
23906                     sw.realign(l, t, w, h);
23907                 }
23908                 if(sh){
23909                     if(doShow){
23910                        sh.show();
23911                     }
23912                     // fit the shim behind the shadow, so it is shimmed too
23913                     var a = sw.adjusts, s = sh.dom.style;
23914                     s.left = (Math.min(l, l+a.l))+"px";
23915                     s.top = (Math.min(t, t+a.t))+"px";
23916                     s.width = (w+a.w)+"px";
23917                     s.height = (h+a.h)+"px";
23918                 }
23919             }else if(sh){
23920                 if(doShow){
23921                    sh.show();
23922                 }
23923                 sh.setSize(w, h);
23924                 sh.setLeftTop(l, t);
23925             }
23926             
23927         }
23928     },
23929
23930     // private
23931     destroy : function(){
23932         this.hideShim();
23933         if(this.shadow){
23934             this.shadow.hide();
23935         }
23936         this.removeAllListeners();
23937         var pn = this.dom.parentNode;
23938         if(pn){
23939             pn.removeChild(this.dom);
23940         }
23941         Roo.Element.uncache(this.id);
23942     },
23943
23944     remove : function(){
23945         this.destroy();
23946     },
23947
23948     // private
23949     beginUpdate : function(){
23950         this.updating = true;
23951     },
23952
23953     // private
23954     endUpdate : function(){
23955         this.updating = false;
23956         this.sync(true);
23957     },
23958
23959     // private
23960     hideUnders : function(negOffset){
23961         if(this.shadow){
23962             this.shadow.hide();
23963         }
23964         this.hideShim();
23965     },
23966
23967     // private
23968     constrainXY : function(){
23969         if(this.constrain){
23970             var vw = Roo.lib.Dom.getViewWidth(),
23971                 vh = Roo.lib.Dom.getViewHeight();
23972             var s = Roo.get(document).getScroll();
23973
23974             var xy = this.getXY();
23975             var x = xy[0], y = xy[1];   
23976             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23977             // only move it if it needs it
23978             var moved = false;
23979             // first validate right/bottom
23980             if((x + w) > vw+s.left){
23981                 x = vw - w - this.shadowOffset;
23982                 moved = true;
23983             }
23984             if((y + h) > vh+s.top){
23985                 y = vh - h - this.shadowOffset;
23986                 moved = true;
23987             }
23988             // then make sure top/left isn't negative
23989             if(x < s.left){
23990                 x = s.left;
23991                 moved = true;
23992             }
23993             if(y < s.top){
23994                 y = s.top;
23995                 moved = true;
23996             }
23997             if(moved){
23998                 if(this.avoidY){
23999                     var ay = this.avoidY;
24000                     if(y <= ay && (y+h) >= ay){
24001                         y = ay-h-5;   
24002                     }
24003                 }
24004                 xy = [x, y];
24005                 this.storeXY(xy);
24006                 supr.setXY.call(this, xy);
24007                 this.sync();
24008             }
24009         }
24010     },
24011
24012     isVisible : function(){
24013         return this.visible;    
24014     },
24015
24016     // private
24017     showAction : function(){
24018         this.visible = true; // track visibility to prevent getStyle calls
24019         if(this.useDisplay === true){
24020             this.setDisplayed("");
24021         }else if(this.lastXY){
24022             supr.setXY.call(this, this.lastXY);
24023         }else if(this.lastLT){
24024             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
24025         }
24026     },
24027
24028     // private
24029     hideAction : function(){
24030         this.visible = false;
24031         if(this.useDisplay === true){
24032             this.setDisplayed(false);
24033         }else{
24034             this.setLeftTop(-10000,-10000);
24035         }
24036     },
24037
24038     // overridden Element method
24039     setVisible : function(v, a, d, c, e){
24040         if(v){
24041             this.showAction();
24042         }
24043         if(a && v){
24044             var cb = function(){
24045                 this.sync(true);
24046                 if(c){
24047                     c();
24048                 }
24049             }.createDelegate(this);
24050             supr.setVisible.call(this, true, true, d, cb, e);
24051         }else{
24052             if(!v){
24053                 this.hideUnders(true);
24054             }
24055             var cb = c;
24056             if(a){
24057                 cb = function(){
24058                     this.hideAction();
24059                     if(c){
24060                         c();
24061                     }
24062                 }.createDelegate(this);
24063             }
24064             supr.setVisible.call(this, v, a, d, cb, e);
24065             if(v){
24066                 this.sync(true);
24067             }else if(!a){
24068                 this.hideAction();
24069             }
24070         }
24071     },
24072
24073     storeXY : function(xy){
24074         delete this.lastLT;
24075         this.lastXY = xy;
24076     },
24077
24078     storeLeftTop : function(left, top){
24079         delete this.lastXY;
24080         this.lastLT = [left, top];
24081     },
24082
24083     // private
24084     beforeFx : function(){
24085         this.beforeAction();
24086         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
24087     },
24088
24089     // private
24090     afterFx : function(){
24091         Roo.Layer.superclass.afterFx.apply(this, arguments);
24092         this.sync(this.isVisible());
24093     },
24094
24095     // private
24096     beforeAction : function(){
24097         if(!this.updating && this.shadow){
24098             this.shadow.hide();
24099         }
24100     },
24101
24102     // overridden Element method
24103     setLeft : function(left){
24104         this.storeLeftTop(left, this.getTop(true));
24105         supr.setLeft.apply(this, arguments);
24106         this.sync();
24107     },
24108
24109     setTop : function(top){
24110         this.storeLeftTop(this.getLeft(true), top);
24111         supr.setTop.apply(this, arguments);
24112         this.sync();
24113     },
24114
24115     setLeftTop : function(left, top){
24116         this.storeLeftTop(left, top);
24117         supr.setLeftTop.apply(this, arguments);
24118         this.sync();
24119     },
24120
24121     setXY : function(xy, a, d, c, e){
24122         this.fixDisplay();
24123         this.beforeAction();
24124         this.storeXY(xy);
24125         var cb = this.createCB(c);
24126         supr.setXY.call(this, xy, a, d, cb, e);
24127         if(!a){
24128             cb();
24129         }
24130     },
24131
24132     // private
24133     createCB : function(c){
24134         var el = this;
24135         return function(){
24136             el.constrainXY();
24137             el.sync(true);
24138             if(c){
24139                 c();
24140             }
24141         };
24142     },
24143
24144     // overridden Element method
24145     setX : function(x, a, d, c, e){
24146         this.setXY([x, this.getY()], a, d, c, e);
24147     },
24148
24149     // overridden Element method
24150     setY : function(y, a, d, c, e){
24151         this.setXY([this.getX(), y], a, d, c, e);
24152     },
24153
24154     // overridden Element method
24155     setSize : function(w, h, a, d, c, e){
24156         this.beforeAction();
24157         var cb = this.createCB(c);
24158         supr.setSize.call(this, w, h, a, d, cb, e);
24159         if(!a){
24160             cb();
24161         }
24162     },
24163
24164     // overridden Element method
24165     setWidth : function(w, a, d, c, e){
24166         this.beforeAction();
24167         var cb = this.createCB(c);
24168         supr.setWidth.call(this, w, a, d, cb, e);
24169         if(!a){
24170             cb();
24171         }
24172     },
24173
24174     // overridden Element method
24175     setHeight : function(h, a, d, c, e){
24176         this.beforeAction();
24177         var cb = this.createCB(c);
24178         supr.setHeight.call(this, h, a, d, cb, e);
24179         if(!a){
24180             cb();
24181         }
24182     },
24183
24184     // overridden Element method
24185     setBounds : function(x, y, w, h, a, d, c, e){
24186         this.beforeAction();
24187         var cb = this.createCB(c);
24188         if(!a){
24189             this.storeXY([x, y]);
24190             supr.setXY.call(this, [x, y]);
24191             supr.setSize.call(this, w, h, a, d, cb, e);
24192             cb();
24193         }else{
24194             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24195         }
24196         return this;
24197     },
24198     
24199     /**
24200      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24201      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24202      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24203      * @param {Number} zindex The new z-index to set
24204      * @return {this} The Layer
24205      */
24206     setZIndex : function(zindex){
24207         this.zindex = zindex;
24208         this.setStyle("z-index", zindex + 2);
24209         if(this.shadow){
24210             this.shadow.setZIndex(zindex + 1);
24211         }
24212         if(this.shim){
24213             this.shim.setStyle("z-index", zindex);
24214         }
24215     }
24216 });
24217 })();/*
24218  * Based on:
24219  * Ext JS Library 1.1.1
24220  * Copyright(c) 2006-2007, Ext JS, LLC.
24221  *
24222  * Originally Released Under LGPL - original licence link has changed is not relivant.
24223  *
24224  * Fork - LGPL
24225  * <script type="text/javascript">
24226  */
24227
24228
24229 /**
24230  * @class Roo.Shadow
24231  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24232  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24233  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24234  * @constructor
24235  * Create a new Shadow
24236  * @param {Object} config The config object
24237  */
24238 Roo.Shadow = function(config){
24239     Roo.apply(this, config);
24240     if(typeof this.mode != "string"){
24241         this.mode = this.defaultMode;
24242     }
24243     var o = this.offset, a = {h: 0};
24244     var rad = Math.floor(this.offset/2);
24245     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24246         case "drop":
24247             a.w = 0;
24248             a.l = a.t = o;
24249             a.t -= 1;
24250             if(Roo.isIE){
24251                 a.l -= this.offset + rad;
24252                 a.t -= this.offset + rad;
24253                 a.w -= rad;
24254                 a.h -= rad;
24255                 a.t += 1;
24256             }
24257         break;
24258         case "sides":
24259             a.w = (o*2);
24260             a.l = -o;
24261             a.t = o-1;
24262             if(Roo.isIE){
24263                 a.l -= (this.offset - rad);
24264                 a.t -= this.offset + rad;
24265                 a.l += 1;
24266                 a.w -= (this.offset - rad)*2;
24267                 a.w -= rad + 1;
24268                 a.h -= 1;
24269             }
24270         break;
24271         case "frame":
24272             a.w = a.h = (o*2);
24273             a.l = a.t = -o;
24274             a.t += 1;
24275             a.h -= 2;
24276             if(Roo.isIE){
24277                 a.l -= (this.offset - rad);
24278                 a.t -= (this.offset - rad);
24279                 a.l += 1;
24280                 a.w -= (this.offset + rad + 1);
24281                 a.h -= (this.offset + rad);
24282                 a.h += 1;
24283             }
24284         break;
24285     };
24286
24287     this.adjusts = a;
24288 };
24289
24290 Roo.Shadow.prototype = {
24291     /**
24292      * @cfg {String} mode
24293      * The shadow display mode.  Supports the following options:<br />
24294      * sides: Shadow displays on both sides and bottom only<br />
24295      * frame: Shadow displays equally on all four sides<br />
24296      * drop: Traditional bottom-right drop shadow (default)
24297      */
24298     /**
24299      * @cfg {String} offset
24300      * The number of pixels to offset the shadow from the element (defaults to 4)
24301      */
24302     offset: 4,
24303
24304     // private
24305     defaultMode: "drop",
24306
24307     /**
24308      * Displays the shadow under the target element
24309      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24310      */
24311     show : function(target){
24312         target = Roo.get(target);
24313         if(!this.el){
24314             this.el = Roo.Shadow.Pool.pull();
24315             if(this.el.dom.nextSibling != target.dom){
24316                 this.el.insertBefore(target);
24317             }
24318         }
24319         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24320         if(Roo.isIE){
24321             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24322         }
24323         this.realign(
24324             target.getLeft(true),
24325             target.getTop(true),
24326             target.getWidth(),
24327             target.getHeight()
24328         );
24329         this.el.dom.style.display = "block";
24330     },
24331
24332     /**
24333      * Returns true if the shadow is visible, else false
24334      */
24335     isVisible : function(){
24336         return this.el ? true : false;  
24337     },
24338
24339     /**
24340      * Direct alignment when values are already available. Show must be called at least once before
24341      * calling this method to ensure it is initialized.
24342      * @param {Number} left The target element left position
24343      * @param {Number} top The target element top position
24344      * @param {Number} width The target element width
24345      * @param {Number} height The target element height
24346      */
24347     realign : function(l, t, w, h){
24348         if(!this.el){
24349             return;
24350         }
24351         var a = this.adjusts, d = this.el.dom, s = d.style;
24352         var iea = 0;
24353         s.left = (l+a.l)+"px";
24354         s.top = (t+a.t)+"px";
24355         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24356  
24357         if(s.width != sws || s.height != shs){
24358             s.width = sws;
24359             s.height = shs;
24360             if(!Roo.isIE){
24361                 var cn = d.childNodes;
24362                 var sww = Math.max(0, (sw-12))+"px";
24363                 cn[0].childNodes[1].style.width = sww;
24364                 cn[1].childNodes[1].style.width = sww;
24365                 cn[2].childNodes[1].style.width = sww;
24366                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24367             }
24368         }
24369     },
24370
24371     /**
24372      * Hides this shadow
24373      */
24374     hide : function(){
24375         if(this.el){
24376             this.el.dom.style.display = "none";
24377             Roo.Shadow.Pool.push(this.el);
24378             delete this.el;
24379         }
24380     },
24381
24382     /**
24383      * Adjust the z-index of this shadow
24384      * @param {Number} zindex The new z-index
24385      */
24386     setZIndex : function(z){
24387         this.zIndex = z;
24388         if(this.el){
24389             this.el.setStyle("z-index", z);
24390         }
24391     }
24392 };
24393
24394 // Private utility class that manages the internal Shadow cache
24395 Roo.Shadow.Pool = function(){
24396     var p = [];
24397     var markup = Roo.isIE ?
24398                  '<div class="x-ie-shadow"></div>' :
24399                  '<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>';
24400     return {
24401         pull : function(){
24402             var sh = p.shift();
24403             if(!sh){
24404                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24405                 sh.autoBoxAdjust = false;
24406             }
24407             return sh;
24408         },
24409
24410         push : function(sh){
24411             p.push(sh);
24412         }
24413     };
24414 }();/*
24415  * Based on:
24416  * Ext JS Library 1.1.1
24417  * Copyright(c) 2006-2007, Ext JS, LLC.
24418  *
24419  * Originally Released Under LGPL - original licence link has changed is not relivant.
24420  *
24421  * Fork - LGPL
24422  * <script type="text/javascript">
24423  */
24424
24425
24426 /**
24427  * @class Roo.SplitBar
24428  * @extends Roo.util.Observable
24429  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24430  * <br><br>
24431  * Usage:
24432  * <pre><code>
24433 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24434                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24435 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24436 split.minSize = 100;
24437 split.maxSize = 600;
24438 split.animate = true;
24439 split.on('moved', splitterMoved);
24440 </code></pre>
24441  * @constructor
24442  * Create a new SplitBar
24443  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24444  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24445  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24446  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24447                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24448                         position of the SplitBar).
24449  */
24450 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24451     
24452     /** @private */
24453     this.el = Roo.get(dragElement, true);
24454     this.el.dom.unselectable = "on";
24455     /** @private */
24456     this.resizingEl = Roo.get(resizingElement, true);
24457
24458     /**
24459      * @private
24460      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24461      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24462      * @type Number
24463      */
24464     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24465     
24466     /**
24467      * The minimum size of the resizing element. (Defaults to 0)
24468      * @type Number
24469      */
24470     this.minSize = 0;
24471     
24472     /**
24473      * The maximum size of the resizing element. (Defaults to 2000)
24474      * @type Number
24475      */
24476     this.maxSize = 2000;
24477     
24478     /**
24479      * Whether to animate the transition to the new size
24480      * @type Boolean
24481      */
24482     this.animate = false;
24483     
24484     /**
24485      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24486      * @type Boolean
24487      */
24488     this.useShim = false;
24489     
24490     /** @private */
24491     this.shim = null;
24492     
24493     if(!existingProxy){
24494         /** @private */
24495         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24496     }else{
24497         this.proxy = Roo.get(existingProxy).dom;
24498     }
24499     /** @private */
24500     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24501     
24502     /** @private */
24503     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24504     
24505     /** @private */
24506     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24507     
24508     /** @private */
24509     this.dragSpecs = {};
24510     
24511     /**
24512      * @private The adapter to use to positon and resize elements
24513      */
24514     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24515     this.adapter.init(this);
24516     
24517     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24518         /** @private */
24519         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24520         this.el.addClass("x-splitbar-h");
24521     }else{
24522         /** @private */
24523         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24524         this.el.addClass("x-splitbar-v");
24525     }
24526     
24527     this.addEvents({
24528         /**
24529          * @event resize
24530          * Fires when the splitter is moved (alias for {@link #event-moved})
24531          * @param {Roo.SplitBar} this
24532          * @param {Number} newSize the new width or height
24533          */
24534         "resize" : true,
24535         /**
24536          * @event moved
24537          * Fires when the splitter is moved
24538          * @param {Roo.SplitBar} this
24539          * @param {Number} newSize the new width or height
24540          */
24541         "moved" : true,
24542         /**
24543          * @event beforeresize
24544          * Fires before the splitter is dragged
24545          * @param {Roo.SplitBar} this
24546          */
24547         "beforeresize" : true,
24548
24549         "beforeapply" : true
24550     });
24551
24552     Roo.util.Observable.call(this);
24553 };
24554
24555 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24556     onStartProxyDrag : function(x, y){
24557         this.fireEvent("beforeresize", this);
24558         if(!this.overlay){
24559             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24560             o.unselectable();
24561             o.enableDisplayMode("block");
24562             // all splitbars share the same overlay
24563             Roo.SplitBar.prototype.overlay = o;
24564         }
24565         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24566         this.overlay.show();
24567         Roo.get(this.proxy).setDisplayed("block");
24568         var size = this.adapter.getElementSize(this);
24569         this.activeMinSize = this.getMinimumSize();;
24570         this.activeMaxSize = this.getMaximumSize();;
24571         var c1 = size - this.activeMinSize;
24572         var c2 = Math.max(this.activeMaxSize - size, 0);
24573         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24574             this.dd.resetConstraints();
24575             this.dd.setXConstraint(
24576                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24577                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24578             );
24579             this.dd.setYConstraint(0, 0);
24580         }else{
24581             this.dd.resetConstraints();
24582             this.dd.setXConstraint(0, 0);
24583             this.dd.setYConstraint(
24584                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24585                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24586             );
24587          }
24588         this.dragSpecs.startSize = size;
24589         this.dragSpecs.startPoint = [x, y];
24590         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24591     },
24592     
24593     /** 
24594      * @private Called after the drag operation by the DDProxy
24595      */
24596     onEndProxyDrag : function(e){
24597         Roo.get(this.proxy).setDisplayed(false);
24598         var endPoint = Roo.lib.Event.getXY(e);
24599         if(this.overlay){
24600             this.overlay.hide();
24601         }
24602         var newSize;
24603         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24604             newSize = this.dragSpecs.startSize + 
24605                 (this.placement == Roo.SplitBar.LEFT ?
24606                     endPoint[0] - this.dragSpecs.startPoint[0] :
24607                     this.dragSpecs.startPoint[0] - endPoint[0]
24608                 );
24609         }else{
24610             newSize = this.dragSpecs.startSize + 
24611                 (this.placement == Roo.SplitBar.TOP ?
24612                     endPoint[1] - this.dragSpecs.startPoint[1] :
24613                     this.dragSpecs.startPoint[1] - endPoint[1]
24614                 );
24615         }
24616         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24617         if(newSize != this.dragSpecs.startSize){
24618             if(this.fireEvent('beforeapply', this, newSize) !== false){
24619                 this.adapter.setElementSize(this, newSize);
24620                 this.fireEvent("moved", this, newSize);
24621                 this.fireEvent("resize", this, newSize);
24622             }
24623         }
24624     },
24625     
24626     /**
24627      * Get the adapter this SplitBar uses
24628      * @return The adapter object
24629      */
24630     getAdapter : function(){
24631         return this.adapter;
24632     },
24633     
24634     /**
24635      * Set the adapter this SplitBar uses
24636      * @param {Object} adapter A SplitBar adapter object
24637      */
24638     setAdapter : function(adapter){
24639         this.adapter = adapter;
24640         this.adapter.init(this);
24641     },
24642     
24643     /**
24644      * Gets the minimum size for the resizing element
24645      * @return {Number} The minimum size
24646      */
24647     getMinimumSize : function(){
24648         return this.minSize;
24649     },
24650     
24651     /**
24652      * Sets the minimum size for the resizing element
24653      * @param {Number} minSize The minimum size
24654      */
24655     setMinimumSize : function(minSize){
24656         this.minSize = minSize;
24657     },
24658     
24659     /**
24660      * Gets the maximum size for the resizing element
24661      * @return {Number} The maximum size
24662      */
24663     getMaximumSize : function(){
24664         return this.maxSize;
24665     },
24666     
24667     /**
24668      * Sets the maximum size for the resizing element
24669      * @param {Number} maxSize The maximum size
24670      */
24671     setMaximumSize : function(maxSize){
24672         this.maxSize = maxSize;
24673     },
24674     
24675     /**
24676      * Sets the initialize size for the resizing element
24677      * @param {Number} size The initial size
24678      */
24679     setCurrentSize : function(size){
24680         var oldAnimate = this.animate;
24681         this.animate = false;
24682         this.adapter.setElementSize(this, size);
24683         this.animate = oldAnimate;
24684     },
24685     
24686     /**
24687      * Destroy this splitbar. 
24688      * @param {Boolean} removeEl True to remove the element
24689      */
24690     destroy : function(removeEl){
24691         if(this.shim){
24692             this.shim.remove();
24693         }
24694         this.dd.unreg();
24695         this.proxy.parentNode.removeChild(this.proxy);
24696         if(removeEl){
24697             this.el.remove();
24698         }
24699     }
24700 });
24701
24702 /**
24703  * @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.
24704  */
24705 Roo.SplitBar.createProxy = function(dir){
24706     var proxy = new Roo.Element(document.createElement("div"));
24707     proxy.unselectable();
24708     var cls = 'x-splitbar-proxy';
24709     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24710     document.body.appendChild(proxy.dom);
24711     return proxy.dom;
24712 };
24713
24714 /** 
24715  * @class Roo.SplitBar.BasicLayoutAdapter
24716  * Default Adapter. It assumes the splitter and resizing element are not positioned
24717  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24718  */
24719 Roo.SplitBar.BasicLayoutAdapter = function(){
24720 };
24721
24722 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24723     // do nothing for now
24724     init : function(s){
24725     
24726     },
24727     /**
24728      * Called before drag operations to get the current size of the resizing element. 
24729      * @param {Roo.SplitBar} s The SplitBar using this adapter
24730      */
24731      getElementSize : function(s){
24732         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24733             return s.resizingEl.getWidth();
24734         }else{
24735             return s.resizingEl.getHeight();
24736         }
24737     },
24738     
24739     /**
24740      * Called after drag operations to set the size of the resizing element.
24741      * @param {Roo.SplitBar} s The SplitBar using this adapter
24742      * @param {Number} newSize The new size to set
24743      * @param {Function} onComplete A function to be invoked when resizing is complete
24744      */
24745     setElementSize : function(s, newSize, onComplete){
24746         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24747             if(!s.animate){
24748                 s.resizingEl.setWidth(newSize);
24749                 if(onComplete){
24750                     onComplete(s, newSize);
24751                 }
24752             }else{
24753                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24754             }
24755         }else{
24756             
24757             if(!s.animate){
24758                 s.resizingEl.setHeight(newSize);
24759                 if(onComplete){
24760                     onComplete(s, newSize);
24761                 }
24762             }else{
24763                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24764             }
24765         }
24766     }
24767 };
24768
24769 /** 
24770  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24771  * @extends Roo.SplitBar.BasicLayoutAdapter
24772  * Adapter that  moves the splitter element to align with the resized sizing element. 
24773  * Used with an absolute positioned SplitBar.
24774  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24775  * document.body, make sure you assign an id to the body element.
24776  */
24777 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24778     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24779     this.container = Roo.get(container);
24780 };
24781
24782 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24783     init : function(s){
24784         this.basic.init(s);
24785     },
24786     
24787     getElementSize : function(s){
24788         return this.basic.getElementSize(s);
24789     },
24790     
24791     setElementSize : function(s, newSize, onComplete){
24792         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24793     },
24794     
24795     moveSplitter : function(s){
24796         var yes = Roo.SplitBar;
24797         switch(s.placement){
24798             case yes.LEFT:
24799                 s.el.setX(s.resizingEl.getRight());
24800                 break;
24801             case yes.RIGHT:
24802                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24803                 break;
24804             case yes.TOP:
24805                 s.el.setY(s.resizingEl.getBottom());
24806                 break;
24807             case yes.BOTTOM:
24808                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24809                 break;
24810         }
24811     }
24812 };
24813
24814 /**
24815  * Orientation constant - Create a vertical SplitBar
24816  * @static
24817  * @type Number
24818  */
24819 Roo.SplitBar.VERTICAL = 1;
24820
24821 /**
24822  * Orientation constant - Create a horizontal SplitBar
24823  * @static
24824  * @type Number
24825  */
24826 Roo.SplitBar.HORIZONTAL = 2;
24827
24828 /**
24829  * Placement constant - The resizing element is to the left of the splitter element
24830  * @static
24831  * @type Number
24832  */
24833 Roo.SplitBar.LEFT = 1;
24834
24835 /**
24836  * Placement constant - The resizing element is to the right of the splitter element
24837  * @static
24838  * @type Number
24839  */
24840 Roo.SplitBar.RIGHT = 2;
24841
24842 /**
24843  * Placement constant - The resizing element is positioned above the splitter element
24844  * @static
24845  * @type Number
24846  */
24847 Roo.SplitBar.TOP = 3;
24848
24849 /**
24850  * Placement constant - The resizing element is positioned under splitter element
24851  * @static
24852  * @type Number
24853  */
24854 Roo.SplitBar.BOTTOM = 4;
24855 /*
24856  * Based on:
24857  * Ext JS Library 1.1.1
24858  * Copyright(c) 2006-2007, Ext JS, LLC.
24859  *
24860  * Originally Released Under LGPL - original licence link has changed is not relivant.
24861  *
24862  * Fork - LGPL
24863  * <script type="text/javascript">
24864  */
24865
24866 /**
24867  * @class Roo.View
24868  * @extends Roo.util.Observable
24869  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24870  * This class also supports single and multi selection modes. <br>
24871  * Create a data model bound view:
24872  <pre><code>
24873  var store = new Roo.data.Store(...);
24874
24875  var view = new Roo.View({
24876     el : "my-element",
24877     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24878  
24879     singleSelect: true,
24880     selectedClass: "ydataview-selected",
24881     store: store
24882  });
24883
24884  // listen for node click?
24885  view.on("click", function(vw, index, node, e){
24886  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24887  });
24888
24889  // load XML data
24890  dataModel.load("foobar.xml");
24891  </code></pre>
24892  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24893  * <br><br>
24894  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24895  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24896  * 
24897  * Note: old style constructor is still suported (container, template, config)
24898  * 
24899  * @constructor
24900  * Create a new View
24901  * @param {Object} config The config object
24902  * 
24903  */
24904 Roo.View = function(config, depreciated_tpl, depreciated_config){
24905     
24906     this.parent = false;
24907     
24908     if (typeof(depreciated_tpl) == 'undefined') {
24909         // new way.. - universal constructor.
24910         Roo.apply(this, config);
24911         this.el  = Roo.get(this.el);
24912     } else {
24913         // old format..
24914         this.el  = Roo.get(config);
24915         this.tpl = depreciated_tpl;
24916         Roo.apply(this, depreciated_config);
24917     }
24918     this.wrapEl  = this.el.wrap().wrap();
24919     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24920     
24921     
24922     if(typeof(this.tpl) == "string"){
24923         this.tpl = new Roo.Template(this.tpl);
24924     } else {
24925         // support xtype ctors..
24926         this.tpl = new Roo.factory(this.tpl, Roo);
24927     }
24928     
24929     
24930     this.tpl.compile();
24931     
24932     /** @private */
24933     this.addEvents({
24934         /**
24935          * @event beforeclick
24936          * Fires before a click is processed. Returns false to cancel the default action.
24937          * @param {Roo.View} this
24938          * @param {Number} index The index of the target node
24939          * @param {HTMLElement} node The target node
24940          * @param {Roo.EventObject} e The raw event object
24941          */
24942             "beforeclick" : true,
24943         /**
24944          * @event click
24945          * Fires when a template node is clicked.
24946          * @param {Roo.View} this
24947          * @param {Number} index The index of the target node
24948          * @param {HTMLElement} node The target node
24949          * @param {Roo.EventObject} e The raw event object
24950          */
24951             "click" : true,
24952         /**
24953          * @event dblclick
24954          * Fires when a template node is double clicked.
24955          * @param {Roo.View} this
24956          * @param {Number} index The index of the target node
24957          * @param {HTMLElement} node The target node
24958          * @param {Roo.EventObject} e The raw event object
24959          */
24960             "dblclick" : true,
24961         /**
24962          * @event contextmenu
24963          * Fires when a template node is right clicked.
24964          * @param {Roo.View} this
24965          * @param {Number} index The index of the target node
24966          * @param {HTMLElement} node The target node
24967          * @param {Roo.EventObject} e The raw event object
24968          */
24969             "contextmenu" : true,
24970         /**
24971          * @event selectionchange
24972          * Fires when the selected nodes change.
24973          * @param {Roo.View} this
24974          * @param {Array} selections Array of the selected nodes
24975          */
24976             "selectionchange" : true,
24977     
24978         /**
24979          * @event beforeselect
24980          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24981          * @param {Roo.View} this
24982          * @param {HTMLElement} node The node to be selected
24983          * @param {Array} selections Array of currently selected nodes
24984          */
24985             "beforeselect" : true,
24986         /**
24987          * @event preparedata
24988          * Fires on every row to render, to allow you to change the data.
24989          * @param {Roo.View} this
24990          * @param {Object} data to be rendered (change this)
24991          */
24992           "preparedata" : true
24993           
24994           
24995         });
24996
24997
24998
24999     this.el.on({
25000         "click": this.onClick,
25001         "dblclick": this.onDblClick,
25002         "contextmenu": this.onContextMenu,
25003         scope:this
25004     });
25005
25006     this.selections = [];
25007     this.nodes = [];
25008     this.cmp = new Roo.CompositeElementLite([]);
25009     if(this.store){
25010         this.store = Roo.factory(this.store, Roo.data);
25011         this.setStore(this.store, true);
25012     }
25013     
25014     if ( this.footer && this.footer.xtype) {
25015            
25016          var fctr = this.wrapEl.appendChild(document.createElement("div"));
25017         
25018         this.footer.dataSource = this.store
25019         this.footer.container = fctr;
25020         this.footer = Roo.factory(this.footer, Roo);
25021         fctr.insertFirst(this.el);
25022         
25023         // this is a bit insane - as the paging toolbar seems to detach the el..
25024 //        dom.parentNode.parentNode.parentNode
25025          // they get detached?
25026     }
25027     
25028     
25029     Roo.View.superclass.constructor.call(this);
25030     
25031     
25032 };
25033
25034 Roo.extend(Roo.View, Roo.util.Observable, {
25035     
25036      /**
25037      * @cfg {Roo.data.Store} store Data store to load data from.
25038      */
25039     store : false,
25040     
25041     /**
25042      * @cfg {String|Roo.Element} el The container element.
25043      */
25044     el : '',
25045     
25046     /**
25047      * @cfg {String|Roo.Template} tpl The template used by this View 
25048      */
25049     tpl : false,
25050     /**
25051      * @cfg {String} dataName the named area of the template to use as the data area
25052      *                          Works with domtemplates roo-name="name"
25053      */
25054     dataName: false,
25055     /**
25056      * @cfg {String} selectedClass The css class to add to selected nodes
25057      */
25058     selectedClass : "x-view-selected",
25059      /**
25060      * @cfg {String} emptyText The empty text to show when nothing is loaded.
25061      */
25062     emptyText : "",
25063     
25064     /**
25065      * @cfg {String} text to display on mask (default Loading)
25066      */
25067     mask : false,
25068     /**
25069      * @cfg {Boolean} multiSelect Allow multiple selection
25070      */
25071     multiSelect : false,
25072     /**
25073      * @cfg {Boolean} singleSelect Allow single selection
25074      */
25075     singleSelect:  false,
25076     
25077     /**
25078      * @cfg {Boolean} toggleSelect - selecting 
25079      */
25080     toggleSelect : false,
25081     
25082     /**
25083      * @cfg {Boolean} tickable - selecting 
25084      */
25085     tickable : false,
25086     
25087     /**
25088      * Returns the element this view is bound to.
25089      * @return {Roo.Element}
25090      */
25091     getEl : function(){
25092         return this.wrapEl;
25093     },
25094     
25095     
25096
25097     /**
25098      * Refreshes the view. - called by datachanged on the store. - do not call directly.
25099      */
25100     refresh : function(){
25101         //Roo.log('refresh');
25102         var t = this.tpl;
25103         
25104         // if we are using something like 'domtemplate', then
25105         // the what gets used is:
25106         // t.applySubtemplate(NAME, data, wrapping data..)
25107         // the outer template then get' applied with
25108         //     the store 'extra data'
25109         // and the body get's added to the
25110         //      roo-name="data" node?
25111         //      <span class='roo-tpl-{name}'></span> ?????
25112         
25113         
25114         
25115         this.clearSelections();
25116         this.el.update("");
25117         var html = [];
25118         var records = this.store.getRange();
25119         if(records.length < 1) {
25120             
25121             // is this valid??  = should it render a template??
25122             
25123             this.el.update(this.emptyText);
25124             return;
25125         }
25126         var el = this.el;
25127         if (this.dataName) {
25128             this.el.update(t.apply(this.store.meta)); //????
25129             el = this.el.child('.roo-tpl-' + this.dataName);
25130         }
25131         
25132         for(var i = 0, len = records.length; i < len; i++){
25133             var data = this.prepareData(records[i].data, i, records[i]);
25134             this.fireEvent("preparedata", this, data, i, records[i]);
25135             
25136             var d = Roo.apply({}, data);
25137             
25138             if(this.tickable){
25139                 Roo.apply(d, {'roo-id' : Roo.id()});
25140                 
25141                 var _this = this;
25142             
25143                 Roo.each(this.parent.item, function(item){
25144                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
25145                         return;
25146                     }
25147                     Roo.apply(d, {'roo-data-checked' : 'checked'});
25148                 });
25149             }
25150             
25151             html[html.length] = Roo.util.Format.trim(
25152                 this.dataName ?
25153                     t.applySubtemplate(this.dataName, d, this.store.meta) :
25154                     t.apply(d)
25155             );
25156         }
25157         
25158         
25159         
25160         el.update(html.join(""));
25161         this.nodes = el.dom.childNodes;
25162         this.updateIndexes(0);
25163     },
25164     
25165
25166     /**
25167      * Function to override to reformat the data that is sent to
25168      * the template for each node.
25169      * DEPRICATED - use the preparedata event handler.
25170      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
25171      * a JSON object for an UpdateManager bound view).
25172      */
25173     prepareData : function(data, index, record)
25174     {
25175         this.fireEvent("preparedata", this, data, index, record);
25176         return data;
25177     },
25178
25179     onUpdate : function(ds, record){
25180         // Roo.log('on update');   
25181         this.clearSelections();
25182         var index = this.store.indexOf(record);
25183         var n = this.nodes[index];
25184         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25185         n.parentNode.removeChild(n);
25186         this.updateIndexes(index, index);
25187     },
25188
25189     
25190     
25191 // --------- FIXME     
25192     onAdd : function(ds, records, index)
25193     {
25194         //Roo.log(['on Add', ds, records, index] );        
25195         this.clearSelections();
25196         if(this.nodes.length == 0){
25197             this.refresh();
25198             return;
25199         }
25200         var n = this.nodes[index];
25201         for(var i = 0, len = records.length; i < len; i++){
25202             var d = this.prepareData(records[i].data, i, records[i]);
25203             if(n){
25204                 this.tpl.insertBefore(n, d);
25205             }else{
25206                 
25207                 this.tpl.append(this.el, d);
25208             }
25209         }
25210         this.updateIndexes(index);
25211     },
25212
25213     onRemove : function(ds, record, index){
25214        // Roo.log('onRemove');
25215         this.clearSelections();
25216         var el = this.dataName  ?
25217             this.el.child('.roo-tpl-' + this.dataName) :
25218             this.el; 
25219         
25220         el.dom.removeChild(this.nodes[index]);
25221         this.updateIndexes(index);
25222     },
25223
25224     /**
25225      * Refresh an individual node.
25226      * @param {Number} index
25227      */
25228     refreshNode : function(index){
25229         this.onUpdate(this.store, this.store.getAt(index));
25230     },
25231
25232     updateIndexes : function(startIndex, endIndex){
25233         var ns = this.nodes;
25234         startIndex = startIndex || 0;
25235         endIndex = endIndex || ns.length - 1;
25236         for(var i = startIndex; i <= endIndex; i++){
25237             ns[i].nodeIndex = i;
25238         }
25239     },
25240
25241     /**
25242      * Changes the data store this view uses and refresh the view.
25243      * @param {Store} store
25244      */
25245     setStore : function(store, initial){
25246         if(!initial && this.store){
25247             this.store.un("datachanged", this.refresh);
25248             this.store.un("add", this.onAdd);
25249             this.store.un("remove", this.onRemove);
25250             this.store.un("update", this.onUpdate);
25251             this.store.un("clear", this.refresh);
25252             this.store.un("beforeload", this.onBeforeLoad);
25253             this.store.un("load", this.onLoad);
25254             this.store.un("loadexception", this.onLoad);
25255         }
25256         if(store){
25257           
25258             store.on("datachanged", this.refresh, this);
25259             store.on("add", this.onAdd, this);
25260             store.on("remove", this.onRemove, this);
25261             store.on("update", this.onUpdate, this);
25262             store.on("clear", this.refresh, this);
25263             store.on("beforeload", this.onBeforeLoad, this);
25264             store.on("load", this.onLoad, this);
25265             store.on("loadexception", this.onLoad, this);
25266         }
25267         
25268         if(store){
25269             this.refresh();
25270         }
25271     },
25272     /**
25273      * onbeforeLoad - masks the loading area.
25274      *
25275      */
25276     onBeforeLoad : function(store,opts)
25277     {
25278          //Roo.log('onBeforeLoad');   
25279         if (!opts.add) {
25280             this.el.update("");
25281         }
25282         this.el.mask(this.mask ? this.mask : "Loading" ); 
25283     },
25284     onLoad : function ()
25285     {
25286         this.el.unmask();
25287     },
25288     
25289
25290     /**
25291      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25292      * @param {HTMLElement} node
25293      * @return {HTMLElement} The template node
25294      */
25295     findItemFromChild : function(node){
25296         var el = this.dataName  ?
25297             this.el.child('.roo-tpl-' + this.dataName,true) :
25298             this.el.dom; 
25299         
25300         if(!node || node.parentNode == el){
25301                     return node;
25302             }
25303             var p = node.parentNode;
25304             while(p && p != el){
25305             if(p.parentNode == el){
25306                 return p;
25307             }
25308             p = p.parentNode;
25309         }
25310             return null;
25311     },
25312
25313     /** @ignore */
25314     onClick : function(e){
25315         var item = this.findItemFromChild(e.getTarget());
25316         if(item){
25317             var index = this.indexOf(item);
25318             if(this.onItemClick(item, index, e) !== false){
25319                 this.fireEvent("click", this, index, item, e);
25320             }
25321         }else{
25322             this.clearSelections();
25323         }
25324     },
25325
25326     /** @ignore */
25327     onContextMenu : function(e){
25328         var item = this.findItemFromChild(e.getTarget());
25329         if(item){
25330             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25331         }
25332     },
25333
25334     /** @ignore */
25335     onDblClick : function(e){
25336         var item = this.findItemFromChild(e.getTarget());
25337         if(item){
25338             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25339         }
25340     },
25341
25342     onItemClick : function(item, index, e)
25343     {
25344         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25345             return false;
25346         }
25347         if (this.toggleSelect) {
25348             var m = this.isSelected(item) ? 'unselect' : 'select';
25349             //Roo.log(m);
25350             var _t = this;
25351             _t[m](item, true, false);
25352             return true;
25353         }
25354         if(this.multiSelect || this.singleSelect){
25355             if(this.multiSelect && e.shiftKey && this.lastSelection){
25356                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25357             }else{
25358                 this.select(item, this.multiSelect && e.ctrlKey);
25359                 this.lastSelection = item;
25360             }
25361             
25362             if(!this.tickable){
25363                 e.preventDefault();
25364             }
25365             
25366         }
25367         return true;
25368     },
25369
25370     /**
25371      * Get the number of selected nodes.
25372      * @return {Number}
25373      */
25374     getSelectionCount : function(){
25375         return this.selections.length;
25376     },
25377
25378     /**
25379      * Get the currently selected nodes.
25380      * @return {Array} An array of HTMLElements
25381      */
25382     getSelectedNodes : function(){
25383         return this.selections;
25384     },
25385
25386     /**
25387      * Get the indexes of the selected nodes.
25388      * @return {Array}
25389      */
25390     getSelectedIndexes : function(){
25391         var indexes = [], s = this.selections;
25392         for(var i = 0, len = s.length; i < len; i++){
25393             indexes.push(s[i].nodeIndex);
25394         }
25395         return indexes;
25396     },
25397
25398     /**
25399      * Clear all selections
25400      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25401      */
25402     clearSelections : function(suppressEvent){
25403         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25404             this.cmp.elements = this.selections;
25405             this.cmp.removeClass(this.selectedClass);
25406             this.selections = [];
25407             if(!suppressEvent){
25408                 this.fireEvent("selectionchange", this, this.selections);
25409             }
25410         }
25411     },
25412
25413     /**
25414      * Returns true if the passed node is selected
25415      * @param {HTMLElement/Number} node The node or node index
25416      * @return {Boolean}
25417      */
25418     isSelected : function(node){
25419         var s = this.selections;
25420         if(s.length < 1){
25421             return false;
25422         }
25423         node = this.getNode(node);
25424         return s.indexOf(node) !== -1;
25425     },
25426
25427     /**
25428      * Selects nodes.
25429      * @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
25430      * @param {Boolean} keepExisting (optional) true to keep existing selections
25431      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25432      */
25433     select : function(nodeInfo, keepExisting, suppressEvent){
25434         if(nodeInfo instanceof Array){
25435             if(!keepExisting){
25436                 this.clearSelections(true);
25437             }
25438             for(var i = 0, len = nodeInfo.length; i < len; i++){
25439                 this.select(nodeInfo[i], true, true);
25440             }
25441             return;
25442         } 
25443         var node = this.getNode(nodeInfo);
25444         if(!node || this.isSelected(node)){
25445             return; // already selected.
25446         }
25447         if(!keepExisting){
25448             this.clearSelections(true);
25449         }
25450         
25451         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25452             Roo.fly(node).addClass(this.selectedClass);
25453             this.selections.push(node);
25454             if(!suppressEvent){
25455                 this.fireEvent("selectionchange", this, this.selections);
25456             }
25457         }
25458         
25459         
25460     },
25461       /**
25462      * Unselects nodes.
25463      * @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
25464      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25465      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25466      */
25467     unselect : function(nodeInfo, keepExisting, suppressEvent)
25468     {
25469         if(nodeInfo instanceof Array){
25470             Roo.each(this.selections, function(s) {
25471                 this.unselect(s, nodeInfo);
25472             }, this);
25473             return;
25474         }
25475         var node = this.getNode(nodeInfo);
25476         if(!node || !this.isSelected(node)){
25477             //Roo.log("not selected");
25478             return; // not selected.
25479         }
25480         // fireevent???
25481         var ns = [];
25482         Roo.each(this.selections, function(s) {
25483             if (s == node ) {
25484                 Roo.fly(node).removeClass(this.selectedClass);
25485
25486                 return;
25487             }
25488             ns.push(s);
25489         },this);
25490         
25491         this.selections= ns;
25492         this.fireEvent("selectionchange", this, this.selections);
25493     },
25494
25495     /**
25496      * Gets a template node.
25497      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25498      * @return {HTMLElement} The node or null if it wasn't found
25499      */
25500     getNode : function(nodeInfo){
25501         if(typeof nodeInfo == "string"){
25502             return document.getElementById(nodeInfo);
25503         }else if(typeof nodeInfo == "number"){
25504             return this.nodes[nodeInfo];
25505         }
25506         return nodeInfo;
25507     },
25508
25509     /**
25510      * Gets a range template nodes.
25511      * @param {Number} startIndex
25512      * @param {Number} endIndex
25513      * @return {Array} An array of nodes
25514      */
25515     getNodes : function(start, end){
25516         var ns = this.nodes;
25517         start = start || 0;
25518         end = typeof end == "undefined" ? ns.length - 1 : end;
25519         var nodes = [];
25520         if(start <= end){
25521             for(var i = start; i <= end; i++){
25522                 nodes.push(ns[i]);
25523             }
25524         } else{
25525             for(var i = start; i >= end; i--){
25526                 nodes.push(ns[i]);
25527             }
25528         }
25529         return nodes;
25530     },
25531
25532     /**
25533      * Finds the index of the passed node
25534      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25535      * @return {Number} The index of the node or -1
25536      */
25537     indexOf : function(node){
25538         node = this.getNode(node);
25539         if(typeof node.nodeIndex == "number"){
25540             return node.nodeIndex;
25541         }
25542         var ns = this.nodes;
25543         for(var i = 0, len = ns.length; i < len; i++){
25544             if(ns[i] == node){
25545                 return i;
25546             }
25547         }
25548         return -1;
25549     }
25550 });
25551 /*
25552  * Based on:
25553  * Ext JS Library 1.1.1
25554  * Copyright(c) 2006-2007, Ext JS, LLC.
25555  *
25556  * Originally Released Under LGPL - original licence link has changed is not relivant.
25557  *
25558  * Fork - LGPL
25559  * <script type="text/javascript">
25560  */
25561
25562 /**
25563  * @class Roo.JsonView
25564  * @extends Roo.View
25565  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25566 <pre><code>
25567 var view = new Roo.JsonView({
25568     container: "my-element",
25569     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25570     multiSelect: true, 
25571     jsonRoot: "data" 
25572 });
25573
25574 // listen for node click?
25575 view.on("click", function(vw, index, node, e){
25576     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25577 });
25578
25579 // direct load of JSON data
25580 view.load("foobar.php");
25581
25582 // Example from my blog list
25583 var tpl = new Roo.Template(
25584     '&lt;div class="entry"&gt;' +
25585     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25586     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25587     "&lt;/div&gt;&lt;hr /&gt;"
25588 );
25589
25590 var moreView = new Roo.JsonView({
25591     container :  "entry-list", 
25592     template : tpl,
25593     jsonRoot: "posts"
25594 });
25595 moreView.on("beforerender", this.sortEntries, this);
25596 moreView.load({
25597     url: "/blog/get-posts.php",
25598     params: "allposts=true",
25599     text: "Loading Blog Entries..."
25600 });
25601 </code></pre>
25602
25603 * Note: old code is supported with arguments : (container, template, config)
25604
25605
25606  * @constructor
25607  * Create a new JsonView
25608  * 
25609  * @param {Object} config The config object
25610  * 
25611  */
25612 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25613     
25614     
25615     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25616
25617     var um = this.el.getUpdateManager();
25618     um.setRenderer(this);
25619     um.on("update", this.onLoad, this);
25620     um.on("failure", this.onLoadException, this);
25621
25622     /**
25623      * @event beforerender
25624      * Fires before rendering of the downloaded JSON data.
25625      * @param {Roo.JsonView} this
25626      * @param {Object} data The JSON data loaded
25627      */
25628     /**
25629      * @event load
25630      * Fires when data is loaded.
25631      * @param {Roo.JsonView} this
25632      * @param {Object} data The JSON data loaded
25633      * @param {Object} response The raw Connect response object
25634      */
25635     /**
25636      * @event loadexception
25637      * Fires when loading fails.
25638      * @param {Roo.JsonView} this
25639      * @param {Object} response The raw Connect response object
25640      */
25641     this.addEvents({
25642         'beforerender' : true,
25643         'load' : true,
25644         'loadexception' : true
25645     });
25646 };
25647 Roo.extend(Roo.JsonView, Roo.View, {
25648     /**
25649      * @type {String} The root property in the loaded JSON object that contains the data
25650      */
25651     jsonRoot : "",
25652
25653     /**
25654      * Refreshes the view.
25655      */
25656     refresh : function(){
25657         this.clearSelections();
25658         this.el.update("");
25659         var html = [];
25660         var o = this.jsonData;
25661         if(o && o.length > 0){
25662             for(var i = 0, len = o.length; i < len; i++){
25663                 var data = this.prepareData(o[i], i, o);
25664                 html[html.length] = this.tpl.apply(data);
25665             }
25666         }else{
25667             html.push(this.emptyText);
25668         }
25669         this.el.update(html.join(""));
25670         this.nodes = this.el.dom.childNodes;
25671         this.updateIndexes(0);
25672     },
25673
25674     /**
25675      * 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.
25676      * @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:
25677      <pre><code>
25678      view.load({
25679          url: "your-url.php",
25680          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25681          callback: yourFunction,
25682          scope: yourObject, //(optional scope)
25683          discardUrl: false,
25684          nocache: false,
25685          text: "Loading...",
25686          timeout: 30,
25687          scripts: false
25688      });
25689      </code></pre>
25690      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25691      * 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.
25692      * @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}
25693      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25694      * @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.
25695      */
25696     load : function(){
25697         var um = this.el.getUpdateManager();
25698         um.update.apply(um, arguments);
25699     },
25700
25701     render : function(el, response){
25702         this.clearSelections();
25703         this.el.update("");
25704         var o;
25705         try{
25706             o = Roo.util.JSON.decode(response.responseText);
25707             if(this.jsonRoot){
25708                 
25709                 o = o[this.jsonRoot];
25710             }
25711         } catch(e){
25712         }
25713         /**
25714          * The current JSON data or null
25715          */
25716         this.jsonData = o;
25717         this.beforeRender();
25718         this.refresh();
25719     },
25720
25721 /**
25722  * Get the number of records in the current JSON dataset
25723  * @return {Number}
25724  */
25725     getCount : function(){
25726         return this.jsonData ? this.jsonData.length : 0;
25727     },
25728
25729 /**
25730  * Returns the JSON object for the specified node(s)
25731  * @param {HTMLElement/Array} node The node or an array of nodes
25732  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25733  * you get the JSON object for the node
25734  */
25735     getNodeData : function(node){
25736         if(node instanceof Array){
25737             var data = [];
25738             for(var i = 0, len = node.length; i < len; i++){
25739                 data.push(this.getNodeData(node[i]));
25740             }
25741             return data;
25742         }
25743         return this.jsonData[this.indexOf(node)] || null;
25744     },
25745
25746     beforeRender : function(){
25747         this.snapshot = this.jsonData;
25748         if(this.sortInfo){
25749             this.sort.apply(this, this.sortInfo);
25750         }
25751         this.fireEvent("beforerender", this, this.jsonData);
25752     },
25753
25754     onLoad : function(el, o){
25755         this.fireEvent("load", this, this.jsonData, o);
25756     },
25757
25758     onLoadException : function(el, o){
25759         this.fireEvent("loadexception", this, o);
25760     },
25761
25762 /**
25763  * Filter the data by a specific property.
25764  * @param {String} property A property on your JSON objects
25765  * @param {String/RegExp} value Either string that the property values
25766  * should start with, or a RegExp to test against the property
25767  */
25768     filter : function(property, value){
25769         if(this.jsonData){
25770             var data = [];
25771             var ss = this.snapshot;
25772             if(typeof value == "string"){
25773                 var vlen = value.length;
25774                 if(vlen == 0){
25775                     this.clearFilter();
25776                     return;
25777                 }
25778                 value = value.toLowerCase();
25779                 for(var i = 0, len = ss.length; i < len; i++){
25780                     var o = ss[i];
25781                     if(o[property].substr(0, vlen).toLowerCase() == value){
25782                         data.push(o);
25783                     }
25784                 }
25785             } else if(value.exec){ // regex?
25786                 for(var i = 0, len = ss.length; i < len; i++){
25787                     var o = ss[i];
25788                     if(value.test(o[property])){
25789                         data.push(o);
25790                     }
25791                 }
25792             } else{
25793                 return;
25794             }
25795             this.jsonData = data;
25796             this.refresh();
25797         }
25798     },
25799
25800 /**
25801  * Filter by a function. The passed function will be called with each
25802  * object in the current dataset. If the function returns true the value is kept,
25803  * otherwise it is filtered.
25804  * @param {Function} fn
25805  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25806  */
25807     filterBy : function(fn, scope){
25808         if(this.jsonData){
25809             var data = [];
25810             var ss = this.snapshot;
25811             for(var i = 0, len = ss.length; i < len; i++){
25812                 var o = ss[i];
25813                 if(fn.call(scope || this, o)){
25814                     data.push(o);
25815                 }
25816             }
25817             this.jsonData = data;
25818             this.refresh();
25819         }
25820     },
25821
25822 /**
25823  * Clears the current filter.
25824  */
25825     clearFilter : function(){
25826         if(this.snapshot && this.jsonData != this.snapshot){
25827             this.jsonData = this.snapshot;
25828             this.refresh();
25829         }
25830     },
25831
25832
25833 /**
25834  * Sorts the data for this view and refreshes it.
25835  * @param {String} property A property on your JSON objects to sort on
25836  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25837  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25838  */
25839     sort : function(property, dir, sortType){
25840         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25841         if(this.jsonData){
25842             var p = property;
25843             var dsc = dir && dir.toLowerCase() == "desc";
25844             var f = function(o1, o2){
25845                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25846                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25847                 ;
25848                 if(v1 < v2){
25849                     return dsc ? +1 : -1;
25850                 } else if(v1 > v2){
25851                     return dsc ? -1 : +1;
25852                 } else{
25853                     return 0;
25854                 }
25855             };
25856             this.jsonData.sort(f);
25857             this.refresh();
25858             if(this.jsonData != this.snapshot){
25859                 this.snapshot.sort(f);
25860             }
25861         }
25862     }
25863 });/*
25864  * Based on:
25865  * Ext JS Library 1.1.1
25866  * Copyright(c) 2006-2007, Ext JS, LLC.
25867  *
25868  * Originally Released Under LGPL - original licence link has changed is not relivant.
25869  *
25870  * Fork - LGPL
25871  * <script type="text/javascript">
25872  */
25873  
25874
25875 /**
25876  * @class Roo.ColorPalette
25877  * @extends Roo.Component
25878  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25879  * Here's an example of typical usage:
25880  * <pre><code>
25881 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25882 cp.render('my-div');
25883
25884 cp.on('select', function(palette, selColor){
25885     // do something with selColor
25886 });
25887 </code></pre>
25888  * @constructor
25889  * Create a new ColorPalette
25890  * @param {Object} config The config object
25891  */
25892 Roo.ColorPalette = function(config){
25893     Roo.ColorPalette.superclass.constructor.call(this, config);
25894     this.addEvents({
25895         /**
25896              * @event select
25897              * Fires when a color is selected
25898              * @param {ColorPalette} this
25899              * @param {String} color The 6-digit color hex code (without the # symbol)
25900              */
25901         select: true
25902     });
25903
25904     if(this.handler){
25905         this.on("select", this.handler, this.scope, true);
25906     }
25907 };
25908 Roo.extend(Roo.ColorPalette, Roo.Component, {
25909     /**
25910      * @cfg {String} itemCls
25911      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25912      */
25913     itemCls : "x-color-palette",
25914     /**
25915      * @cfg {String} value
25916      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25917      * the hex codes are case-sensitive.
25918      */
25919     value : null,
25920     clickEvent:'click',
25921     // private
25922     ctype: "Roo.ColorPalette",
25923
25924     /**
25925      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25926      */
25927     allowReselect : false,
25928
25929     /**
25930      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25931      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25932      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25933      * of colors with the width setting until the box is symmetrical.</p>
25934      * <p>You can override individual colors if needed:</p>
25935      * <pre><code>
25936 var cp = new Roo.ColorPalette();
25937 cp.colors[0] = "FF0000";  // change the first box to red
25938 </code></pre>
25939
25940 Or you can provide a custom array of your own for complete control:
25941 <pre><code>
25942 var cp = new Roo.ColorPalette();
25943 cp.colors = ["000000", "993300", "333300"];
25944 </code></pre>
25945      * @type Array
25946      */
25947     colors : [
25948         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25949         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25950         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25951         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25952         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25953     ],
25954
25955     // private
25956     onRender : function(container, position){
25957         var t = new Roo.MasterTemplate(
25958             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25959         );
25960         var c = this.colors;
25961         for(var i = 0, len = c.length; i < len; i++){
25962             t.add([c[i]]);
25963         }
25964         var el = document.createElement("div");
25965         el.className = this.itemCls;
25966         t.overwrite(el);
25967         container.dom.insertBefore(el, position);
25968         this.el = Roo.get(el);
25969         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25970         if(this.clickEvent != 'click'){
25971             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25972         }
25973     },
25974
25975     // private
25976     afterRender : function(){
25977         Roo.ColorPalette.superclass.afterRender.call(this);
25978         if(this.value){
25979             var s = this.value;
25980             this.value = null;
25981             this.select(s);
25982         }
25983     },
25984
25985     // private
25986     handleClick : function(e, t){
25987         e.preventDefault();
25988         if(!this.disabled){
25989             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25990             this.select(c.toUpperCase());
25991         }
25992     },
25993
25994     /**
25995      * Selects the specified color in the palette (fires the select event)
25996      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25997      */
25998     select : function(color){
25999         color = color.replace("#", "");
26000         if(color != this.value || this.allowReselect){
26001             var el = this.el;
26002             if(this.value){
26003                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
26004             }
26005             el.child("a.color-"+color).addClass("x-color-palette-sel");
26006             this.value = color;
26007             this.fireEvent("select", this, color);
26008         }
26009     }
26010 });/*
26011  * Based on:
26012  * Ext JS Library 1.1.1
26013  * Copyright(c) 2006-2007, Ext JS, LLC.
26014  *
26015  * Originally Released Under LGPL - original licence link has changed is not relivant.
26016  *
26017  * Fork - LGPL
26018  * <script type="text/javascript">
26019  */
26020  
26021 /**
26022  * @class Roo.DatePicker
26023  * @extends Roo.Component
26024  * Simple date picker class.
26025  * @constructor
26026  * Create a new DatePicker
26027  * @param {Object} config The config object
26028  */
26029 Roo.DatePicker = function(config){
26030     Roo.DatePicker.superclass.constructor.call(this, config);
26031
26032     this.value = config && config.value ?
26033                  config.value.clearTime() : new Date().clearTime();
26034
26035     this.addEvents({
26036         /**
26037              * @event select
26038              * Fires when a date is selected
26039              * @param {DatePicker} this
26040              * @param {Date} date The selected date
26041              */
26042         'select': true,
26043         /**
26044              * @event monthchange
26045              * Fires when the displayed month changes 
26046              * @param {DatePicker} this
26047              * @param {Date} date The selected month
26048              */
26049         'monthchange': true
26050     });
26051
26052     if(this.handler){
26053         this.on("select", this.handler,  this.scope || this);
26054     }
26055     // build the disabledDatesRE
26056     if(!this.disabledDatesRE && this.disabledDates){
26057         var dd = this.disabledDates;
26058         var re = "(?:";
26059         for(var i = 0; i < dd.length; i++){
26060             re += dd[i];
26061             if(i != dd.length-1) re += "|";
26062         }
26063         this.disabledDatesRE = new RegExp(re + ")");
26064     }
26065 };
26066
26067 Roo.extend(Roo.DatePicker, Roo.Component, {
26068     /**
26069      * @cfg {String} todayText
26070      * The text to display on the button that selects the current date (defaults to "Today")
26071      */
26072     todayText : "Today",
26073     /**
26074      * @cfg {String} okText
26075      * The text to display on the ok button
26076      */
26077     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
26078     /**
26079      * @cfg {String} cancelText
26080      * The text to display on the cancel button
26081      */
26082     cancelText : "Cancel",
26083     /**
26084      * @cfg {String} todayTip
26085      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
26086      */
26087     todayTip : "{0} (Spacebar)",
26088     /**
26089      * @cfg {Date} minDate
26090      * Minimum allowable date (JavaScript date object, defaults to null)
26091      */
26092     minDate : null,
26093     /**
26094      * @cfg {Date} maxDate
26095      * Maximum allowable date (JavaScript date object, defaults to null)
26096      */
26097     maxDate : null,
26098     /**
26099      * @cfg {String} minText
26100      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
26101      */
26102     minText : "This date is before the minimum date",
26103     /**
26104      * @cfg {String} maxText
26105      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
26106      */
26107     maxText : "This date is after the maximum date",
26108     /**
26109      * @cfg {String} format
26110      * The default date format string which can be overriden for localization support.  The format must be
26111      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
26112      */
26113     format : "m/d/y",
26114     /**
26115      * @cfg {Array} disabledDays
26116      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
26117      */
26118     disabledDays : null,
26119     /**
26120      * @cfg {String} disabledDaysText
26121      * The tooltip to display when the date falls on a disabled day (defaults to "")
26122      */
26123     disabledDaysText : "",
26124     /**
26125      * @cfg {RegExp} disabledDatesRE
26126      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
26127      */
26128     disabledDatesRE : null,
26129     /**
26130      * @cfg {String} disabledDatesText
26131      * The tooltip text to display when the date falls on a disabled date (defaults to "")
26132      */
26133     disabledDatesText : "",
26134     /**
26135      * @cfg {Boolean} constrainToViewport
26136      * True to constrain the date picker to the viewport (defaults to true)
26137      */
26138     constrainToViewport : true,
26139     /**
26140      * @cfg {Array} monthNames
26141      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
26142      */
26143     monthNames : Date.monthNames,
26144     /**
26145      * @cfg {Array} dayNames
26146      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
26147      */
26148     dayNames : Date.dayNames,
26149     /**
26150      * @cfg {String} nextText
26151      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
26152      */
26153     nextText: 'Next Month (Control+Right)',
26154     /**
26155      * @cfg {String} prevText
26156      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
26157      */
26158     prevText: 'Previous Month (Control+Left)',
26159     /**
26160      * @cfg {String} monthYearText
26161      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
26162      */
26163     monthYearText: 'Choose a month (Control+Up/Down to move years)',
26164     /**
26165      * @cfg {Number} startDay
26166      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
26167      */
26168     startDay : 0,
26169     /**
26170      * @cfg {Bool} showClear
26171      * Show a clear button (usefull for date form elements that can be blank.)
26172      */
26173     
26174     showClear: false,
26175     
26176     /**
26177      * Sets the value of the date field
26178      * @param {Date} value The date to set
26179      */
26180     setValue : function(value){
26181         var old = this.value;
26182         
26183         if (typeof(value) == 'string') {
26184          
26185             value = Date.parseDate(value, this.format);
26186         }
26187         if (!value) {
26188             value = new Date();
26189         }
26190         
26191         this.value = value.clearTime(true);
26192         if(this.el){
26193             this.update(this.value);
26194         }
26195     },
26196
26197     /**
26198      * Gets the current selected value of the date field
26199      * @return {Date} The selected date
26200      */
26201     getValue : function(){
26202         return this.value;
26203     },
26204
26205     // private
26206     focus : function(){
26207         if(this.el){
26208             this.update(this.activeDate);
26209         }
26210     },
26211
26212     // privateval
26213     onRender : function(container, position){
26214         
26215         var m = [
26216              '<table cellspacing="0">',
26217                 '<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>',
26218                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26219         var dn = this.dayNames;
26220         for(var i = 0; i < 7; i++){
26221             var d = this.startDay+i;
26222             if(d > 6){
26223                 d = d-7;
26224             }
26225             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26226         }
26227         m[m.length] = "</tr></thead><tbody><tr>";
26228         for(var i = 0; i < 42; i++) {
26229             if(i % 7 == 0 && i != 0){
26230                 m[m.length] = "</tr><tr>";
26231             }
26232             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26233         }
26234         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26235             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26236
26237         var el = document.createElement("div");
26238         el.className = "x-date-picker";
26239         el.innerHTML = m.join("");
26240
26241         container.dom.insertBefore(el, position);
26242
26243         this.el = Roo.get(el);
26244         this.eventEl = Roo.get(el.firstChild);
26245
26246         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26247             handler: this.showPrevMonth,
26248             scope: this,
26249             preventDefault:true,
26250             stopDefault:true
26251         });
26252
26253         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26254             handler: this.showNextMonth,
26255             scope: this,
26256             preventDefault:true,
26257             stopDefault:true
26258         });
26259
26260         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26261
26262         this.monthPicker = this.el.down('div.x-date-mp');
26263         this.monthPicker.enableDisplayMode('block');
26264         
26265         var kn = new Roo.KeyNav(this.eventEl, {
26266             "left" : function(e){
26267                 e.ctrlKey ?
26268                     this.showPrevMonth() :
26269                     this.update(this.activeDate.add("d", -1));
26270             },
26271
26272             "right" : function(e){
26273                 e.ctrlKey ?
26274                     this.showNextMonth() :
26275                     this.update(this.activeDate.add("d", 1));
26276             },
26277
26278             "up" : function(e){
26279                 e.ctrlKey ?
26280                     this.showNextYear() :
26281                     this.update(this.activeDate.add("d", -7));
26282             },
26283
26284             "down" : function(e){
26285                 e.ctrlKey ?
26286                     this.showPrevYear() :
26287                     this.update(this.activeDate.add("d", 7));
26288             },
26289
26290             "pageUp" : function(e){
26291                 this.showNextMonth();
26292             },
26293
26294             "pageDown" : function(e){
26295                 this.showPrevMonth();
26296             },
26297
26298             "enter" : function(e){
26299                 e.stopPropagation();
26300                 return true;
26301             },
26302
26303             scope : this
26304         });
26305
26306         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26307
26308         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26309
26310         this.el.unselectable();
26311         
26312         this.cells = this.el.select("table.x-date-inner tbody td");
26313         this.textNodes = this.el.query("table.x-date-inner tbody span");
26314
26315         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26316             text: "&#160;",
26317             tooltip: this.monthYearText
26318         });
26319
26320         this.mbtn.on('click', this.showMonthPicker, this);
26321         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26322
26323
26324         var today = (new Date()).dateFormat(this.format);
26325         
26326         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26327         if (this.showClear) {
26328             baseTb.add( new Roo.Toolbar.Fill());
26329         }
26330         baseTb.add({
26331             text: String.format(this.todayText, today),
26332             tooltip: String.format(this.todayTip, today),
26333             handler: this.selectToday,
26334             scope: this
26335         });
26336         
26337         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26338             
26339         //});
26340         if (this.showClear) {
26341             
26342             baseTb.add( new Roo.Toolbar.Fill());
26343             baseTb.add({
26344                 text: '&#160;',
26345                 cls: 'x-btn-icon x-btn-clear',
26346                 handler: function() {
26347                     //this.value = '';
26348                     this.fireEvent("select", this, '');
26349                 },
26350                 scope: this
26351             });
26352         }
26353         
26354         
26355         if(Roo.isIE){
26356             this.el.repaint();
26357         }
26358         this.update(this.value);
26359     },
26360
26361     createMonthPicker : function(){
26362         if(!this.monthPicker.dom.firstChild){
26363             var buf = ['<table border="0" cellspacing="0">'];
26364             for(var i = 0; i < 6; i++){
26365                 buf.push(
26366                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26367                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26368                     i == 0 ?
26369                     '<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>' :
26370                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26371                 );
26372             }
26373             buf.push(
26374                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26375                     this.okText,
26376                     '</button><button type="button" class="x-date-mp-cancel">',
26377                     this.cancelText,
26378                     '</button></td></tr>',
26379                 '</table>'
26380             );
26381             this.monthPicker.update(buf.join(''));
26382             this.monthPicker.on('click', this.onMonthClick, this);
26383             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26384
26385             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26386             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26387
26388             this.mpMonths.each(function(m, a, i){
26389                 i += 1;
26390                 if((i%2) == 0){
26391                     m.dom.xmonth = 5 + Math.round(i * .5);
26392                 }else{
26393                     m.dom.xmonth = Math.round((i-1) * .5);
26394                 }
26395             });
26396         }
26397     },
26398
26399     showMonthPicker : function(){
26400         this.createMonthPicker();
26401         var size = this.el.getSize();
26402         this.monthPicker.setSize(size);
26403         this.monthPicker.child('table').setSize(size);
26404
26405         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26406         this.updateMPMonth(this.mpSelMonth);
26407         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26408         this.updateMPYear(this.mpSelYear);
26409
26410         this.monthPicker.slideIn('t', {duration:.2});
26411     },
26412
26413     updateMPYear : function(y){
26414         this.mpyear = y;
26415         var ys = this.mpYears.elements;
26416         for(var i = 1; i <= 10; i++){
26417             var td = ys[i-1], y2;
26418             if((i%2) == 0){
26419                 y2 = y + Math.round(i * .5);
26420                 td.firstChild.innerHTML = y2;
26421                 td.xyear = y2;
26422             }else{
26423                 y2 = y - (5-Math.round(i * .5));
26424                 td.firstChild.innerHTML = y2;
26425                 td.xyear = y2;
26426             }
26427             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26428         }
26429     },
26430
26431     updateMPMonth : function(sm){
26432         this.mpMonths.each(function(m, a, i){
26433             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26434         });
26435     },
26436
26437     selectMPMonth: function(m){
26438         
26439     },
26440
26441     onMonthClick : function(e, t){
26442         e.stopEvent();
26443         var el = new Roo.Element(t), pn;
26444         if(el.is('button.x-date-mp-cancel')){
26445             this.hideMonthPicker();
26446         }
26447         else if(el.is('button.x-date-mp-ok')){
26448             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26449             this.hideMonthPicker();
26450         }
26451         else if(pn = el.up('td.x-date-mp-month', 2)){
26452             this.mpMonths.removeClass('x-date-mp-sel');
26453             pn.addClass('x-date-mp-sel');
26454             this.mpSelMonth = pn.dom.xmonth;
26455         }
26456         else if(pn = el.up('td.x-date-mp-year', 2)){
26457             this.mpYears.removeClass('x-date-mp-sel');
26458             pn.addClass('x-date-mp-sel');
26459             this.mpSelYear = pn.dom.xyear;
26460         }
26461         else if(el.is('a.x-date-mp-prev')){
26462             this.updateMPYear(this.mpyear-10);
26463         }
26464         else if(el.is('a.x-date-mp-next')){
26465             this.updateMPYear(this.mpyear+10);
26466         }
26467     },
26468
26469     onMonthDblClick : function(e, t){
26470         e.stopEvent();
26471         var el = new Roo.Element(t), pn;
26472         if(pn = el.up('td.x-date-mp-month', 2)){
26473             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26474             this.hideMonthPicker();
26475         }
26476         else if(pn = el.up('td.x-date-mp-year', 2)){
26477             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26478             this.hideMonthPicker();
26479         }
26480     },
26481
26482     hideMonthPicker : function(disableAnim){
26483         if(this.monthPicker){
26484             if(disableAnim === true){
26485                 this.monthPicker.hide();
26486             }else{
26487                 this.monthPicker.slideOut('t', {duration:.2});
26488             }
26489         }
26490     },
26491
26492     // private
26493     showPrevMonth : function(e){
26494         this.update(this.activeDate.add("mo", -1));
26495     },
26496
26497     // private
26498     showNextMonth : function(e){
26499         this.update(this.activeDate.add("mo", 1));
26500     },
26501
26502     // private
26503     showPrevYear : function(){
26504         this.update(this.activeDate.add("y", -1));
26505     },
26506
26507     // private
26508     showNextYear : function(){
26509         this.update(this.activeDate.add("y", 1));
26510     },
26511
26512     // private
26513     handleMouseWheel : function(e){
26514         var delta = e.getWheelDelta();
26515         if(delta > 0){
26516             this.showPrevMonth();
26517             e.stopEvent();
26518         } else if(delta < 0){
26519             this.showNextMonth();
26520             e.stopEvent();
26521         }
26522     },
26523
26524     // private
26525     handleDateClick : function(e, t){
26526         e.stopEvent();
26527         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26528             this.setValue(new Date(t.dateValue));
26529             this.fireEvent("select", this, this.value);
26530         }
26531     },
26532
26533     // private
26534     selectToday : function(){
26535         this.setValue(new Date().clearTime());
26536         this.fireEvent("select", this, this.value);
26537     },
26538
26539     // private
26540     update : function(date)
26541     {
26542         var vd = this.activeDate;
26543         this.activeDate = date;
26544         if(vd && this.el){
26545             var t = date.getTime();
26546             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26547                 this.cells.removeClass("x-date-selected");
26548                 this.cells.each(function(c){
26549                    if(c.dom.firstChild.dateValue == t){
26550                        c.addClass("x-date-selected");
26551                        setTimeout(function(){
26552                             try{c.dom.firstChild.focus();}catch(e){}
26553                        }, 50);
26554                        return false;
26555                    }
26556                 });
26557                 return;
26558             }
26559         }
26560         
26561         var days = date.getDaysInMonth();
26562         var firstOfMonth = date.getFirstDateOfMonth();
26563         var startingPos = firstOfMonth.getDay()-this.startDay;
26564
26565         if(startingPos <= this.startDay){
26566             startingPos += 7;
26567         }
26568
26569         var pm = date.add("mo", -1);
26570         var prevStart = pm.getDaysInMonth()-startingPos;
26571
26572         var cells = this.cells.elements;
26573         var textEls = this.textNodes;
26574         days += startingPos;
26575
26576         // convert everything to numbers so it's fast
26577         var day = 86400000;
26578         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26579         var today = new Date().clearTime().getTime();
26580         var sel = date.clearTime().getTime();
26581         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26582         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26583         var ddMatch = this.disabledDatesRE;
26584         var ddText = this.disabledDatesText;
26585         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26586         var ddaysText = this.disabledDaysText;
26587         var format = this.format;
26588
26589         var setCellClass = function(cal, cell){
26590             cell.title = "";
26591             var t = d.getTime();
26592             cell.firstChild.dateValue = t;
26593             if(t == today){
26594                 cell.className += " x-date-today";
26595                 cell.title = cal.todayText;
26596             }
26597             if(t == sel){
26598                 cell.className += " x-date-selected";
26599                 setTimeout(function(){
26600                     try{cell.firstChild.focus();}catch(e){}
26601                 }, 50);
26602             }
26603             // disabling
26604             if(t < min) {
26605                 cell.className = " x-date-disabled";
26606                 cell.title = cal.minText;
26607                 return;
26608             }
26609             if(t > max) {
26610                 cell.className = " x-date-disabled";
26611                 cell.title = cal.maxText;
26612                 return;
26613             }
26614             if(ddays){
26615                 if(ddays.indexOf(d.getDay()) != -1){
26616                     cell.title = ddaysText;
26617                     cell.className = " x-date-disabled";
26618                 }
26619             }
26620             if(ddMatch && format){
26621                 var fvalue = d.dateFormat(format);
26622                 if(ddMatch.test(fvalue)){
26623                     cell.title = ddText.replace("%0", fvalue);
26624                     cell.className = " x-date-disabled";
26625                 }
26626             }
26627         };
26628
26629         var i = 0;
26630         for(; i < startingPos; i++) {
26631             textEls[i].innerHTML = (++prevStart);
26632             d.setDate(d.getDate()+1);
26633             cells[i].className = "x-date-prevday";
26634             setCellClass(this, cells[i]);
26635         }
26636         for(; i < days; i++){
26637             intDay = i - startingPos + 1;
26638             textEls[i].innerHTML = (intDay);
26639             d.setDate(d.getDate()+1);
26640             cells[i].className = "x-date-active";
26641             setCellClass(this, cells[i]);
26642         }
26643         var extraDays = 0;
26644         for(; i < 42; i++) {
26645              textEls[i].innerHTML = (++extraDays);
26646              d.setDate(d.getDate()+1);
26647              cells[i].className = "x-date-nextday";
26648              setCellClass(this, cells[i]);
26649         }
26650
26651         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26652         this.fireEvent('monthchange', this, date);
26653         
26654         if(!this.internalRender){
26655             var main = this.el.dom.firstChild;
26656             var w = main.offsetWidth;
26657             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26658             Roo.fly(main).setWidth(w);
26659             this.internalRender = true;
26660             // opera does not respect the auto grow header center column
26661             // then, after it gets a width opera refuses to recalculate
26662             // without a second pass
26663             if(Roo.isOpera && !this.secondPass){
26664                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26665                 this.secondPass = true;
26666                 this.update.defer(10, this, [date]);
26667             }
26668         }
26669         
26670         
26671     }
26672 });        /*
26673  * Based on:
26674  * Ext JS Library 1.1.1
26675  * Copyright(c) 2006-2007, Ext JS, LLC.
26676  *
26677  * Originally Released Under LGPL - original licence link has changed is not relivant.
26678  *
26679  * Fork - LGPL
26680  * <script type="text/javascript">
26681  */
26682 /**
26683  * @class Roo.TabPanel
26684  * @extends Roo.util.Observable
26685  * A lightweight tab container.
26686  * <br><br>
26687  * Usage:
26688  * <pre><code>
26689 // basic tabs 1, built from existing content
26690 var tabs = new Roo.TabPanel("tabs1");
26691 tabs.addTab("script", "View Script");
26692 tabs.addTab("markup", "View Markup");
26693 tabs.activate("script");
26694
26695 // more advanced tabs, built from javascript
26696 var jtabs = new Roo.TabPanel("jtabs");
26697 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26698
26699 // set up the UpdateManager
26700 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26701 var updater = tab2.getUpdateManager();
26702 updater.setDefaultUrl("ajax1.htm");
26703 tab2.on('activate', updater.refresh, updater, true);
26704
26705 // Use setUrl for Ajax loading
26706 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26707 tab3.setUrl("ajax2.htm", null, true);
26708
26709 // Disabled tab
26710 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26711 tab4.disable();
26712
26713 jtabs.activate("jtabs-1");
26714  * </code></pre>
26715  * @constructor
26716  * Create a new TabPanel.
26717  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26718  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26719  */
26720 Roo.TabPanel = function(container, config){
26721     /**
26722     * The container element for this TabPanel.
26723     * @type Roo.Element
26724     */
26725     this.el = Roo.get(container, true);
26726     if(config){
26727         if(typeof config == "boolean"){
26728             this.tabPosition = config ? "bottom" : "top";
26729         }else{
26730             Roo.apply(this, config);
26731         }
26732     }
26733     if(this.tabPosition == "bottom"){
26734         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26735         this.el.addClass("x-tabs-bottom");
26736     }
26737     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26738     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26739     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26740     if(Roo.isIE){
26741         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26742     }
26743     if(this.tabPosition != "bottom"){
26744         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26745          * @type Roo.Element
26746          */
26747         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26748         this.el.addClass("x-tabs-top");
26749     }
26750     this.items = [];
26751
26752     this.bodyEl.setStyle("position", "relative");
26753
26754     this.active = null;
26755     this.activateDelegate = this.activate.createDelegate(this);
26756
26757     this.addEvents({
26758         /**
26759          * @event tabchange
26760          * Fires when the active tab changes
26761          * @param {Roo.TabPanel} this
26762          * @param {Roo.TabPanelItem} activePanel The new active tab
26763          */
26764         "tabchange": true,
26765         /**
26766          * @event beforetabchange
26767          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26768          * @param {Roo.TabPanel} this
26769          * @param {Object} e Set cancel to true on this object to cancel the tab change
26770          * @param {Roo.TabPanelItem} tab The tab being changed to
26771          */
26772         "beforetabchange" : true
26773     });
26774
26775     Roo.EventManager.onWindowResize(this.onResize, this);
26776     this.cpad = this.el.getPadding("lr");
26777     this.hiddenCount = 0;
26778
26779
26780     // toolbar on the tabbar support...
26781     if (this.toolbar) {
26782         var tcfg = this.toolbar;
26783         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26784         this.toolbar = new Roo.Toolbar(tcfg);
26785         if (Roo.isSafari) {
26786             var tbl = tcfg.container.child('table', true);
26787             tbl.setAttribute('width', '100%');
26788         }
26789         
26790     }
26791    
26792
26793
26794     Roo.TabPanel.superclass.constructor.call(this);
26795 };
26796
26797 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26798     /*
26799      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26800      */
26801     tabPosition : "top",
26802     /*
26803      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26804      */
26805     currentTabWidth : 0,
26806     /*
26807      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26808      */
26809     minTabWidth : 40,
26810     /*
26811      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26812      */
26813     maxTabWidth : 250,
26814     /*
26815      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26816      */
26817     preferredTabWidth : 175,
26818     /*
26819      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26820      */
26821     resizeTabs : false,
26822     /*
26823      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26824      */
26825     monitorResize : true,
26826     /*
26827      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26828      */
26829     toolbar : false,
26830
26831     /**
26832      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26833      * @param {String} id The id of the div to use <b>or create</b>
26834      * @param {String} text The text for the tab
26835      * @param {String} content (optional) Content to put in the TabPanelItem body
26836      * @param {Boolean} closable (optional) True to create a close icon on the tab
26837      * @return {Roo.TabPanelItem} The created TabPanelItem
26838      */
26839     addTab : function(id, text, content, closable){
26840         var item = new Roo.TabPanelItem(this, id, text, closable);
26841         this.addTabItem(item);
26842         if(content){
26843             item.setContent(content);
26844         }
26845         return item;
26846     },
26847
26848     /**
26849      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26850      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26851      * @return {Roo.TabPanelItem}
26852      */
26853     getTab : function(id){
26854         return this.items[id];
26855     },
26856
26857     /**
26858      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26859      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26860      */
26861     hideTab : function(id){
26862         var t = this.items[id];
26863         if(!t.isHidden()){
26864            t.setHidden(true);
26865            this.hiddenCount++;
26866            this.autoSizeTabs();
26867         }
26868     },
26869
26870     /**
26871      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26872      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26873      */
26874     unhideTab : function(id){
26875         var t = this.items[id];
26876         if(t.isHidden()){
26877            t.setHidden(false);
26878            this.hiddenCount--;
26879            this.autoSizeTabs();
26880         }
26881     },
26882
26883     /**
26884      * Adds an existing {@link Roo.TabPanelItem}.
26885      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26886      */
26887     addTabItem : function(item){
26888         this.items[item.id] = item;
26889         this.items.push(item);
26890         if(this.resizeTabs){
26891            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26892            this.autoSizeTabs();
26893         }else{
26894             item.autoSize();
26895         }
26896     },
26897
26898     /**
26899      * Removes a {@link Roo.TabPanelItem}.
26900      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26901      */
26902     removeTab : function(id){
26903         var items = this.items;
26904         var tab = items[id];
26905         if(!tab) { return; }
26906         var index = items.indexOf(tab);
26907         if(this.active == tab && items.length > 1){
26908             var newTab = this.getNextAvailable(index);
26909             if(newTab) {
26910                 newTab.activate();
26911             }
26912         }
26913         this.stripEl.dom.removeChild(tab.pnode.dom);
26914         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26915             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26916         }
26917         items.splice(index, 1);
26918         delete this.items[tab.id];
26919         tab.fireEvent("close", tab);
26920         tab.purgeListeners();
26921         this.autoSizeTabs();
26922     },
26923
26924     getNextAvailable : function(start){
26925         var items = this.items;
26926         var index = start;
26927         // look for a next tab that will slide over to
26928         // replace the one being removed
26929         while(index < items.length){
26930             var item = items[++index];
26931             if(item && !item.isHidden()){
26932                 return item;
26933             }
26934         }
26935         // if one isn't found select the previous tab (on the left)
26936         index = start;
26937         while(index >= 0){
26938             var item = items[--index];
26939             if(item && !item.isHidden()){
26940                 return item;
26941             }
26942         }
26943         return null;
26944     },
26945
26946     /**
26947      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26948      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26949      */
26950     disableTab : function(id){
26951         var tab = this.items[id];
26952         if(tab && this.active != tab){
26953             tab.disable();
26954         }
26955     },
26956
26957     /**
26958      * Enables a {@link Roo.TabPanelItem} that is disabled.
26959      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26960      */
26961     enableTab : function(id){
26962         var tab = this.items[id];
26963         tab.enable();
26964     },
26965
26966     /**
26967      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26968      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26969      * @return {Roo.TabPanelItem} The TabPanelItem.
26970      */
26971     activate : function(id){
26972         var tab = this.items[id];
26973         if(!tab){
26974             return null;
26975         }
26976         if(tab == this.active || tab.disabled){
26977             return tab;
26978         }
26979         var e = {};
26980         this.fireEvent("beforetabchange", this, e, tab);
26981         if(e.cancel !== true && !tab.disabled){
26982             if(this.active){
26983                 this.active.hide();
26984             }
26985             this.active = this.items[id];
26986             this.active.show();
26987             this.fireEvent("tabchange", this, this.active);
26988         }
26989         return tab;
26990     },
26991
26992     /**
26993      * Gets the active {@link Roo.TabPanelItem}.
26994      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26995      */
26996     getActiveTab : function(){
26997         return this.active;
26998     },
26999
27000     /**
27001      * Updates the tab body element to fit the height of the container element
27002      * for overflow scrolling
27003      * @param {Number} targetHeight (optional) Override the starting height from the elements height
27004      */
27005     syncHeight : function(targetHeight){
27006         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
27007         var bm = this.bodyEl.getMargins();
27008         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
27009         this.bodyEl.setHeight(newHeight);
27010         return newHeight;
27011     },
27012
27013     onResize : function(){
27014         if(this.monitorResize){
27015             this.autoSizeTabs();
27016         }
27017     },
27018
27019     /**
27020      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
27021      */
27022     beginUpdate : function(){
27023         this.updating = true;
27024     },
27025
27026     /**
27027      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
27028      */
27029     endUpdate : function(){
27030         this.updating = false;
27031         this.autoSizeTabs();
27032     },
27033
27034     /**
27035      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
27036      */
27037     autoSizeTabs : function(){
27038         var count = this.items.length;
27039         var vcount = count - this.hiddenCount;
27040         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
27041         var w = Math.max(this.el.getWidth() - this.cpad, 10);
27042         var availWidth = Math.floor(w / vcount);
27043         var b = this.stripBody;
27044         if(b.getWidth() > w){
27045             var tabs = this.items;
27046             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
27047             if(availWidth < this.minTabWidth){
27048                 /*if(!this.sleft){    // incomplete scrolling code
27049                     this.createScrollButtons();
27050                 }
27051                 this.showScroll();
27052                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
27053             }
27054         }else{
27055             if(this.currentTabWidth < this.preferredTabWidth){
27056                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
27057             }
27058         }
27059     },
27060
27061     /**
27062      * Returns the number of tabs in this TabPanel.
27063      * @return {Number}
27064      */
27065      getCount : function(){
27066          return this.items.length;
27067      },
27068
27069     /**
27070      * Resizes all the tabs to the passed width
27071      * @param {Number} The new width
27072      */
27073     setTabWidth : function(width){
27074         this.currentTabWidth = width;
27075         for(var i = 0, len = this.items.length; i < len; i++) {
27076                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
27077         }
27078     },
27079
27080     /**
27081      * Destroys this TabPanel
27082      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
27083      */
27084     destroy : function(removeEl){
27085         Roo.EventManager.removeResizeListener(this.onResize, this);
27086         for(var i = 0, len = this.items.length; i < len; i++){
27087             this.items[i].purgeListeners();
27088         }
27089         if(removeEl === true){
27090             this.el.update("");
27091             this.el.remove();
27092         }
27093     }
27094 });
27095
27096 /**
27097  * @class Roo.TabPanelItem
27098  * @extends Roo.util.Observable
27099  * Represents an individual item (tab plus body) in a TabPanel.
27100  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
27101  * @param {String} id The id of this TabPanelItem
27102  * @param {String} text The text for the tab of this TabPanelItem
27103  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
27104  */
27105 Roo.TabPanelItem = function(tabPanel, id, text, closable){
27106     /**
27107      * The {@link Roo.TabPanel} this TabPanelItem belongs to
27108      * @type Roo.TabPanel
27109      */
27110     this.tabPanel = tabPanel;
27111     /**
27112      * The id for this TabPanelItem
27113      * @type String
27114      */
27115     this.id = id;
27116     /** @private */
27117     this.disabled = false;
27118     /** @private */
27119     this.text = text;
27120     /** @private */
27121     this.loaded = false;
27122     this.closable = closable;
27123
27124     /**
27125      * The body element for this TabPanelItem.
27126      * @type Roo.Element
27127      */
27128     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
27129     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
27130     this.bodyEl.setStyle("display", "block");
27131     this.bodyEl.setStyle("zoom", "1");
27132     this.hideAction();
27133
27134     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
27135     /** @private */
27136     this.el = Roo.get(els.el, true);
27137     this.inner = Roo.get(els.inner, true);
27138     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
27139     this.pnode = Roo.get(els.el.parentNode, true);
27140     this.el.on("mousedown", this.onTabMouseDown, this);
27141     this.el.on("click", this.onTabClick, this);
27142     /** @private */
27143     if(closable){
27144         var c = Roo.get(els.close, true);
27145         c.dom.title = this.closeText;
27146         c.addClassOnOver("close-over");
27147         c.on("click", this.closeClick, this);
27148      }
27149
27150     this.addEvents({
27151          /**
27152          * @event activate
27153          * Fires when this tab becomes the active tab.
27154          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27155          * @param {Roo.TabPanelItem} this
27156          */
27157         "activate": true,
27158         /**
27159          * @event beforeclose
27160          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
27161          * @param {Roo.TabPanelItem} this
27162          * @param {Object} e Set cancel to true on this object to cancel the close.
27163          */
27164         "beforeclose": true,
27165         /**
27166          * @event close
27167          * Fires when this tab is closed.
27168          * @param {Roo.TabPanelItem} this
27169          */
27170          "close": true,
27171         /**
27172          * @event deactivate
27173          * Fires when this tab is no longer the active tab.
27174          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27175          * @param {Roo.TabPanelItem} this
27176          */
27177          "deactivate" : true
27178     });
27179     this.hidden = false;
27180
27181     Roo.TabPanelItem.superclass.constructor.call(this);
27182 };
27183
27184 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27185     purgeListeners : function(){
27186        Roo.util.Observable.prototype.purgeListeners.call(this);
27187        this.el.removeAllListeners();
27188     },
27189     /**
27190      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27191      */
27192     show : function(){
27193         this.pnode.addClass("on");
27194         this.showAction();
27195         if(Roo.isOpera){
27196             this.tabPanel.stripWrap.repaint();
27197         }
27198         this.fireEvent("activate", this.tabPanel, this);
27199     },
27200
27201     /**
27202      * Returns true if this tab is the active tab.
27203      * @return {Boolean}
27204      */
27205     isActive : function(){
27206         return this.tabPanel.getActiveTab() == this;
27207     },
27208
27209     /**
27210      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27211      */
27212     hide : function(){
27213         this.pnode.removeClass("on");
27214         this.hideAction();
27215         this.fireEvent("deactivate", this.tabPanel, this);
27216     },
27217
27218     hideAction : function(){
27219         this.bodyEl.hide();
27220         this.bodyEl.setStyle("position", "absolute");
27221         this.bodyEl.setLeft("-20000px");
27222         this.bodyEl.setTop("-20000px");
27223     },
27224
27225     showAction : function(){
27226         this.bodyEl.setStyle("position", "relative");
27227         this.bodyEl.setTop("");
27228         this.bodyEl.setLeft("");
27229         this.bodyEl.show();
27230     },
27231
27232     /**
27233      * Set the tooltip for the tab.
27234      * @param {String} tooltip The tab's tooltip
27235      */
27236     setTooltip : function(text){
27237         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27238             this.textEl.dom.qtip = text;
27239             this.textEl.dom.removeAttribute('title');
27240         }else{
27241             this.textEl.dom.title = text;
27242         }
27243     },
27244
27245     onTabClick : function(e){
27246         e.preventDefault();
27247         this.tabPanel.activate(this.id);
27248     },
27249
27250     onTabMouseDown : function(e){
27251         e.preventDefault();
27252         this.tabPanel.activate(this.id);
27253     },
27254
27255     getWidth : function(){
27256         return this.inner.getWidth();
27257     },
27258
27259     setWidth : function(width){
27260         var iwidth = width - this.pnode.getPadding("lr");
27261         this.inner.setWidth(iwidth);
27262         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27263         this.pnode.setWidth(width);
27264     },
27265
27266     /**
27267      * Show or hide the tab
27268      * @param {Boolean} hidden True to hide or false to show.
27269      */
27270     setHidden : function(hidden){
27271         this.hidden = hidden;
27272         this.pnode.setStyle("display", hidden ? "none" : "");
27273     },
27274
27275     /**
27276      * Returns true if this tab is "hidden"
27277      * @return {Boolean}
27278      */
27279     isHidden : function(){
27280         return this.hidden;
27281     },
27282
27283     /**
27284      * Returns the text for this tab
27285      * @return {String}
27286      */
27287     getText : function(){
27288         return this.text;
27289     },
27290
27291     autoSize : function(){
27292         //this.el.beginMeasure();
27293         this.textEl.setWidth(1);
27294         /*
27295          *  #2804 [new] Tabs in Roojs
27296          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
27297          */
27298         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
27299         //this.el.endMeasure();
27300     },
27301
27302     /**
27303      * Sets the text for the tab (Note: this also sets the tooltip text)
27304      * @param {String} text The tab's text and tooltip
27305      */
27306     setText : function(text){
27307         this.text = text;
27308         this.textEl.update(text);
27309         this.setTooltip(text);
27310         if(!this.tabPanel.resizeTabs){
27311             this.autoSize();
27312         }
27313     },
27314     /**
27315      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27316      */
27317     activate : function(){
27318         this.tabPanel.activate(this.id);
27319     },
27320
27321     /**
27322      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27323      */
27324     disable : function(){
27325         if(this.tabPanel.active != this){
27326             this.disabled = true;
27327             this.pnode.addClass("disabled");
27328         }
27329     },
27330
27331     /**
27332      * Enables this TabPanelItem if it was previously disabled.
27333      */
27334     enable : function(){
27335         this.disabled = false;
27336         this.pnode.removeClass("disabled");
27337     },
27338
27339     /**
27340      * Sets the content for this TabPanelItem.
27341      * @param {String} content The content
27342      * @param {Boolean} loadScripts true to look for and load scripts
27343      */
27344     setContent : function(content, loadScripts){
27345         this.bodyEl.update(content, loadScripts);
27346     },
27347
27348     /**
27349      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27350      * @return {Roo.UpdateManager} The UpdateManager
27351      */
27352     getUpdateManager : function(){
27353         return this.bodyEl.getUpdateManager();
27354     },
27355
27356     /**
27357      * Set a URL to be used to load the content for this TabPanelItem.
27358      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27359      * @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)
27360      * @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)
27361      * @return {Roo.UpdateManager} The UpdateManager
27362      */
27363     setUrl : function(url, params, loadOnce){
27364         if(this.refreshDelegate){
27365             this.un('activate', this.refreshDelegate);
27366         }
27367         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27368         this.on("activate", this.refreshDelegate);
27369         return this.bodyEl.getUpdateManager();
27370     },
27371
27372     /** @private */
27373     _handleRefresh : function(url, params, loadOnce){
27374         if(!loadOnce || !this.loaded){
27375             var updater = this.bodyEl.getUpdateManager();
27376             updater.update(url, params, this._setLoaded.createDelegate(this));
27377         }
27378     },
27379
27380     /**
27381      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27382      *   Will fail silently if the setUrl method has not been called.
27383      *   This does not activate the panel, just updates its content.
27384      */
27385     refresh : function(){
27386         if(this.refreshDelegate){
27387            this.loaded = false;
27388            this.refreshDelegate();
27389         }
27390     },
27391
27392     /** @private */
27393     _setLoaded : function(){
27394         this.loaded = true;
27395     },
27396
27397     /** @private */
27398     closeClick : function(e){
27399         var o = {};
27400         e.stopEvent();
27401         this.fireEvent("beforeclose", this, o);
27402         if(o.cancel !== true){
27403             this.tabPanel.removeTab(this.id);
27404         }
27405     },
27406     /**
27407      * The text displayed in the tooltip for the close icon.
27408      * @type String
27409      */
27410     closeText : "Close this tab"
27411 });
27412
27413 /** @private */
27414 Roo.TabPanel.prototype.createStrip = function(container){
27415     var strip = document.createElement("div");
27416     strip.className = "x-tabs-wrap";
27417     container.appendChild(strip);
27418     return strip;
27419 };
27420 /** @private */
27421 Roo.TabPanel.prototype.createStripList = function(strip){
27422     // div wrapper for retard IE
27423     // returns the "tr" element.
27424     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27425         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27426         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27427     return strip.firstChild.firstChild.firstChild.firstChild;
27428 };
27429 /** @private */
27430 Roo.TabPanel.prototype.createBody = function(container){
27431     var body = document.createElement("div");
27432     Roo.id(body, "tab-body");
27433     Roo.fly(body).addClass("x-tabs-body");
27434     container.appendChild(body);
27435     return body;
27436 };
27437 /** @private */
27438 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27439     var body = Roo.getDom(id);
27440     if(!body){
27441         body = document.createElement("div");
27442         body.id = id;
27443     }
27444     Roo.fly(body).addClass("x-tabs-item-body");
27445     bodyEl.insertBefore(body, bodyEl.firstChild);
27446     return body;
27447 };
27448 /** @private */
27449 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27450     var td = document.createElement("td");
27451     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27452     //stripEl.appendChild(td);
27453     if(closable){
27454         td.className = "x-tabs-closable";
27455         if(!this.closeTpl){
27456             this.closeTpl = new Roo.Template(
27457                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27458                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27459                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27460             );
27461         }
27462         var el = this.closeTpl.overwrite(td, {"text": text});
27463         var close = el.getElementsByTagName("div")[0];
27464         var inner = el.getElementsByTagName("em")[0];
27465         return {"el": el, "close": close, "inner": inner};
27466     } else {
27467         if(!this.tabTpl){
27468             this.tabTpl = new Roo.Template(
27469                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27470                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27471             );
27472         }
27473         var el = this.tabTpl.overwrite(td, {"text": text});
27474         var inner = el.getElementsByTagName("em")[0];
27475         return {"el": el, "inner": inner};
27476     }
27477 };/*
27478  * Based on:
27479  * Ext JS Library 1.1.1
27480  * Copyright(c) 2006-2007, Ext JS, LLC.
27481  *
27482  * Originally Released Under LGPL - original licence link has changed is not relivant.
27483  *
27484  * Fork - LGPL
27485  * <script type="text/javascript">
27486  */
27487
27488 /**
27489  * @class Roo.Button
27490  * @extends Roo.util.Observable
27491  * Simple Button class
27492  * @cfg {String} text The button text
27493  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27494  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27495  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27496  * @cfg {Object} scope The scope of the handler
27497  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27498  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27499  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27500  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27501  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27502  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27503    applies if enableToggle = true)
27504  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27505  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27506   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27507  * @constructor
27508  * Create a new button
27509  * @param {Object} config The config object
27510  */
27511 Roo.Button = function(renderTo, config)
27512 {
27513     if (!config) {
27514         config = renderTo;
27515         renderTo = config.renderTo || false;
27516     }
27517     
27518     Roo.apply(this, config);
27519     this.addEvents({
27520         /**
27521              * @event click
27522              * Fires when this button is clicked
27523              * @param {Button} this
27524              * @param {EventObject} e The click event
27525              */
27526             "click" : true,
27527         /**
27528              * @event toggle
27529              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27530              * @param {Button} this
27531              * @param {Boolean} pressed
27532              */
27533             "toggle" : true,
27534         /**
27535              * @event mouseover
27536              * Fires when the mouse hovers over the button
27537              * @param {Button} this
27538              * @param {Event} e The event object
27539              */
27540         'mouseover' : true,
27541         /**
27542              * @event mouseout
27543              * Fires when the mouse exits the button
27544              * @param {Button} this
27545              * @param {Event} e The event object
27546              */
27547         'mouseout': true,
27548          /**
27549              * @event render
27550              * Fires when the button is rendered
27551              * @param {Button} this
27552              */
27553         'render': true
27554     });
27555     if(this.menu){
27556         this.menu = Roo.menu.MenuMgr.get(this.menu);
27557     }
27558     // register listeners first!!  - so render can be captured..
27559     Roo.util.Observable.call(this);
27560     if(renderTo){
27561         this.render(renderTo);
27562     }
27563     
27564   
27565 };
27566
27567 Roo.extend(Roo.Button, Roo.util.Observable, {
27568     /**
27569      * 
27570      */
27571     
27572     /**
27573      * Read-only. True if this button is hidden
27574      * @type Boolean
27575      */
27576     hidden : false,
27577     /**
27578      * Read-only. True if this button is disabled
27579      * @type Boolean
27580      */
27581     disabled : false,
27582     /**
27583      * Read-only. True if this button is pressed (only if enableToggle = true)
27584      * @type Boolean
27585      */
27586     pressed : false,
27587
27588     /**
27589      * @cfg {Number} tabIndex 
27590      * The DOM tabIndex for this button (defaults to undefined)
27591      */
27592     tabIndex : undefined,
27593
27594     /**
27595      * @cfg {Boolean} enableToggle
27596      * True to enable pressed/not pressed toggling (defaults to false)
27597      */
27598     enableToggle: false,
27599     /**
27600      * @cfg {Mixed} menu
27601      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27602      */
27603     menu : undefined,
27604     /**
27605      * @cfg {String} menuAlign
27606      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27607      */
27608     menuAlign : "tl-bl?",
27609
27610     /**
27611      * @cfg {String} iconCls
27612      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27613      */
27614     iconCls : undefined,
27615     /**
27616      * @cfg {String} type
27617      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27618      */
27619     type : 'button',
27620
27621     // private
27622     menuClassTarget: 'tr',
27623
27624     /**
27625      * @cfg {String} clickEvent
27626      * The type of event to map to the button's event handler (defaults to 'click')
27627      */
27628     clickEvent : 'click',
27629
27630     /**
27631      * @cfg {Boolean} handleMouseEvents
27632      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27633      */
27634     handleMouseEvents : true,
27635
27636     /**
27637      * @cfg {String} tooltipType
27638      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27639      */
27640     tooltipType : 'qtip',
27641
27642     /**
27643      * @cfg {String} cls
27644      * A CSS class to apply to the button's main element.
27645      */
27646     
27647     /**
27648      * @cfg {Roo.Template} template (Optional)
27649      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27650      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27651      * require code modifications if required elements (e.g. a button) aren't present.
27652      */
27653
27654     // private
27655     render : function(renderTo){
27656         var btn;
27657         if(this.hideParent){
27658             this.parentEl = Roo.get(renderTo);
27659         }
27660         if(!this.dhconfig){
27661             if(!this.template){
27662                 if(!Roo.Button.buttonTemplate){
27663                     // hideous table template
27664                     Roo.Button.buttonTemplate = new Roo.Template(
27665                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27666                         '<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>',
27667                         "</tr></tbody></table>");
27668                 }
27669                 this.template = Roo.Button.buttonTemplate;
27670             }
27671             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27672             var btnEl = btn.child("button:first");
27673             btnEl.on('focus', this.onFocus, this);
27674             btnEl.on('blur', this.onBlur, this);
27675             if(this.cls){
27676                 btn.addClass(this.cls);
27677             }
27678             if(this.icon){
27679                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27680             }
27681             if(this.iconCls){
27682                 btnEl.addClass(this.iconCls);
27683                 if(!this.cls){
27684                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27685                 }
27686             }
27687             if(this.tabIndex !== undefined){
27688                 btnEl.dom.tabIndex = this.tabIndex;
27689             }
27690             if(this.tooltip){
27691                 if(typeof this.tooltip == 'object'){
27692                     Roo.QuickTips.tips(Roo.apply({
27693                           target: btnEl.id
27694                     }, this.tooltip));
27695                 } else {
27696                     btnEl.dom[this.tooltipType] = this.tooltip;
27697                 }
27698             }
27699         }else{
27700             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27701         }
27702         this.el = btn;
27703         if(this.id){
27704             this.el.dom.id = this.el.id = this.id;
27705         }
27706         if(this.menu){
27707             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27708             this.menu.on("show", this.onMenuShow, this);
27709             this.menu.on("hide", this.onMenuHide, this);
27710         }
27711         btn.addClass("x-btn");
27712         if(Roo.isIE && !Roo.isIE7){
27713             this.autoWidth.defer(1, this);
27714         }else{
27715             this.autoWidth();
27716         }
27717         if(this.handleMouseEvents){
27718             btn.on("mouseover", this.onMouseOver, this);
27719             btn.on("mouseout", this.onMouseOut, this);
27720             btn.on("mousedown", this.onMouseDown, this);
27721         }
27722         btn.on(this.clickEvent, this.onClick, this);
27723         //btn.on("mouseup", this.onMouseUp, this);
27724         if(this.hidden){
27725             this.hide();
27726         }
27727         if(this.disabled){
27728             this.disable();
27729         }
27730         Roo.ButtonToggleMgr.register(this);
27731         if(this.pressed){
27732             this.el.addClass("x-btn-pressed");
27733         }
27734         if(this.repeat){
27735             var repeater = new Roo.util.ClickRepeater(btn,
27736                 typeof this.repeat == "object" ? this.repeat : {}
27737             );
27738             repeater.on("click", this.onClick,  this);
27739         }
27740         
27741         this.fireEvent('render', this);
27742         
27743     },
27744     /**
27745      * Returns the button's underlying element
27746      * @return {Roo.Element} The element
27747      */
27748     getEl : function(){
27749         return this.el;  
27750     },
27751     
27752     /**
27753      * Destroys this Button and removes any listeners.
27754      */
27755     destroy : function(){
27756         Roo.ButtonToggleMgr.unregister(this);
27757         this.el.removeAllListeners();
27758         this.purgeListeners();
27759         this.el.remove();
27760     },
27761
27762     // private
27763     autoWidth : function(){
27764         if(this.el){
27765             this.el.setWidth("auto");
27766             if(Roo.isIE7 && Roo.isStrict){
27767                 var ib = this.el.child('button');
27768                 if(ib && ib.getWidth() > 20){
27769                     ib.clip();
27770                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27771                 }
27772             }
27773             if(this.minWidth){
27774                 if(this.hidden){
27775                     this.el.beginMeasure();
27776                 }
27777                 if(this.el.getWidth() < this.minWidth){
27778                     this.el.setWidth(this.minWidth);
27779                 }
27780                 if(this.hidden){
27781                     this.el.endMeasure();
27782                 }
27783             }
27784         }
27785     },
27786
27787     /**
27788      * Assigns this button's click handler
27789      * @param {Function} handler The function to call when the button is clicked
27790      * @param {Object} scope (optional) Scope for the function passed in
27791      */
27792     setHandler : function(handler, scope){
27793         this.handler = handler;
27794         this.scope = scope;  
27795     },
27796     
27797     /**
27798      * Sets this button's text
27799      * @param {String} text The button text
27800      */
27801     setText : function(text){
27802         this.text = text;
27803         if(this.el){
27804             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27805         }
27806         this.autoWidth();
27807     },
27808     
27809     /**
27810      * Gets the text for this button
27811      * @return {String} The button text
27812      */
27813     getText : function(){
27814         return this.text;  
27815     },
27816     
27817     /**
27818      * Show this button
27819      */
27820     show: function(){
27821         this.hidden = false;
27822         if(this.el){
27823             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27824         }
27825     },
27826     
27827     /**
27828      * Hide this button
27829      */
27830     hide: function(){
27831         this.hidden = true;
27832         if(this.el){
27833             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27834         }
27835     },
27836     
27837     /**
27838      * Convenience function for boolean show/hide
27839      * @param {Boolean} visible True to show, false to hide
27840      */
27841     setVisible: function(visible){
27842         if(visible) {
27843             this.show();
27844         }else{
27845             this.hide();
27846         }
27847     },
27848     
27849     /**
27850      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27851      * @param {Boolean} state (optional) Force a particular state
27852      */
27853     toggle : function(state){
27854         state = state === undefined ? !this.pressed : state;
27855         if(state != this.pressed){
27856             if(state){
27857                 this.el.addClass("x-btn-pressed");
27858                 this.pressed = true;
27859                 this.fireEvent("toggle", this, true);
27860             }else{
27861                 this.el.removeClass("x-btn-pressed");
27862                 this.pressed = false;
27863                 this.fireEvent("toggle", this, false);
27864             }
27865             if(this.toggleHandler){
27866                 this.toggleHandler.call(this.scope || this, this, state);
27867             }
27868         }
27869     },
27870     
27871     /**
27872      * Focus the button
27873      */
27874     focus : function(){
27875         this.el.child('button:first').focus();
27876     },
27877     
27878     /**
27879      * Disable this button
27880      */
27881     disable : function(){
27882         if(this.el){
27883             this.el.addClass("x-btn-disabled");
27884         }
27885         this.disabled = true;
27886     },
27887     
27888     /**
27889      * Enable this button
27890      */
27891     enable : function(){
27892         if(this.el){
27893             this.el.removeClass("x-btn-disabled");
27894         }
27895         this.disabled = false;
27896     },
27897
27898     /**
27899      * Convenience function for boolean enable/disable
27900      * @param {Boolean} enabled True to enable, false to disable
27901      */
27902     setDisabled : function(v){
27903         this[v !== true ? "enable" : "disable"]();
27904     },
27905
27906     // private
27907     onClick : function(e)
27908     {
27909         if(e){
27910             e.preventDefault();
27911         }
27912         if(e.button != 0){
27913             return;
27914         }
27915         if(!this.disabled){
27916             if(this.enableToggle){
27917                 this.toggle();
27918             }
27919             if(this.menu && !this.menu.isVisible()){
27920                 this.menu.show(this.el, this.menuAlign);
27921             }
27922             this.fireEvent("click", this, e);
27923             if(this.handler){
27924                 this.el.removeClass("x-btn-over");
27925                 this.handler.call(this.scope || this, this, e);
27926             }
27927         }
27928     },
27929     // private
27930     onMouseOver : function(e){
27931         if(!this.disabled){
27932             this.el.addClass("x-btn-over");
27933             this.fireEvent('mouseover', this, e);
27934         }
27935     },
27936     // private
27937     onMouseOut : function(e){
27938         if(!e.within(this.el,  true)){
27939             this.el.removeClass("x-btn-over");
27940             this.fireEvent('mouseout', this, e);
27941         }
27942     },
27943     // private
27944     onFocus : function(e){
27945         if(!this.disabled){
27946             this.el.addClass("x-btn-focus");
27947         }
27948     },
27949     // private
27950     onBlur : function(e){
27951         this.el.removeClass("x-btn-focus");
27952     },
27953     // private
27954     onMouseDown : function(e){
27955         if(!this.disabled && e.button == 0){
27956             this.el.addClass("x-btn-click");
27957             Roo.get(document).on('mouseup', this.onMouseUp, this);
27958         }
27959     },
27960     // private
27961     onMouseUp : function(e){
27962         if(e.button == 0){
27963             this.el.removeClass("x-btn-click");
27964             Roo.get(document).un('mouseup', this.onMouseUp, this);
27965         }
27966     },
27967     // private
27968     onMenuShow : function(e){
27969         this.el.addClass("x-btn-menu-active");
27970     },
27971     // private
27972     onMenuHide : function(e){
27973         this.el.removeClass("x-btn-menu-active");
27974     }   
27975 });
27976
27977 // Private utility class used by Button
27978 Roo.ButtonToggleMgr = function(){
27979    var groups = {};
27980    
27981    function toggleGroup(btn, state){
27982        if(state){
27983            var g = groups[btn.toggleGroup];
27984            for(var i = 0, l = g.length; i < l; i++){
27985                if(g[i] != btn){
27986                    g[i].toggle(false);
27987                }
27988            }
27989        }
27990    }
27991    
27992    return {
27993        register : function(btn){
27994            if(!btn.toggleGroup){
27995                return;
27996            }
27997            var g = groups[btn.toggleGroup];
27998            if(!g){
27999                g = groups[btn.toggleGroup] = [];
28000            }
28001            g.push(btn);
28002            btn.on("toggle", toggleGroup);
28003        },
28004        
28005        unregister : function(btn){
28006            if(!btn.toggleGroup){
28007                return;
28008            }
28009            var g = groups[btn.toggleGroup];
28010            if(g){
28011                g.remove(btn);
28012                btn.un("toggle", toggleGroup);
28013            }
28014        }
28015    };
28016 }();/*
28017  * Based on:
28018  * Ext JS Library 1.1.1
28019  * Copyright(c) 2006-2007, Ext JS, LLC.
28020  *
28021  * Originally Released Under LGPL - original licence link has changed is not relivant.
28022  *
28023  * Fork - LGPL
28024  * <script type="text/javascript">
28025  */
28026  
28027 /**
28028  * @class Roo.SplitButton
28029  * @extends Roo.Button
28030  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
28031  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
28032  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
28033  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
28034  * @cfg {String} arrowTooltip The title attribute of the arrow
28035  * @constructor
28036  * Create a new menu button
28037  * @param {String/HTMLElement/Element} renderTo The element to append the button to
28038  * @param {Object} config The config object
28039  */
28040 Roo.SplitButton = function(renderTo, config){
28041     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
28042     /**
28043      * @event arrowclick
28044      * Fires when this button's arrow is clicked
28045      * @param {SplitButton} this
28046      * @param {EventObject} e The click event
28047      */
28048     this.addEvents({"arrowclick":true});
28049 };
28050
28051 Roo.extend(Roo.SplitButton, Roo.Button, {
28052     render : function(renderTo){
28053         // this is one sweet looking template!
28054         var tpl = new Roo.Template(
28055             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
28056             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
28057             '<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>',
28058             "</tbody></table></td><td>",
28059             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
28060             '<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>',
28061             "</tbody></table></td></tr></table>"
28062         );
28063         var btn = tpl.append(renderTo, [this.text, this.type], true);
28064         var btnEl = btn.child("button");
28065         if(this.cls){
28066             btn.addClass(this.cls);
28067         }
28068         if(this.icon){
28069             btnEl.setStyle('background-image', 'url(' +this.icon +')');
28070         }
28071         if(this.iconCls){
28072             btnEl.addClass(this.iconCls);
28073             if(!this.cls){
28074                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
28075             }
28076         }
28077         this.el = btn;
28078         if(this.handleMouseEvents){
28079             btn.on("mouseover", this.onMouseOver, this);
28080             btn.on("mouseout", this.onMouseOut, this);
28081             btn.on("mousedown", this.onMouseDown, this);
28082             btn.on("mouseup", this.onMouseUp, this);
28083         }
28084         btn.on(this.clickEvent, this.onClick, this);
28085         if(this.tooltip){
28086             if(typeof this.tooltip == 'object'){
28087                 Roo.QuickTips.tips(Roo.apply({
28088                       target: btnEl.id
28089                 }, this.tooltip));
28090             } else {
28091                 btnEl.dom[this.tooltipType] = this.tooltip;
28092             }
28093         }
28094         if(this.arrowTooltip){
28095             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
28096         }
28097         if(this.hidden){
28098             this.hide();
28099         }
28100         if(this.disabled){
28101             this.disable();
28102         }
28103         if(this.pressed){
28104             this.el.addClass("x-btn-pressed");
28105         }
28106         if(Roo.isIE && !Roo.isIE7){
28107             this.autoWidth.defer(1, this);
28108         }else{
28109             this.autoWidth();
28110         }
28111         if(this.menu){
28112             this.menu.on("show", this.onMenuShow, this);
28113             this.menu.on("hide", this.onMenuHide, this);
28114         }
28115         this.fireEvent('render', this);
28116     },
28117
28118     // private
28119     autoWidth : function(){
28120         if(this.el){
28121             var tbl = this.el.child("table:first");
28122             var tbl2 = this.el.child("table:last");
28123             this.el.setWidth("auto");
28124             tbl.setWidth("auto");
28125             if(Roo.isIE7 && Roo.isStrict){
28126                 var ib = this.el.child('button:first');
28127                 if(ib && ib.getWidth() > 20){
28128                     ib.clip();
28129                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
28130                 }
28131             }
28132             if(this.minWidth){
28133                 if(this.hidden){
28134                     this.el.beginMeasure();
28135                 }
28136                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
28137                     tbl.setWidth(this.minWidth-tbl2.getWidth());
28138                 }
28139                 if(this.hidden){
28140                     this.el.endMeasure();
28141                 }
28142             }
28143             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
28144         } 
28145     },
28146     /**
28147      * Sets this button's click handler
28148      * @param {Function} handler The function to call when the button is clicked
28149      * @param {Object} scope (optional) Scope for the function passed above
28150      */
28151     setHandler : function(handler, scope){
28152         this.handler = handler;
28153         this.scope = scope;  
28154     },
28155     
28156     /**
28157      * Sets this button's arrow click handler
28158      * @param {Function} handler The function to call when the arrow is clicked
28159      * @param {Object} scope (optional) Scope for the function passed above
28160      */
28161     setArrowHandler : function(handler, scope){
28162         this.arrowHandler = handler;
28163         this.scope = scope;  
28164     },
28165     
28166     /**
28167      * Focus the button
28168      */
28169     focus : function(){
28170         if(this.el){
28171             this.el.child("button:first").focus();
28172         }
28173     },
28174
28175     // private
28176     onClick : function(e){
28177         e.preventDefault();
28178         if(!this.disabled){
28179             if(e.getTarget(".x-btn-menu-arrow-wrap")){
28180                 if(this.menu && !this.menu.isVisible()){
28181                     this.menu.show(this.el, this.menuAlign);
28182                 }
28183                 this.fireEvent("arrowclick", this, e);
28184                 if(this.arrowHandler){
28185                     this.arrowHandler.call(this.scope || this, this, e);
28186                 }
28187             }else{
28188                 this.fireEvent("click", this, e);
28189                 if(this.handler){
28190                     this.handler.call(this.scope || this, this, e);
28191                 }
28192             }
28193         }
28194     },
28195     // private
28196     onMouseDown : function(e){
28197         if(!this.disabled){
28198             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28199         }
28200     },
28201     // private
28202     onMouseUp : function(e){
28203         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28204     }   
28205 });
28206
28207
28208 // backwards compat
28209 Roo.MenuButton = Roo.SplitButton;/*
28210  * Based on:
28211  * Ext JS Library 1.1.1
28212  * Copyright(c) 2006-2007, Ext JS, LLC.
28213  *
28214  * Originally Released Under LGPL - original licence link has changed is not relivant.
28215  *
28216  * Fork - LGPL
28217  * <script type="text/javascript">
28218  */
28219
28220 /**
28221  * @class Roo.Toolbar
28222  * Basic Toolbar class.
28223  * @constructor
28224  * Creates a new Toolbar
28225  * @param {Object} container The config object
28226  */ 
28227 Roo.Toolbar = function(container, buttons, config)
28228 {
28229     /// old consturctor format still supported..
28230     if(container instanceof Array){ // omit the container for later rendering
28231         buttons = container;
28232         config = buttons;
28233         container = null;
28234     }
28235     if (typeof(container) == 'object' && container.xtype) {
28236         config = container;
28237         container = config.container;
28238         buttons = config.buttons || []; // not really - use items!!
28239     }
28240     var xitems = [];
28241     if (config && config.items) {
28242         xitems = config.items;
28243         delete config.items;
28244     }
28245     Roo.apply(this, config);
28246     this.buttons = buttons;
28247     
28248     if(container){
28249         this.render(container);
28250     }
28251     this.xitems = xitems;
28252     Roo.each(xitems, function(b) {
28253         this.add(b);
28254     }, this);
28255     
28256 };
28257
28258 Roo.Toolbar.prototype = {
28259     /**
28260      * @cfg {Array} items
28261      * array of button configs or elements to add (will be converted to a MixedCollection)
28262      */
28263     
28264     /**
28265      * @cfg {String/HTMLElement/Element} container
28266      * The id or element that will contain the toolbar
28267      */
28268     // private
28269     render : function(ct){
28270         this.el = Roo.get(ct);
28271         if(this.cls){
28272             this.el.addClass(this.cls);
28273         }
28274         // using a table allows for vertical alignment
28275         // 100% width is needed by Safari...
28276         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28277         this.tr = this.el.child("tr", true);
28278         var autoId = 0;
28279         this.items = new Roo.util.MixedCollection(false, function(o){
28280             return o.id || ("item" + (++autoId));
28281         });
28282         if(this.buttons){
28283             this.add.apply(this, this.buttons);
28284             delete this.buttons;
28285         }
28286     },
28287
28288     /**
28289      * Adds element(s) to the toolbar -- this function takes a variable number of 
28290      * arguments of mixed type and adds them to the toolbar.
28291      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28292      * <ul>
28293      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28294      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28295      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28296      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28297      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28298      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28299      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28300      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28301      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28302      * </ul>
28303      * @param {Mixed} arg2
28304      * @param {Mixed} etc.
28305      */
28306     add : function(){
28307         var a = arguments, l = a.length;
28308         for(var i = 0; i < l; i++){
28309             this._add(a[i]);
28310         }
28311     },
28312     // private..
28313     _add : function(el) {
28314         
28315         if (el.xtype) {
28316             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28317         }
28318         
28319         if (el.applyTo){ // some kind of form field
28320             return this.addField(el);
28321         } 
28322         if (el.render){ // some kind of Toolbar.Item
28323             return this.addItem(el);
28324         }
28325         if (typeof el == "string"){ // string
28326             if(el == "separator" || el == "-"){
28327                 return this.addSeparator();
28328             }
28329             if (el == " "){
28330                 return this.addSpacer();
28331             }
28332             if(el == "->"){
28333                 return this.addFill();
28334             }
28335             return this.addText(el);
28336             
28337         }
28338         if(el.tagName){ // element
28339             return this.addElement(el);
28340         }
28341         if(typeof el == "object"){ // must be button config?
28342             return this.addButton(el);
28343         }
28344         // and now what?!?!
28345         return false;
28346         
28347     },
28348     
28349     /**
28350      * Add an Xtype element
28351      * @param {Object} xtype Xtype Object
28352      * @return {Object} created Object
28353      */
28354     addxtype : function(e){
28355         return this.add(e);  
28356     },
28357     
28358     /**
28359      * Returns the Element for this toolbar.
28360      * @return {Roo.Element}
28361      */
28362     getEl : function(){
28363         return this.el;  
28364     },
28365     
28366     /**
28367      * Adds a separator
28368      * @return {Roo.Toolbar.Item} The separator item
28369      */
28370     addSeparator : function(){
28371         return this.addItem(new Roo.Toolbar.Separator());
28372     },
28373
28374     /**
28375      * Adds a spacer element
28376      * @return {Roo.Toolbar.Spacer} The spacer item
28377      */
28378     addSpacer : function(){
28379         return this.addItem(new Roo.Toolbar.Spacer());
28380     },
28381
28382     /**
28383      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28384      * @return {Roo.Toolbar.Fill} The fill item
28385      */
28386     addFill : function(){
28387         return this.addItem(new Roo.Toolbar.Fill());
28388     },
28389
28390     /**
28391      * Adds any standard HTML element to the toolbar
28392      * @param {String/HTMLElement/Element} el The element or id of the element to add
28393      * @return {Roo.Toolbar.Item} The element's item
28394      */
28395     addElement : function(el){
28396         return this.addItem(new Roo.Toolbar.Item(el));
28397     },
28398     /**
28399      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28400      * @type Roo.util.MixedCollection  
28401      */
28402     items : false,
28403      
28404     /**
28405      * Adds any Toolbar.Item or subclass
28406      * @param {Roo.Toolbar.Item} item
28407      * @return {Roo.Toolbar.Item} The item
28408      */
28409     addItem : function(item){
28410         var td = this.nextBlock();
28411         item.render(td);
28412         this.items.add(item);
28413         return item;
28414     },
28415     
28416     /**
28417      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28418      * @param {Object/Array} config A button config or array of configs
28419      * @return {Roo.Toolbar.Button/Array}
28420      */
28421     addButton : function(config){
28422         if(config instanceof Array){
28423             var buttons = [];
28424             for(var i = 0, len = config.length; i < len; i++) {
28425                 buttons.push(this.addButton(config[i]));
28426             }
28427             return buttons;
28428         }
28429         var b = config;
28430         if(!(config instanceof Roo.Toolbar.Button)){
28431             b = config.split ?
28432                 new Roo.Toolbar.SplitButton(config) :
28433                 new Roo.Toolbar.Button(config);
28434         }
28435         var td = this.nextBlock();
28436         b.render(td);
28437         this.items.add(b);
28438         return b;
28439     },
28440     
28441     /**
28442      * Adds text to the toolbar
28443      * @param {String} text The text to add
28444      * @return {Roo.Toolbar.Item} The element's item
28445      */
28446     addText : function(text){
28447         return this.addItem(new Roo.Toolbar.TextItem(text));
28448     },
28449     
28450     /**
28451      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28452      * @param {Number} index The index where the item is to be inserted
28453      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28454      * @return {Roo.Toolbar.Button/Item}
28455      */
28456     insertButton : function(index, item){
28457         if(item instanceof Array){
28458             var buttons = [];
28459             for(var i = 0, len = item.length; i < len; i++) {
28460                buttons.push(this.insertButton(index + i, item[i]));
28461             }
28462             return buttons;
28463         }
28464         if (!(item instanceof Roo.Toolbar.Button)){
28465            item = new Roo.Toolbar.Button(item);
28466         }
28467         var td = document.createElement("td");
28468         this.tr.insertBefore(td, this.tr.childNodes[index]);
28469         item.render(td);
28470         this.items.insert(index, item);
28471         return item;
28472     },
28473     
28474     /**
28475      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28476      * @param {Object} config
28477      * @return {Roo.Toolbar.Item} The element's item
28478      */
28479     addDom : function(config, returnEl){
28480         var td = this.nextBlock();
28481         Roo.DomHelper.overwrite(td, config);
28482         var ti = new Roo.Toolbar.Item(td.firstChild);
28483         ti.render(td);
28484         this.items.add(ti);
28485         return ti;
28486     },
28487
28488     /**
28489      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28490      * @type Roo.util.MixedCollection  
28491      */
28492     fields : false,
28493     
28494     /**
28495      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28496      * Note: the field should not have been rendered yet. For a field that has already been
28497      * rendered, use {@link #addElement}.
28498      * @param {Roo.form.Field} field
28499      * @return {Roo.ToolbarItem}
28500      */
28501      
28502       
28503     addField : function(field) {
28504         if (!this.fields) {
28505             var autoId = 0;
28506             this.fields = new Roo.util.MixedCollection(false, function(o){
28507                 return o.id || ("item" + (++autoId));
28508             });
28509
28510         }
28511         
28512         var td = this.nextBlock();
28513         field.render(td);
28514         var ti = new Roo.Toolbar.Item(td.firstChild);
28515         ti.render(td);
28516         this.items.add(ti);
28517         this.fields.add(field);
28518         return ti;
28519     },
28520     /**
28521      * Hide the toolbar
28522      * @method hide
28523      */
28524      
28525       
28526     hide : function()
28527     {
28528         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28529         this.el.child('div').hide();
28530     },
28531     /**
28532      * Show the toolbar
28533      * @method show
28534      */
28535     show : function()
28536     {
28537         this.el.child('div').show();
28538     },
28539       
28540     // private
28541     nextBlock : function(){
28542         var td = document.createElement("td");
28543         this.tr.appendChild(td);
28544         return td;
28545     },
28546
28547     // private
28548     destroy : function(){
28549         if(this.items){ // rendered?
28550             Roo.destroy.apply(Roo, this.items.items);
28551         }
28552         if(this.fields){ // rendered?
28553             Roo.destroy.apply(Roo, this.fields.items);
28554         }
28555         Roo.Element.uncache(this.el, this.tr);
28556     }
28557 };
28558
28559 /**
28560  * @class Roo.Toolbar.Item
28561  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28562  * @constructor
28563  * Creates a new Item
28564  * @param {HTMLElement} el 
28565  */
28566 Roo.Toolbar.Item = function(el){
28567     var cfg = {};
28568     if (typeof (el.xtype) != 'undefined') {
28569         cfg = el;
28570         el = cfg.el;
28571     }
28572     
28573     this.el = Roo.getDom(el);
28574     this.id = Roo.id(this.el);
28575     this.hidden = false;
28576     
28577     this.addEvents({
28578          /**
28579              * @event render
28580              * Fires when the button is rendered
28581              * @param {Button} this
28582              */
28583         'render': true
28584     });
28585     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
28586 };
28587 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
28588 //Roo.Toolbar.Item.prototype = {
28589     
28590     /**
28591      * Get this item's HTML Element
28592      * @return {HTMLElement}
28593      */
28594     getEl : function(){
28595        return this.el;  
28596     },
28597
28598     // private
28599     render : function(td){
28600         
28601          this.td = td;
28602         td.appendChild(this.el);
28603         
28604         this.fireEvent('render', this);
28605     },
28606     
28607     /**
28608      * Removes and destroys this item.
28609      */
28610     destroy : function(){
28611         this.td.parentNode.removeChild(this.td);
28612     },
28613     
28614     /**
28615      * Shows this item.
28616      */
28617     show: function(){
28618         this.hidden = false;
28619         this.td.style.display = "";
28620     },
28621     
28622     /**
28623      * Hides this item.
28624      */
28625     hide: function(){
28626         this.hidden = true;
28627         this.td.style.display = "none";
28628     },
28629     
28630     /**
28631      * Convenience function for boolean show/hide.
28632      * @param {Boolean} visible true to show/false to hide
28633      */
28634     setVisible: function(visible){
28635         if(visible) {
28636             this.show();
28637         }else{
28638             this.hide();
28639         }
28640     },
28641     
28642     /**
28643      * Try to focus this item.
28644      */
28645     focus : function(){
28646         Roo.fly(this.el).focus();
28647     },
28648     
28649     /**
28650      * Disables this item.
28651      */
28652     disable : function(){
28653         Roo.fly(this.td).addClass("x-item-disabled");
28654         this.disabled = true;
28655         this.el.disabled = true;
28656     },
28657     
28658     /**
28659      * Enables this item.
28660      */
28661     enable : function(){
28662         Roo.fly(this.td).removeClass("x-item-disabled");
28663         this.disabled = false;
28664         this.el.disabled = false;
28665     }
28666 });
28667
28668
28669 /**
28670  * @class Roo.Toolbar.Separator
28671  * @extends Roo.Toolbar.Item
28672  * A simple toolbar separator class
28673  * @constructor
28674  * Creates a new Separator
28675  */
28676 Roo.Toolbar.Separator = function(cfg){
28677     
28678     var s = document.createElement("span");
28679     s.className = "ytb-sep";
28680     if (cfg) {
28681         cfg.el = s;
28682     }
28683     
28684     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
28685 };
28686 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28687     enable:Roo.emptyFn,
28688     disable:Roo.emptyFn,
28689     focus:Roo.emptyFn
28690 });
28691
28692 /**
28693  * @class Roo.Toolbar.Spacer
28694  * @extends Roo.Toolbar.Item
28695  * A simple element that adds extra horizontal space to a toolbar.
28696  * @constructor
28697  * Creates a new Spacer
28698  */
28699 Roo.Toolbar.Spacer = function(cfg){
28700     var s = document.createElement("div");
28701     s.className = "ytb-spacer";
28702     if (cfg) {
28703         cfg.el = s;
28704     }
28705     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
28706 };
28707 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28708     enable:Roo.emptyFn,
28709     disable:Roo.emptyFn,
28710     focus:Roo.emptyFn
28711 });
28712
28713 /**
28714  * @class Roo.Toolbar.Fill
28715  * @extends Roo.Toolbar.Spacer
28716  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28717  * @constructor
28718  * Creates a new Spacer
28719  */
28720 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28721     // private
28722     render : function(td){
28723         td.style.width = '100%';
28724         Roo.Toolbar.Fill.superclass.render.call(this, td);
28725     }
28726 });
28727
28728 /**
28729  * @class Roo.Toolbar.TextItem
28730  * @extends Roo.Toolbar.Item
28731  * A simple class that renders text directly into a toolbar.
28732  * @constructor
28733  * Creates a new TextItem
28734  * @param {String} text
28735  */
28736 Roo.Toolbar.TextItem = function(cfg){
28737     var  text = cfg || "";
28738     if (typeof(cfg) == 'object') {
28739         text = cfg.text || "";
28740     }  else {
28741         cfg = null;
28742     }
28743     var s = document.createElement("span");
28744     s.className = "ytb-text";
28745     s.innerHTML = text;
28746     if (cfg) {
28747         cfg.el  = s;
28748     }
28749     
28750     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
28751 };
28752 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28753     
28754      
28755     enable:Roo.emptyFn,
28756     disable:Roo.emptyFn,
28757     focus:Roo.emptyFn
28758 });
28759
28760 /**
28761  * @class Roo.Toolbar.Button
28762  * @extends Roo.Button
28763  * A button that renders into a toolbar.
28764  * @constructor
28765  * Creates a new Button
28766  * @param {Object} config A standard {@link Roo.Button} config object
28767  */
28768 Roo.Toolbar.Button = function(config){
28769     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28770 };
28771 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28772     render : function(td){
28773         this.td = td;
28774         Roo.Toolbar.Button.superclass.render.call(this, td);
28775     },
28776     
28777     /**
28778      * Removes and destroys this button
28779      */
28780     destroy : function(){
28781         Roo.Toolbar.Button.superclass.destroy.call(this);
28782         this.td.parentNode.removeChild(this.td);
28783     },
28784     
28785     /**
28786      * Shows this button
28787      */
28788     show: function(){
28789         this.hidden = false;
28790         this.td.style.display = "";
28791     },
28792     
28793     /**
28794      * Hides this button
28795      */
28796     hide: function(){
28797         this.hidden = true;
28798         this.td.style.display = "none";
28799     },
28800
28801     /**
28802      * Disables this item
28803      */
28804     disable : function(){
28805         Roo.fly(this.td).addClass("x-item-disabled");
28806         this.disabled = true;
28807     },
28808
28809     /**
28810      * Enables this item
28811      */
28812     enable : function(){
28813         Roo.fly(this.td).removeClass("x-item-disabled");
28814         this.disabled = false;
28815     }
28816 });
28817 // backwards compat
28818 Roo.ToolbarButton = Roo.Toolbar.Button;
28819
28820 /**
28821  * @class Roo.Toolbar.SplitButton
28822  * @extends Roo.SplitButton
28823  * A menu button that renders into a toolbar.
28824  * @constructor
28825  * Creates a new SplitButton
28826  * @param {Object} config A standard {@link Roo.SplitButton} config object
28827  */
28828 Roo.Toolbar.SplitButton = function(config){
28829     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28830 };
28831 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28832     render : function(td){
28833         this.td = td;
28834         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28835     },
28836     
28837     /**
28838      * Removes and destroys this button
28839      */
28840     destroy : function(){
28841         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28842         this.td.parentNode.removeChild(this.td);
28843     },
28844     
28845     /**
28846      * Shows this button
28847      */
28848     show: function(){
28849         this.hidden = false;
28850         this.td.style.display = "";
28851     },
28852     
28853     /**
28854      * Hides this button
28855      */
28856     hide: function(){
28857         this.hidden = true;
28858         this.td.style.display = "none";
28859     }
28860 });
28861
28862 // backwards compat
28863 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28864  * Based on:
28865  * Ext JS Library 1.1.1
28866  * Copyright(c) 2006-2007, Ext JS, LLC.
28867  *
28868  * Originally Released Under LGPL - original licence link has changed is not relivant.
28869  *
28870  * Fork - LGPL
28871  * <script type="text/javascript">
28872  */
28873  
28874 /**
28875  * @class Roo.PagingToolbar
28876  * @extends Roo.Toolbar
28877  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28878  * @constructor
28879  * Create a new PagingToolbar
28880  * @param {Object} config The config object
28881  */
28882 Roo.PagingToolbar = function(el, ds, config)
28883 {
28884     // old args format still supported... - xtype is prefered..
28885     if (typeof(el) == 'object' && el.xtype) {
28886         // created from xtype...
28887         config = el;
28888         ds = el.dataSource;
28889         el = config.container;
28890     }
28891     var items = [];
28892     if (config.items) {
28893         items = config.items;
28894         config.items = [];
28895     }
28896     
28897     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28898     this.ds = ds;
28899     this.cursor = 0;
28900     this.renderButtons(this.el);
28901     this.bind(ds);
28902     
28903     // supprot items array.
28904    
28905     Roo.each(items, function(e) {
28906         this.add(Roo.factory(e));
28907     },this);
28908     
28909 };
28910
28911 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28912     /**
28913      * @cfg {Roo.data.Store} dataSource
28914      * The underlying data store providing the paged data
28915      */
28916     /**
28917      * @cfg {String/HTMLElement/Element} container
28918      * container The id or element that will contain the toolbar
28919      */
28920     /**
28921      * @cfg {Boolean} displayInfo
28922      * True to display the displayMsg (defaults to false)
28923      */
28924     /**
28925      * @cfg {Number} pageSize
28926      * The number of records to display per page (defaults to 20)
28927      */
28928     pageSize: 20,
28929     /**
28930      * @cfg {String} displayMsg
28931      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28932      */
28933     displayMsg : 'Displaying {0} - {1} of {2}',
28934     /**
28935      * @cfg {String} emptyMsg
28936      * The message to display when no records are found (defaults to "No data to display")
28937      */
28938     emptyMsg : 'No data to display',
28939     /**
28940      * Customizable piece of the default paging text (defaults to "Page")
28941      * @type String
28942      */
28943     beforePageText : "Page",
28944     /**
28945      * Customizable piece of the default paging text (defaults to "of %0")
28946      * @type String
28947      */
28948     afterPageText : "of {0}",
28949     /**
28950      * Customizable piece of the default paging text (defaults to "First Page")
28951      * @type String
28952      */
28953     firstText : "First Page",
28954     /**
28955      * Customizable piece of the default paging text (defaults to "Previous Page")
28956      * @type String
28957      */
28958     prevText : "Previous Page",
28959     /**
28960      * Customizable piece of the default paging text (defaults to "Next Page")
28961      * @type String
28962      */
28963     nextText : "Next Page",
28964     /**
28965      * Customizable piece of the default paging text (defaults to "Last Page")
28966      * @type String
28967      */
28968     lastText : "Last Page",
28969     /**
28970      * Customizable piece of the default paging text (defaults to "Refresh")
28971      * @type String
28972      */
28973     refreshText : "Refresh",
28974
28975     // private
28976     renderButtons : function(el){
28977         Roo.PagingToolbar.superclass.render.call(this, el);
28978         this.first = this.addButton({
28979             tooltip: this.firstText,
28980             cls: "x-btn-icon x-grid-page-first",
28981             disabled: true,
28982             handler: this.onClick.createDelegate(this, ["first"])
28983         });
28984         this.prev = this.addButton({
28985             tooltip: this.prevText,
28986             cls: "x-btn-icon x-grid-page-prev",
28987             disabled: true,
28988             handler: this.onClick.createDelegate(this, ["prev"])
28989         });
28990         //this.addSeparator();
28991         this.add(this.beforePageText);
28992         this.field = Roo.get(this.addDom({
28993            tag: "input",
28994            type: "text",
28995            size: "3",
28996            value: "1",
28997            cls: "x-grid-page-number"
28998         }).el);
28999         this.field.on("keydown", this.onPagingKeydown, this);
29000         this.field.on("focus", function(){this.dom.select();});
29001         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
29002         this.field.setHeight(18);
29003         //this.addSeparator();
29004         this.next = this.addButton({
29005             tooltip: this.nextText,
29006             cls: "x-btn-icon x-grid-page-next",
29007             disabled: true,
29008             handler: this.onClick.createDelegate(this, ["next"])
29009         });
29010         this.last = this.addButton({
29011             tooltip: this.lastText,
29012             cls: "x-btn-icon x-grid-page-last",
29013             disabled: true,
29014             handler: this.onClick.createDelegate(this, ["last"])
29015         });
29016         //this.addSeparator();
29017         this.loading = this.addButton({
29018             tooltip: this.refreshText,
29019             cls: "x-btn-icon x-grid-loading",
29020             handler: this.onClick.createDelegate(this, ["refresh"])
29021         });
29022
29023         if(this.displayInfo){
29024             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
29025         }
29026     },
29027
29028     // private
29029     updateInfo : function(){
29030         if(this.displayEl){
29031             var count = this.ds.getCount();
29032             var msg = count == 0 ?
29033                 this.emptyMsg :
29034                 String.format(
29035                     this.displayMsg,
29036                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
29037                 );
29038             this.displayEl.update(msg);
29039         }
29040     },
29041
29042     // private
29043     onLoad : function(ds, r, o){
29044        this.cursor = o.params ? o.params.start : 0;
29045        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
29046
29047        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
29048        this.field.dom.value = ap;
29049        this.first.setDisabled(ap == 1);
29050        this.prev.setDisabled(ap == 1);
29051        this.next.setDisabled(ap == ps);
29052        this.last.setDisabled(ap == ps);
29053        this.loading.enable();
29054        this.updateInfo();
29055     },
29056
29057     // private
29058     getPageData : function(){
29059         var total = this.ds.getTotalCount();
29060         return {
29061             total : total,
29062             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
29063             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
29064         };
29065     },
29066
29067     // private
29068     onLoadError : function(){
29069         this.loading.enable();
29070     },
29071
29072     // private
29073     onPagingKeydown : function(e){
29074         var k = e.getKey();
29075         var d = this.getPageData();
29076         if(k == e.RETURN){
29077             var v = this.field.dom.value, pageNum;
29078             if(!v || isNaN(pageNum = parseInt(v, 10))){
29079                 this.field.dom.value = d.activePage;
29080                 return;
29081             }
29082             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
29083             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29084             e.stopEvent();
29085         }
29086         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))
29087         {
29088           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
29089           this.field.dom.value = pageNum;
29090           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
29091           e.stopEvent();
29092         }
29093         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29094         {
29095           var v = this.field.dom.value, pageNum; 
29096           var increment = (e.shiftKey) ? 10 : 1;
29097           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29098             increment *= -1;
29099           if(!v || isNaN(pageNum = parseInt(v, 10))) {
29100             this.field.dom.value = d.activePage;
29101             return;
29102           }
29103           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
29104           {
29105             this.field.dom.value = parseInt(v, 10) + increment;
29106             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
29107             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29108           }
29109           e.stopEvent();
29110         }
29111     },
29112
29113     // private
29114     beforeLoad : function(){
29115         if(this.loading){
29116             this.loading.disable();
29117         }
29118     },
29119
29120     // private
29121     onClick : function(which){
29122         var ds = this.ds;
29123         switch(which){
29124             case "first":
29125                 ds.load({params:{start: 0, limit: this.pageSize}});
29126             break;
29127             case "prev":
29128                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
29129             break;
29130             case "next":
29131                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
29132             break;
29133             case "last":
29134                 var total = ds.getTotalCount();
29135                 var extra = total % this.pageSize;
29136                 var lastStart = extra ? (total - extra) : total-this.pageSize;
29137                 ds.load({params:{start: lastStart, limit: this.pageSize}});
29138             break;
29139             case "refresh":
29140                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
29141             break;
29142         }
29143     },
29144
29145     /**
29146      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
29147      * @param {Roo.data.Store} store The data store to unbind
29148      */
29149     unbind : function(ds){
29150         ds.un("beforeload", this.beforeLoad, this);
29151         ds.un("load", this.onLoad, this);
29152         ds.un("loadexception", this.onLoadError, this);
29153         ds.un("remove", this.updateInfo, this);
29154         ds.un("add", this.updateInfo, this);
29155         this.ds = undefined;
29156     },
29157
29158     /**
29159      * Binds the paging toolbar to the specified {@link Roo.data.Store}
29160      * @param {Roo.data.Store} store The data store to bind
29161      */
29162     bind : function(ds){
29163         ds.on("beforeload", this.beforeLoad, this);
29164         ds.on("load", this.onLoad, this);
29165         ds.on("loadexception", this.onLoadError, this);
29166         ds.on("remove", this.updateInfo, this);
29167         ds.on("add", this.updateInfo, this);
29168         this.ds = ds;
29169     }
29170 });/*
29171  * Based on:
29172  * Ext JS Library 1.1.1
29173  * Copyright(c) 2006-2007, Ext JS, LLC.
29174  *
29175  * Originally Released Under LGPL - original licence link has changed is not relivant.
29176  *
29177  * Fork - LGPL
29178  * <script type="text/javascript">
29179  */
29180
29181 /**
29182  * @class Roo.Resizable
29183  * @extends Roo.util.Observable
29184  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
29185  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
29186  * 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
29187  * the element will be wrapped for you automatically.</p>
29188  * <p>Here is the list of valid resize handles:</p>
29189  * <pre>
29190 Value   Description
29191 ------  -------------------
29192  'n'     north
29193  's'     south
29194  'e'     east
29195  'w'     west
29196  'nw'    northwest
29197  'sw'    southwest
29198  'se'    southeast
29199  'ne'    northeast
29200  'hd'    horizontal drag
29201  'all'   all
29202 </pre>
29203  * <p>Here's an example showing the creation of a typical Resizable:</p>
29204  * <pre><code>
29205 var resizer = new Roo.Resizable("element-id", {
29206     handles: 'all',
29207     minWidth: 200,
29208     minHeight: 100,
29209     maxWidth: 500,
29210     maxHeight: 400,
29211     pinned: true
29212 });
29213 resizer.on("resize", myHandler);
29214 </code></pre>
29215  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
29216  * resizer.east.setDisplayed(false);</p>
29217  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
29218  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
29219  * resize operation's new size (defaults to [0, 0])
29220  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
29221  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
29222  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29223  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29224  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29225  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29226  * @cfg {Number} width The width of the element in pixels (defaults to null)
29227  * @cfg {Number} height The height of the element in pixels (defaults to null)
29228  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29229  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29230  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29231  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29232  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29233  * in favor of the handles config option (defaults to false)
29234  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29235  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29236  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29237  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29238  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29239  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29240  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29241  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29242  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29243  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29244  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29245  * @constructor
29246  * Create a new resizable component
29247  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29248  * @param {Object} config configuration options
29249   */
29250 Roo.Resizable = function(el, config)
29251 {
29252     this.el = Roo.get(el);
29253
29254     if(config && config.wrap){
29255         config.resizeChild = this.el;
29256         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29257         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29258         this.el.setStyle("overflow", "hidden");
29259         this.el.setPositioning(config.resizeChild.getPositioning());
29260         config.resizeChild.clearPositioning();
29261         if(!config.width || !config.height){
29262             var csize = config.resizeChild.getSize();
29263             this.el.setSize(csize.width, csize.height);
29264         }
29265         if(config.pinned && !config.adjustments){
29266             config.adjustments = "auto";
29267         }
29268     }
29269
29270     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29271     this.proxy.unselectable();
29272     this.proxy.enableDisplayMode('block');
29273
29274     Roo.apply(this, config);
29275
29276     if(this.pinned){
29277         this.disableTrackOver = true;
29278         this.el.addClass("x-resizable-pinned");
29279     }
29280     // if the element isn't positioned, make it relative
29281     var position = this.el.getStyle("position");
29282     if(position != "absolute" && position != "fixed"){
29283         this.el.setStyle("position", "relative");
29284     }
29285     if(!this.handles){ // no handles passed, must be legacy style
29286         this.handles = 's,e,se';
29287         if(this.multiDirectional){
29288             this.handles += ',n,w';
29289         }
29290     }
29291     if(this.handles == "all"){
29292         this.handles = "n s e w ne nw se sw";
29293     }
29294     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29295     var ps = Roo.Resizable.positions;
29296     for(var i = 0, len = hs.length; i < len; i++){
29297         if(hs[i] && ps[hs[i]]){
29298             var pos = ps[hs[i]];
29299             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29300         }
29301     }
29302     // legacy
29303     this.corner = this.southeast;
29304     
29305     // updateBox = the box can move..
29306     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29307         this.updateBox = true;
29308     }
29309
29310     this.activeHandle = null;
29311
29312     if(this.resizeChild){
29313         if(typeof this.resizeChild == "boolean"){
29314             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29315         }else{
29316             this.resizeChild = Roo.get(this.resizeChild, true);
29317         }
29318     }
29319     
29320     if(this.adjustments == "auto"){
29321         var rc = this.resizeChild;
29322         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29323         if(rc && (hw || hn)){
29324             rc.position("relative");
29325             rc.setLeft(hw ? hw.el.getWidth() : 0);
29326             rc.setTop(hn ? hn.el.getHeight() : 0);
29327         }
29328         this.adjustments = [
29329             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29330             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29331         ];
29332     }
29333
29334     if(this.draggable){
29335         this.dd = this.dynamic ?
29336             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29337         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29338     }
29339
29340     // public events
29341     this.addEvents({
29342         /**
29343          * @event beforeresize
29344          * Fired before resize is allowed. Set enabled to false to cancel resize.
29345          * @param {Roo.Resizable} this
29346          * @param {Roo.EventObject} e The mousedown event
29347          */
29348         "beforeresize" : true,
29349         /**
29350          * @event resizing
29351          * Fired a resizing.
29352          * @param {Roo.Resizable} this
29353          * @param {Number} x The new x position
29354          * @param {Number} y The new y position
29355          * @param {Number} w The new w width
29356          * @param {Number} h The new h hight
29357          * @param {Roo.EventObject} e The mouseup event
29358          */
29359         "resizing" : true,
29360         /**
29361          * @event resize
29362          * Fired after a resize.
29363          * @param {Roo.Resizable} this
29364          * @param {Number} width The new width
29365          * @param {Number} height The new height
29366          * @param {Roo.EventObject} e The mouseup event
29367          */
29368         "resize" : true
29369     });
29370
29371     if(this.width !== null && this.height !== null){
29372         this.resizeTo(this.width, this.height);
29373     }else{
29374         this.updateChildSize();
29375     }
29376     if(Roo.isIE){
29377         this.el.dom.style.zoom = 1;
29378     }
29379     Roo.Resizable.superclass.constructor.call(this);
29380 };
29381
29382 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29383         resizeChild : false,
29384         adjustments : [0, 0],
29385         minWidth : 5,
29386         minHeight : 5,
29387         maxWidth : 10000,
29388         maxHeight : 10000,
29389         enabled : true,
29390         animate : false,
29391         duration : .35,
29392         dynamic : false,
29393         handles : false,
29394         multiDirectional : false,
29395         disableTrackOver : false,
29396         easing : 'easeOutStrong',
29397         widthIncrement : 0,
29398         heightIncrement : 0,
29399         pinned : false,
29400         width : null,
29401         height : null,
29402         preserveRatio : false,
29403         transparent: false,
29404         minX: 0,
29405         minY: 0,
29406         draggable: false,
29407
29408         /**
29409          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29410          */
29411         constrainTo: undefined,
29412         /**
29413          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29414          */
29415         resizeRegion: undefined,
29416
29417
29418     /**
29419      * Perform a manual resize
29420      * @param {Number} width
29421      * @param {Number} height
29422      */
29423     resizeTo : function(width, height){
29424         this.el.setSize(width, height);
29425         this.updateChildSize();
29426         this.fireEvent("resize", this, width, height, null);
29427     },
29428
29429     // private
29430     startSizing : function(e, handle){
29431         this.fireEvent("beforeresize", this, e);
29432         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29433
29434             if(!this.overlay){
29435                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29436                 this.overlay.unselectable();
29437                 this.overlay.enableDisplayMode("block");
29438                 this.overlay.on("mousemove", this.onMouseMove, this);
29439                 this.overlay.on("mouseup", this.onMouseUp, this);
29440             }
29441             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29442
29443             this.resizing = true;
29444             this.startBox = this.el.getBox();
29445             this.startPoint = e.getXY();
29446             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29447                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29448
29449             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29450             this.overlay.show();
29451
29452             if(this.constrainTo) {
29453                 var ct = Roo.get(this.constrainTo);
29454                 this.resizeRegion = ct.getRegion().adjust(
29455                     ct.getFrameWidth('t'),
29456                     ct.getFrameWidth('l'),
29457                     -ct.getFrameWidth('b'),
29458                     -ct.getFrameWidth('r')
29459                 );
29460             }
29461
29462             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29463             this.proxy.show();
29464             this.proxy.setBox(this.startBox);
29465             if(!this.dynamic){
29466                 this.proxy.setStyle('visibility', 'visible');
29467             }
29468         }
29469     },
29470
29471     // private
29472     onMouseDown : function(handle, e){
29473         if(this.enabled){
29474             e.stopEvent();
29475             this.activeHandle = handle;
29476             this.startSizing(e, handle);
29477         }
29478     },
29479
29480     // private
29481     onMouseUp : function(e){
29482         var size = this.resizeElement();
29483         this.resizing = false;
29484         this.handleOut();
29485         this.overlay.hide();
29486         this.proxy.hide();
29487         this.fireEvent("resize", this, size.width, size.height, e);
29488     },
29489
29490     // private
29491     updateChildSize : function(){
29492         
29493         if(this.resizeChild){
29494             var el = this.el;
29495             var child = this.resizeChild;
29496             var adj = this.adjustments;
29497             if(el.dom.offsetWidth){
29498                 var b = el.getSize(true);
29499                 child.setSize(b.width+adj[0], b.height+adj[1]);
29500             }
29501             // Second call here for IE
29502             // The first call enables instant resizing and
29503             // the second call corrects scroll bars if they
29504             // exist
29505             if(Roo.isIE){
29506                 setTimeout(function(){
29507                     if(el.dom.offsetWidth){
29508                         var b = el.getSize(true);
29509                         child.setSize(b.width+adj[0], b.height+adj[1]);
29510                     }
29511                 }, 10);
29512             }
29513         }
29514     },
29515
29516     // private
29517     snap : function(value, inc, min){
29518         if(!inc || !value) return value;
29519         var newValue = value;
29520         var m = value % inc;
29521         if(m > 0){
29522             if(m > (inc/2)){
29523                 newValue = value + (inc-m);
29524             }else{
29525                 newValue = value - m;
29526             }
29527         }
29528         return Math.max(min, newValue);
29529     },
29530
29531     // private
29532     resizeElement : function(){
29533         var box = this.proxy.getBox();
29534         if(this.updateBox){
29535             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29536         }else{
29537             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29538         }
29539         this.updateChildSize();
29540         if(!this.dynamic){
29541             this.proxy.hide();
29542         }
29543         return box;
29544     },
29545
29546     // private
29547     constrain : function(v, diff, m, mx){
29548         if(v - diff < m){
29549             diff = v - m;
29550         }else if(v - diff > mx){
29551             diff = mx - v;
29552         }
29553         return diff;
29554     },
29555
29556     // private
29557     onMouseMove : function(e){
29558         
29559         if(this.enabled){
29560             try{// try catch so if something goes wrong the user doesn't get hung
29561
29562             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29563                 return;
29564             }
29565
29566             //var curXY = this.startPoint;
29567             var curSize = this.curSize || this.startBox;
29568             var x = this.startBox.x, y = this.startBox.y;
29569             var ox = x, oy = y;
29570             var w = curSize.width, h = curSize.height;
29571             var ow = w, oh = h;
29572             var mw = this.minWidth, mh = this.minHeight;
29573             var mxw = this.maxWidth, mxh = this.maxHeight;
29574             var wi = this.widthIncrement;
29575             var hi = this.heightIncrement;
29576
29577             var eventXY = e.getXY();
29578             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29579             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29580
29581             var pos = this.activeHandle.position;
29582
29583             switch(pos){
29584                 case "east":
29585                     w += diffX;
29586                     w = Math.min(Math.max(mw, w), mxw);
29587                     break;
29588              
29589                 case "south":
29590                     h += diffY;
29591                     h = Math.min(Math.max(mh, h), mxh);
29592                     break;
29593                 case "southeast":
29594                     w += diffX;
29595                     h += diffY;
29596                     w = Math.min(Math.max(mw, w), mxw);
29597                     h = Math.min(Math.max(mh, h), mxh);
29598                     break;
29599                 case "north":
29600                     diffY = this.constrain(h, diffY, mh, mxh);
29601                     y += diffY;
29602                     h -= diffY;
29603                     break;
29604                 case "hdrag":
29605                     
29606                     if (wi) {
29607                         var adiffX = Math.abs(diffX);
29608                         var sub = (adiffX % wi); // how much 
29609                         if (sub > (wi/2)) { // far enough to snap
29610                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29611                         } else {
29612                             // remove difference.. 
29613                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29614                         }
29615                     }
29616                     x += diffX;
29617                     x = Math.max(this.minX, x);
29618                     break;
29619                 case "west":
29620                     diffX = this.constrain(w, diffX, mw, mxw);
29621                     x += diffX;
29622                     w -= diffX;
29623                     break;
29624                 case "northeast":
29625                     w += diffX;
29626                     w = Math.min(Math.max(mw, w), mxw);
29627                     diffY = this.constrain(h, diffY, mh, mxh);
29628                     y += diffY;
29629                     h -= diffY;
29630                     break;
29631                 case "northwest":
29632                     diffX = this.constrain(w, diffX, mw, mxw);
29633                     diffY = this.constrain(h, diffY, mh, mxh);
29634                     y += diffY;
29635                     h -= diffY;
29636                     x += diffX;
29637                     w -= diffX;
29638                     break;
29639                case "southwest":
29640                     diffX = this.constrain(w, diffX, mw, mxw);
29641                     h += diffY;
29642                     h = Math.min(Math.max(mh, h), mxh);
29643                     x += diffX;
29644                     w -= diffX;
29645                     break;
29646             }
29647
29648             var sw = this.snap(w, wi, mw);
29649             var sh = this.snap(h, hi, mh);
29650             if(sw != w || sh != h){
29651                 switch(pos){
29652                     case "northeast":
29653                         y -= sh - h;
29654                     break;
29655                     case "north":
29656                         y -= sh - h;
29657                         break;
29658                     case "southwest":
29659                         x -= sw - w;
29660                     break;
29661                     case "west":
29662                         x -= sw - w;
29663                         break;
29664                     case "northwest":
29665                         x -= sw - w;
29666                         y -= sh - h;
29667                     break;
29668                 }
29669                 w = sw;
29670                 h = sh;
29671             }
29672
29673             if(this.preserveRatio){
29674                 switch(pos){
29675                     case "southeast":
29676                     case "east":
29677                         h = oh * (w/ow);
29678                         h = Math.min(Math.max(mh, h), mxh);
29679                         w = ow * (h/oh);
29680                        break;
29681                     case "south":
29682                         w = ow * (h/oh);
29683                         w = Math.min(Math.max(mw, w), mxw);
29684                         h = oh * (w/ow);
29685                         break;
29686                     case "northeast":
29687                         w = ow * (h/oh);
29688                         w = Math.min(Math.max(mw, w), mxw);
29689                         h = oh * (w/ow);
29690                     break;
29691                     case "north":
29692                         var tw = w;
29693                         w = ow * (h/oh);
29694                         w = Math.min(Math.max(mw, w), mxw);
29695                         h = oh * (w/ow);
29696                         x += (tw - w) / 2;
29697                         break;
29698                     case "southwest":
29699                         h = oh * (w/ow);
29700                         h = Math.min(Math.max(mh, h), mxh);
29701                         var tw = w;
29702                         w = ow * (h/oh);
29703                         x += tw - w;
29704                         break;
29705                     case "west":
29706                         var th = h;
29707                         h = oh * (w/ow);
29708                         h = Math.min(Math.max(mh, h), mxh);
29709                         y += (th - h) / 2;
29710                         var tw = w;
29711                         w = ow * (h/oh);
29712                         x += tw - w;
29713                        break;
29714                     case "northwest":
29715                         var tw = w;
29716                         var th = h;
29717                         h = oh * (w/ow);
29718                         h = Math.min(Math.max(mh, h), mxh);
29719                         w = ow * (h/oh);
29720                         y += th - h;
29721                         x += tw - w;
29722                        break;
29723
29724                 }
29725             }
29726             if (pos == 'hdrag') {
29727                 w = ow;
29728             }
29729             this.proxy.setBounds(x, y, w, h);
29730             if(this.dynamic){
29731                 this.resizeElement();
29732             }
29733             }catch(e){}
29734         }
29735         this.fireEvent("resizing", this, x, y, w, h, e);
29736     },
29737
29738     // private
29739     handleOver : function(){
29740         if(this.enabled){
29741             this.el.addClass("x-resizable-over");
29742         }
29743     },
29744
29745     // private
29746     handleOut : function(){
29747         if(!this.resizing){
29748             this.el.removeClass("x-resizable-over");
29749         }
29750     },
29751
29752     /**
29753      * Returns the element this component is bound to.
29754      * @return {Roo.Element}
29755      */
29756     getEl : function(){
29757         return this.el;
29758     },
29759
29760     /**
29761      * Returns the resizeChild element (or null).
29762      * @return {Roo.Element}
29763      */
29764     getResizeChild : function(){
29765         return this.resizeChild;
29766     },
29767     groupHandler : function()
29768     {
29769         
29770     },
29771     /**
29772      * Destroys this resizable. If the element was wrapped and
29773      * removeEl is not true then the element remains.
29774      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29775      */
29776     destroy : function(removeEl){
29777         this.proxy.remove();
29778         if(this.overlay){
29779             this.overlay.removeAllListeners();
29780             this.overlay.remove();
29781         }
29782         var ps = Roo.Resizable.positions;
29783         for(var k in ps){
29784             if(typeof ps[k] != "function" && this[ps[k]]){
29785                 var h = this[ps[k]];
29786                 h.el.removeAllListeners();
29787                 h.el.remove();
29788             }
29789         }
29790         if(removeEl){
29791             this.el.update("");
29792             this.el.remove();
29793         }
29794     }
29795 });
29796
29797 // private
29798 // hash to map config positions to true positions
29799 Roo.Resizable.positions = {
29800     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29801     hd: "hdrag"
29802 };
29803
29804 // private
29805 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29806     if(!this.tpl){
29807         // only initialize the template if resizable is used
29808         var tpl = Roo.DomHelper.createTemplate(
29809             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29810         );
29811         tpl.compile();
29812         Roo.Resizable.Handle.prototype.tpl = tpl;
29813     }
29814     this.position = pos;
29815     this.rz = rz;
29816     // show north drag fro topdra
29817     var handlepos = pos == 'hdrag' ? 'north' : pos;
29818     
29819     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29820     if (pos == 'hdrag') {
29821         this.el.setStyle('cursor', 'pointer');
29822     }
29823     this.el.unselectable();
29824     if(transparent){
29825         this.el.setOpacity(0);
29826     }
29827     this.el.on("mousedown", this.onMouseDown, this);
29828     if(!disableTrackOver){
29829         this.el.on("mouseover", this.onMouseOver, this);
29830         this.el.on("mouseout", this.onMouseOut, this);
29831     }
29832 };
29833
29834 // private
29835 Roo.Resizable.Handle.prototype = {
29836     afterResize : function(rz){
29837         Roo.log('after?');
29838         // do nothing
29839     },
29840     // private
29841     onMouseDown : function(e){
29842         this.rz.onMouseDown(this, e);
29843     },
29844     // private
29845     onMouseOver : function(e){
29846         this.rz.handleOver(this, e);
29847     },
29848     // private
29849     onMouseOut : function(e){
29850         this.rz.handleOut(this, e);
29851     }
29852 };/*
29853  * Based on:
29854  * Ext JS Library 1.1.1
29855  * Copyright(c) 2006-2007, Ext JS, LLC.
29856  *
29857  * Originally Released Under LGPL - original licence link has changed is not relivant.
29858  *
29859  * Fork - LGPL
29860  * <script type="text/javascript">
29861  */
29862
29863 /**
29864  * @class Roo.Editor
29865  * @extends Roo.Component
29866  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29867  * @constructor
29868  * Create a new Editor
29869  * @param {Roo.form.Field} field The Field object (or descendant)
29870  * @param {Object} config The config object
29871  */
29872 Roo.Editor = function(field, config){
29873     Roo.Editor.superclass.constructor.call(this, config);
29874     this.field = field;
29875     this.addEvents({
29876         /**
29877              * @event beforestartedit
29878              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29879              * false from the handler of this event.
29880              * @param {Editor} this
29881              * @param {Roo.Element} boundEl The underlying element bound to this editor
29882              * @param {Mixed} value The field value being set
29883              */
29884         "beforestartedit" : true,
29885         /**
29886              * @event startedit
29887              * Fires when this editor is displayed
29888              * @param {Roo.Element} boundEl The underlying element bound to this editor
29889              * @param {Mixed} value The starting field value
29890              */
29891         "startedit" : true,
29892         /**
29893              * @event beforecomplete
29894              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29895              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29896              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29897              * event will not fire since no edit actually occurred.
29898              * @param {Editor} this
29899              * @param {Mixed} value The current field value
29900              * @param {Mixed} startValue The original field value
29901              */
29902         "beforecomplete" : true,
29903         /**
29904              * @event complete
29905              * Fires after editing is complete and any changed value has been written to the underlying field.
29906              * @param {Editor} this
29907              * @param {Mixed} value The current field value
29908              * @param {Mixed} startValue The original field value
29909              */
29910         "complete" : true,
29911         /**
29912          * @event specialkey
29913          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29914          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29915          * @param {Roo.form.Field} this
29916          * @param {Roo.EventObject} e The event object
29917          */
29918         "specialkey" : true
29919     });
29920 };
29921
29922 Roo.extend(Roo.Editor, Roo.Component, {
29923     /**
29924      * @cfg {Boolean/String} autosize
29925      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29926      * or "height" to adopt the height only (defaults to false)
29927      */
29928     /**
29929      * @cfg {Boolean} revertInvalid
29930      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29931      * validation fails (defaults to true)
29932      */
29933     /**
29934      * @cfg {Boolean} ignoreNoChange
29935      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29936      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29937      * will never be ignored.
29938      */
29939     /**
29940      * @cfg {Boolean} hideEl
29941      * False to keep the bound element visible while the editor is displayed (defaults to true)
29942      */
29943     /**
29944      * @cfg {Mixed} value
29945      * The data value of the underlying field (defaults to "")
29946      */
29947     value : "",
29948     /**
29949      * @cfg {String} alignment
29950      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29951      */
29952     alignment: "c-c?",
29953     /**
29954      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29955      * for bottom-right shadow (defaults to "frame")
29956      */
29957     shadow : "frame",
29958     /**
29959      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29960      */
29961     constrain : false,
29962     /**
29963      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29964      */
29965     completeOnEnter : false,
29966     /**
29967      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29968      */
29969     cancelOnEsc : false,
29970     /**
29971      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29972      */
29973     updateEl : false,
29974
29975     // private
29976     onRender : function(ct, position){
29977         this.el = new Roo.Layer({
29978             shadow: this.shadow,
29979             cls: "x-editor",
29980             parentEl : ct,
29981             shim : this.shim,
29982             shadowOffset:4,
29983             id: this.id,
29984             constrain: this.constrain
29985         });
29986         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29987         if(this.field.msgTarget != 'title'){
29988             this.field.msgTarget = 'qtip';
29989         }
29990         this.field.render(this.el);
29991         if(Roo.isGecko){
29992             this.field.el.dom.setAttribute('autocomplete', 'off');
29993         }
29994         this.field.on("specialkey", this.onSpecialKey, this);
29995         if(this.swallowKeys){
29996             this.field.el.swallowEvent(['keydown','keypress']);
29997         }
29998         this.field.show();
29999         this.field.on("blur", this.onBlur, this);
30000         if(this.field.grow){
30001             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
30002         }
30003     },
30004
30005     onSpecialKey : function(field, e)
30006     {
30007         //Roo.log('editor onSpecialKey');
30008         if(this.completeOnEnter && e.getKey() == e.ENTER){
30009             e.stopEvent();
30010             this.completeEdit();
30011             return;
30012         }
30013         // do not fire special key otherwise it might hide close the editor...
30014         if(e.getKey() == e.ENTER){    
30015             return;
30016         }
30017         if(this.cancelOnEsc && e.getKey() == e.ESC){
30018             this.cancelEdit();
30019             return;
30020         } 
30021         this.fireEvent('specialkey', field, e);
30022     
30023     },
30024
30025     /**
30026      * Starts the editing process and shows the editor.
30027      * @param {String/HTMLElement/Element} el The element to edit
30028      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
30029       * to the innerHTML of el.
30030      */
30031     startEdit : function(el, value){
30032         if(this.editing){
30033             this.completeEdit();
30034         }
30035         this.boundEl = Roo.get(el);
30036         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
30037         if(!this.rendered){
30038             this.render(this.parentEl || document.body);
30039         }
30040         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
30041             return;
30042         }
30043         this.startValue = v;
30044         this.field.setValue(v);
30045         if(this.autoSize){
30046             var sz = this.boundEl.getSize();
30047             switch(this.autoSize){
30048                 case "width":
30049                 this.setSize(sz.width,  "");
30050                 break;
30051                 case "height":
30052                 this.setSize("",  sz.height);
30053                 break;
30054                 default:
30055                 this.setSize(sz.width,  sz.height);
30056             }
30057         }
30058         this.el.alignTo(this.boundEl, this.alignment);
30059         this.editing = true;
30060         if(Roo.QuickTips){
30061             Roo.QuickTips.disable();
30062         }
30063         this.show();
30064     },
30065
30066     /**
30067      * Sets the height and width of this editor.
30068      * @param {Number} width The new width
30069      * @param {Number} height The new height
30070      */
30071     setSize : function(w, h){
30072         this.field.setSize(w, h);
30073         if(this.el){
30074             this.el.sync();
30075         }
30076     },
30077
30078     /**
30079      * Realigns the editor to the bound field based on the current alignment config value.
30080      */
30081     realign : function(){
30082         this.el.alignTo(this.boundEl, this.alignment);
30083     },
30084
30085     /**
30086      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
30087      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
30088      */
30089     completeEdit : function(remainVisible){
30090         if(!this.editing){
30091             return;
30092         }
30093         var v = this.getValue();
30094         if(this.revertInvalid !== false && !this.field.isValid()){
30095             v = this.startValue;
30096             this.cancelEdit(true);
30097         }
30098         if(String(v) === String(this.startValue) && this.ignoreNoChange){
30099             this.editing = false;
30100             this.hide();
30101             return;
30102         }
30103         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
30104             this.editing = false;
30105             if(this.updateEl && this.boundEl){
30106                 this.boundEl.update(v);
30107             }
30108             if(remainVisible !== true){
30109                 this.hide();
30110             }
30111             this.fireEvent("complete", this, v, this.startValue);
30112         }
30113     },
30114
30115     // private
30116     onShow : function(){
30117         this.el.show();
30118         if(this.hideEl !== false){
30119             this.boundEl.hide();
30120         }
30121         this.field.show();
30122         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
30123             this.fixIEFocus = true;
30124             this.deferredFocus.defer(50, this);
30125         }else{
30126             this.field.focus();
30127         }
30128         this.fireEvent("startedit", this.boundEl, this.startValue);
30129     },
30130
30131     deferredFocus : function(){
30132         if(this.editing){
30133             this.field.focus();
30134         }
30135     },
30136
30137     /**
30138      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
30139      * reverted to the original starting value.
30140      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
30141      * cancel (defaults to false)
30142      */
30143     cancelEdit : function(remainVisible){
30144         if(this.editing){
30145             this.setValue(this.startValue);
30146             if(remainVisible !== true){
30147                 this.hide();
30148             }
30149         }
30150     },
30151
30152     // private
30153     onBlur : function(){
30154         if(this.allowBlur !== true && this.editing){
30155             this.completeEdit();
30156         }
30157     },
30158
30159     // private
30160     onHide : function(){
30161         if(this.editing){
30162             this.completeEdit();
30163             return;
30164         }
30165         this.field.blur();
30166         if(this.field.collapse){
30167             this.field.collapse();
30168         }
30169         this.el.hide();
30170         if(this.hideEl !== false){
30171             this.boundEl.show();
30172         }
30173         if(Roo.QuickTips){
30174             Roo.QuickTips.enable();
30175         }
30176     },
30177
30178     /**
30179      * Sets the data value of the editor
30180      * @param {Mixed} value Any valid value supported by the underlying field
30181      */
30182     setValue : function(v){
30183         this.field.setValue(v);
30184     },
30185
30186     /**
30187      * Gets the data value of the editor
30188      * @return {Mixed} The data value
30189      */
30190     getValue : function(){
30191         return this.field.getValue();
30192     }
30193 });/*
30194  * Based on:
30195  * Ext JS Library 1.1.1
30196  * Copyright(c) 2006-2007, Ext JS, LLC.
30197  *
30198  * Originally Released Under LGPL - original licence link has changed is not relivant.
30199  *
30200  * Fork - LGPL
30201  * <script type="text/javascript">
30202  */
30203  
30204 /**
30205  * @class Roo.BasicDialog
30206  * @extends Roo.util.Observable
30207  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
30208  * <pre><code>
30209 var dlg = new Roo.BasicDialog("my-dlg", {
30210     height: 200,
30211     width: 300,
30212     minHeight: 100,
30213     minWidth: 150,
30214     modal: true,
30215     proxyDrag: true,
30216     shadow: true
30217 });
30218 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
30219 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
30220 dlg.addButton('Cancel', dlg.hide, dlg);
30221 dlg.show();
30222 </code></pre>
30223   <b>A Dialog should always be a direct child of the body element.</b>
30224  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30225  * @cfg {String} title Default text to display in the title bar (defaults to null)
30226  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30227  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30228  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30229  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30230  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30231  * (defaults to null with no animation)
30232  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30233  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30234  * property for valid values (defaults to 'all')
30235  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30236  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30237  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30238  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30239  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30240  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30241  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30242  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30243  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30244  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30245  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30246  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30247  * draggable = true (defaults to false)
30248  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30249  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30250  * shadow (defaults to false)
30251  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30252  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30253  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30254  * @cfg {Array} buttons Array of buttons
30255  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30256  * @constructor
30257  * Create a new BasicDialog.
30258  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30259  * @param {Object} config Configuration options
30260  */
30261 Roo.BasicDialog = function(el, config){
30262     this.el = Roo.get(el);
30263     var dh = Roo.DomHelper;
30264     if(!this.el && config && config.autoCreate){
30265         if(typeof config.autoCreate == "object"){
30266             if(!config.autoCreate.id){
30267                 config.autoCreate.id = el;
30268             }
30269             this.el = dh.append(document.body,
30270                         config.autoCreate, true);
30271         }else{
30272             this.el = dh.append(document.body,
30273                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30274         }
30275     }
30276     el = this.el;
30277     el.setDisplayed(true);
30278     el.hide = this.hideAction;
30279     this.id = el.id;
30280     el.addClass("x-dlg");
30281
30282     Roo.apply(this, config);
30283
30284     this.proxy = el.createProxy("x-dlg-proxy");
30285     this.proxy.hide = this.hideAction;
30286     this.proxy.setOpacity(.5);
30287     this.proxy.hide();
30288
30289     if(config.width){
30290         el.setWidth(config.width);
30291     }
30292     if(config.height){
30293         el.setHeight(config.height);
30294     }
30295     this.size = el.getSize();
30296     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30297         this.xy = [config.x,config.y];
30298     }else{
30299         this.xy = el.getCenterXY(true);
30300     }
30301     /** The header element @type Roo.Element */
30302     this.header = el.child("> .x-dlg-hd");
30303     /** The body element @type Roo.Element */
30304     this.body = el.child("> .x-dlg-bd");
30305     /** The footer element @type Roo.Element */
30306     this.footer = el.child("> .x-dlg-ft");
30307
30308     if(!this.header){
30309         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30310     }
30311     if(!this.body){
30312         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30313     }
30314
30315     this.header.unselectable();
30316     if(this.title){
30317         this.header.update(this.title);
30318     }
30319     // this element allows the dialog to be focused for keyboard event
30320     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30321     this.focusEl.swallowEvent("click", true);
30322
30323     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30324
30325     // wrap the body and footer for special rendering
30326     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30327     if(this.footer){
30328         this.bwrap.dom.appendChild(this.footer.dom);
30329     }
30330
30331     this.bg = this.el.createChild({
30332         tag: "div", cls:"x-dlg-bg",
30333         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30334     });
30335     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30336
30337
30338     if(this.autoScroll !== false && !this.autoTabs){
30339         this.body.setStyle("overflow", "auto");
30340     }
30341
30342     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30343
30344     if(this.closable !== false){
30345         this.el.addClass("x-dlg-closable");
30346         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30347         this.close.on("click", this.closeClick, this);
30348         this.close.addClassOnOver("x-dlg-close-over");
30349     }
30350     if(this.collapsible !== false){
30351         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30352         this.collapseBtn.on("click", this.collapseClick, this);
30353         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30354         this.header.on("dblclick", this.collapseClick, this);
30355     }
30356     if(this.resizable !== false){
30357         this.el.addClass("x-dlg-resizable");
30358         this.resizer = new Roo.Resizable(el, {
30359             minWidth: this.minWidth || 80,
30360             minHeight:this.minHeight || 80,
30361             handles: this.resizeHandles || "all",
30362             pinned: true
30363         });
30364         this.resizer.on("beforeresize", this.beforeResize, this);
30365         this.resizer.on("resize", this.onResize, this);
30366     }
30367     if(this.draggable !== false){
30368         el.addClass("x-dlg-draggable");
30369         if (!this.proxyDrag) {
30370             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30371         }
30372         else {
30373             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30374         }
30375         dd.setHandleElId(this.header.id);
30376         dd.endDrag = this.endMove.createDelegate(this);
30377         dd.startDrag = this.startMove.createDelegate(this);
30378         dd.onDrag = this.onDrag.createDelegate(this);
30379         dd.scroll = false;
30380         this.dd = dd;
30381     }
30382     if(this.modal){
30383         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30384         this.mask.enableDisplayMode("block");
30385         this.mask.hide();
30386         this.el.addClass("x-dlg-modal");
30387     }
30388     if(this.shadow){
30389         this.shadow = new Roo.Shadow({
30390             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30391             offset : this.shadowOffset
30392         });
30393     }else{
30394         this.shadowOffset = 0;
30395     }
30396     if(Roo.useShims && this.shim !== false){
30397         this.shim = this.el.createShim();
30398         this.shim.hide = this.hideAction;
30399         this.shim.hide();
30400     }else{
30401         this.shim = false;
30402     }
30403     if(this.autoTabs){
30404         this.initTabs();
30405     }
30406     if (this.buttons) { 
30407         var bts= this.buttons;
30408         this.buttons = [];
30409         Roo.each(bts, function(b) {
30410             this.addButton(b);
30411         }, this);
30412     }
30413     
30414     
30415     this.addEvents({
30416         /**
30417          * @event keydown
30418          * Fires when a key is pressed
30419          * @param {Roo.BasicDialog} this
30420          * @param {Roo.EventObject} e
30421          */
30422         "keydown" : true,
30423         /**
30424          * @event move
30425          * Fires when this dialog is moved by the user.
30426          * @param {Roo.BasicDialog} this
30427          * @param {Number} x The new page X
30428          * @param {Number} y The new page Y
30429          */
30430         "move" : true,
30431         /**
30432          * @event resize
30433          * Fires when this dialog is resized by the user.
30434          * @param {Roo.BasicDialog} this
30435          * @param {Number} width The new width
30436          * @param {Number} height The new height
30437          */
30438         "resize" : true,
30439         /**
30440          * @event beforehide
30441          * Fires before this dialog is hidden.
30442          * @param {Roo.BasicDialog} this
30443          */
30444         "beforehide" : true,
30445         /**
30446          * @event hide
30447          * Fires when this dialog is hidden.
30448          * @param {Roo.BasicDialog} this
30449          */
30450         "hide" : true,
30451         /**
30452          * @event beforeshow
30453          * Fires before this dialog is shown.
30454          * @param {Roo.BasicDialog} this
30455          */
30456         "beforeshow" : true,
30457         /**
30458          * @event show
30459          * Fires when this dialog is shown.
30460          * @param {Roo.BasicDialog} this
30461          */
30462         "show" : true
30463     });
30464     el.on("keydown", this.onKeyDown, this);
30465     el.on("mousedown", this.toFront, this);
30466     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30467     this.el.hide();
30468     Roo.DialogManager.register(this);
30469     Roo.BasicDialog.superclass.constructor.call(this);
30470 };
30471
30472 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30473     shadowOffset: Roo.isIE ? 6 : 5,
30474     minHeight: 80,
30475     minWidth: 200,
30476     minButtonWidth: 75,
30477     defaultButton: null,
30478     buttonAlign: "right",
30479     tabTag: 'div',
30480     firstShow: true,
30481
30482     /**
30483      * Sets the dialog title text
30484      * @param {String} text The title text to display
30485      * @return {Roo.BasicDialog} this
30486      */
30487     setTitle : function(text){
30488         this.header.update(text);
30489         return this;
30490     },
30491
30492     // private
30493     closeClick : function(){
30494         this.hide();
30495     },
30496
30497     // private
30498     collapseClick : function(){
30499         this[this.collapsed ? "expand" : "collapse"]();
30500     },
30501
30502     /**
30503      * Collapses the dialog to its minimized state (only the title bar is visible).
30504      * Equivalent to the user clicking the collapse dialog button.
30505      */
30506     collapse : function(){
30507         if(!this.collapsed){
30508             this.collapsed = true;
30509             this.el.addClass("x-dlg-collapsed");
30510             this.restoreHeight = this.el.getHeight();
30511             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30512         }
30513     },
30514
30515     /**
30516      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30517      * clicking the expand dialog button.
30518      */
30519     expand : function(){
30520         if(this.collapsed){
30521             this.collapsed = false;
30522             this.el.removeClass("x-dlg-collapsed");
30523             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30524         }
30525     },
30526
30527     /**
30528      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30529      * @return {Roo.TabPanel} The tabs component
30530      */
30531     initTabs : function(){
30532         var tabs = this.getTabs();
30533         while(tabs.getTab(0)){
30534             tabs.removeTab(0);
30535         }
30536         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30537             var dom = el.dom;
30538             tabs.addTab(Roo.id(dom), dom.title);
30539             dom.title = "";
30540         });
30541         tabs.activate(0);
30542         return tabs;
30543     },
30544
30545     // private
30546     beforeResize : function(){
30547         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30548     },
30549
30550     // private
30551     onResize : function(){
30552         this.refreshSize();
30553         this.syncBodyHeight();
30554         this.adjustAssets();
30555         this.focus();
30556         this.fireEvent("resize", this, this.size.width, this.size.height);
30557     },
30558
30559     // private
30560     onKeyDown : function(e){
30561         if(this.isVisible()){
30562             this.fireEvent("keydown", this, e);
30563         }
30564     },
30565
30566     /**
30567      * Resizes the dialog.
30568      * @param {Number} width
30569      * @param {Number} height
30570      * @return {Roo.BasicDialog} this
30571      */
30572     resizeTo : function(width, height){
30573         this.el.setSize(width, height);
30574         this.size = {width: width, height: height};
30575         this.syncBodyHeight();
30576         if(this.fixedcenter){
30577             this.center();
30578         }
30579         if(this.isVisible()){
30580             this.constrainXY();
30581             this.adjustAssets();
30582         }
30583         this.fireEvent("resize", this, width, height);
30584         return this;
30585     },
30586
30587
30588     /**
30589      * Resizes the dialog to fit the specified content size.
30590      * @param {Number} width
30591      * @param {Number} height
30592      * @return {Roo.BasicDialog} this
30593      */
30594     setContentSize : function(w, h){
30595         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30596         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30597         //if(!this.el.isBorderBox()){
30598             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30599             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30600         //}
30601         if(this.tabs){
30602             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30603             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30604         }
30605         this.resizeTo(w, h);
30606         return this;
30607     },
30608
30609     /**
30610      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30611      * executed in response to a particular key being pressed while the dialog is active.
30612      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30613      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30614      * @param {Function} fn The function to call
30615      * @param {Object} scope (optional) The scope of the function
30616      * @return {Roo.BasicDialog} this
30617      */
30618     addKeyListener : function(key, fn, scope){
30619         var keyCode, shift, ctrl, alt;
30620         if(typeof key == "object" && !(key instanceof Array)){
30621             keyCode = key["key"];
30622             shift = key["shift"];
30623             ctrl = key["ctrl"];
30624             alt = key["alt"];
30625         }else{
30626             keyCode = key;
30627         }
30628         var handler = function(dlg, e){
30629             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30630                 var k = e.getKey();
30631                 if(keyCode instanceof Array){
30632                     for(var i = 0, len = keyCode.length; i < len; i++){
30633                         if(keyCode[i] == k){
30634                           fn.call(scope || window, dlg, k, e);
30635                           return;
30636                         }
30637                     }
30638                 }else{
30639                     if(k == keyCode){
30640                         fn.call(scope || window, dlg, k, e);
30641                     }
30642                 }
30643             }
30644         };
30645         this.on("keydown", handler);
30646         return this;
30647     },
30648
30649     /**
30650      * Returns the TabPanel component (creates it if it doesn't exist).
30651      * Note: If you wish to simply check for the existence of tabs without creating them,
30652      * check for a null 'tabs' property.
30653      * @return {Roo.TabPanel} The tabs component
30654      */
30655     getTabs : function(){
30656         if(!this.tabs){
30657             this.el.addClass("x-dlg-auto-tabs");
30658             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30659             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30660         }
30661         return this.tabs;
30662     },
30663
30664     /**
30665      * Adds a button to the footer section of the dialog.
30666      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30667      * object or a valid Roo.DomHelper element config
30668      * @param {Function} handler The function called when the button is clicked
30669      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30670      * @return {Roo.Button} The new button
30671      */
30672     addButton : function(config, handler, scope){
30673         var dh = Roo.DomHelper;
30674         if(!this.footer){
30675             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30676         }
30677         if(!this.btnContainer){
30678             var tb = this.footer.createChild({
30679
30680                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30681                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30682             }, null, true);
30683             this.btnContainer = tb.firstChild.firstChild.firstChild;
30684         }
30685         var bconfig = {
30686             handler: handler,
30687             scope: scope,
30688             minWidth: this.minButtonWidth,
30689             hideParent:true
30690         };
30691         if(typeof config == "string"){
30692             bconfig.text = config;
30693         }else{
30694             if(config.tag){
30695                 bconfig.dhconfig = config;
30696             }else{
30697                 Roo.apply(bconfig, config);
30698             }
30699         }
30700         var fc = false;
30701         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30702             bconfig.position = Math.max(0, bconfig.position);
30703             fc = this.btnContainer.childNodes[bconfig.position];
30704         }
30705          
30706         var btn = new Roo.Button(
30707             fc ? 
30708                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30709                 : this.btnContainer.appendChild(document.createElement("td")),
30710             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30711             bconfig
30712         );
30713         this.syncBodyHeight();
30714         if(!this.buttons){
30715             /**
30716              * Array of all the buttons that have been added to this dialog via addButton
30717              * @type Array
30718              */
30719             this.buttons = [];
30720         }
30721         this.buttons.push(btn);
30722         return btn;
30723     },
30724
30725     /**
30726      * Sets the default button to be focused when the dialog is displayed.
30727      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30728      * @return {Roo.BasicDialog} this
30729      */
30730     setDefaultButton : function(btn){
30731         this.defaultButton = btn;
30732         return this;
30733     },
30734
30735     // private
30736     getHeaderFooterHeight : function(safe){
30737         var height = 0;
30738         if(this.header){
30739            height += this.header.getHeight();
30740         }
30741         if(this.footer){
30742            var fm = this.footer.getMargins();
30743             height += (this.footer.getHeight()+fm.top+fm.bottom);
30744         }
30745         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30746         height += this.centerBg.getPadding("tb");
30747         return height;
30748     },
30749
30750     // private
30751     syncBodyHeight : function()
30752     {
30753         var bd = this.body, // the text
30754             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30755             bw = this.bwrap;
30756         var height = this.size.height - this.getHeaderFooterHeight(false);
30757         bd.setHeight(height-bd.getMargins("tb"));
30758         var hh = this.header.getHeight();
30759         var h = this.size.height-hh;
30760         cb.setHeight(h);
30761         
30762         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30763         bw.setHeight(h-cb.getPadding("tb"));
30764         
30765         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30766         bd.setWidth(bw.getWidth(true));
30767         if(this.tabs){
30768             this.tabs.syncHeight();
30769             if(Roo.isIE){
30770                 this.tabs.el.repaint();
30771             }
30772         }
30773     },
30774
30775     /**
30776      * Restores the previous state of the dialog if Roo.state is configured.
30777      * @return {Roo.BasicDialog} this
30778      */
30779     restoreState : function(){
30780         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30781         if(box && box.width){
30782             this.xy = [box.x, box.y];
30783             this.resizeTo(box.width, box.height);
30784         }
30785         return this;
30786     },
30787
30788     // private
30789     beforeShow : function(){
30790         this.expand();
30791         if(this.fixedcenter){
30792             this.xy = this.el.getCenterXY(true);
30793         }
30794         if(this.modal){
30795             Roo.get(document.body).addClass("x-body-masked");
30796             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30797             this.mask.show();
30798         }
30799         this.constrainXY();
30800     },
30801
30802     // private
30803     animShow : function(){
30804         var b = Roo.get(this.animateTarget).getBox();
30805         this.proxy.setSize(b.width, b.height);
30806         this.proxy.setLocation(b.x, b.y);
30807         this.proxy.show();
30808         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30809                     true, .35, this.showEl.createDelegate(this));
30810     },
30811
30812     /**
30813      * Shows the dialog.
30814      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30815      * @return {Roo.BasicDialog} this
30816      */
30817     show : function(animateTarget){
30818         if (this.fireEvent("beforeshow", this) === false){
30819             return;
30820         }
30821         if(this.syncHeightBeforeShow){
30822             this.syncBodyHeight();
30823         }else if(this.firstShow){
30824             this.firstShow = false;
30825             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30826         }
30827         this.animateTarget = animateTarget || this.animateTarget;
30828         if(!this.el.isVisible()){
30829             this.beforeShow();
30830             if(this.animateTarget && Roo.get(this.animateTarget)){
30831                 this.animShow();
30832             }else{
30833                 this.showEl();
30834             }
30835         }
30836         return this;
30837     },
30838
30839     // private
30840     showEl : function(){
30841         this.proxy.hide();
30842         this.el.setXY(this.xy);
30843         this.el.show();
30844         this.adjustAssets(true);
30845         this.toFront();
30846         this.focus();
30847         // IE peekaboo bug - fix found by Dave Fenwick
30848         if(Roo.isIE){
30849             this.el.repaint();
30850         }
30851         this.fireEvent("show", this);
30852     },
30853
30854     /**
30855      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30856      * dialog itself will receive focus.
30857      */
30858     focus : function(){
30859         if(this.defaultButton){
30860             this.defaultButton.focus();
30861         }else{
30862             this.focusEl.focus();
30863         }
30864     },
30865
30866     // private
30867     constrainXY : function(){
30868         if(this.constraintoviewport !== false){
30869             if(!this.viewSize){
30870                 if(this.container){
30871                     var s = this.container.getSize();
30872                     this.viewSize = [s.width, s.height];
30873                 }else{
30874                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30875                 }
30876             }
30877             var s = Roo.get(this.container||document).getScroll();
30878
30879             var x = this.xy[0], y = this.xy[1];
30880             var w = this.size.width, h = this.size.height;
30881             var vw = this.viewSize[0], vh = this.viewSize[1];
30882             // only move it if it needs it
30883             var moved = false;
30884             // first validate right/bottom
30885             if(x + w > vw+s.left){
30886                 x = vw - w;
30887                 moved = true;
30888             }
30889             if(y + h > vh+s.top){
30890                 y = vh - h;
30891                 moved = true;
30892             }
30893             // then make sure top/left isn't negative
30894             if(x < s.left){
30895                 x = s.left;
30896                 moved = true;
30897             }
30898             if(y < s.top){
30899                 y = s.top;
30900                 moved = true;
30901             }
30902             if(moved){
30903                 // cache xy
30904                 this.xy = [x, y];
30905                 if(this.isVisible()){
30906                     this.el.setLocation(x, y);
30907                     this.adjustAssets();
30908                 }
30909             }
30910         }
30911     },
30912
30913     // private
30914     onDrag : function(){
30915         if(!this.proxyDrag){
30916             this.xy = this.el.getXY();
30917             this.adjustAssets();
30918         }
30919     },
30920
30921     // private
30922     adjustAssets : function(doShow){
30923         var x = this.xy[0], y = this.xy[1];
30924         var w = this.size.width, h = this.size.height;
30925         if(doShow === true){
30926             if(this.shadow){
30927                 this.shadow.show(this.el);
30928             }
30929             if(this.shim){
30930                 this.shim.show();
30931             }
30932         }
30933         if(this.shadow && this.shadow.isVisible()){
30934             this.shadow.show(this.el);
30935         }
30936         if(this.shim && this.shim.isVisible()){
30937             this.shim.setBounds(x, y, w, h);
30938         }
30939     },
30940
30941     // private
30942     adjustViewport : function(w, h){
30943         if(!w || !h){
30944             w = Roo.lib.Dom.getViewWidth();
30945             h = Roo.lib.Dom.getViewHeight();
30946         }
30947         // cache the size
30948         this.viewSize = [w, h];
30949         if(this.modal && this.mask.isVisible()){
30950             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30951             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30952         }
30953         if(this.isVisible()){
30954             this.constrainXY();
30955         }
30956     },
30957
30958     /**
30959      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30960      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30961      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30962      */
30963     destroy : function(removeEl){
30964         if(this.isVisible()){
30965             this.animateTarget = null;
30966             this.hide();
30967         }
30968         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30969         if(this.tabs){
30970             this.tabs.destroy(removeEl);
30971         }
30972         Roo.destroy(
30973              this.shim,
30974              this.proxy,
30975              this.resizer,
30976              this.close,
30977              this.mask
30978         );
30979         if(this.dd){
30980             this.dd.unreg();
30981         }
30982         if(this.buttons){
30983            for(var i = 0, len = this.buttons.length; i < len; i++){
30984                this.buttons[i].destroy();
30985            }
30986         }
30987         this.el.removeAllListeners();
30988         if(removeEl === true){
30989             this.el.update("");
30990             this.el.remove();
30991         }
30992         Roo.DialogManager.unregister(this);
30993     },
30994
30995     // private
30996     startMove : function(){
30997         if(this.proxyDrag){
30998             this.proxy.show();
30999         }
31000         if(this.constraintoviewport !== false){
31001             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
31002         }
31003     },
31004
31005     // private
31006     endMove : function(){
31007         if(!this.proxyDrag){
31008             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
31009         }else{
31010             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
31011             this.proxy.hide();
31012         }
31013         this.refreshSize();
31014         this.adjustAssets();
31015         this.focus();
31016         this.fireEvent("move", this, this.xy[0], this.xy[1]);
31017     },
31018
31019     /**
31020      * Brings this dialog to the front of any other visible dialogs
31021      * @return {Roo.BasicDialog} this
31022      */
31023     toFront : function(){
31024         Roo.DialogManager.bringToFront(this);
31025         return this;
31026     },
31027
31028     /**
31029      * Sends this dialog to the back (under) of any other visible dialogs
31030      * @return {Roo.BasicDialog} this
31031      */
31032     toBack : function(){
31033         Roo.DialogManager.sendToBack(this);
31034         return this;
31035     },
31036
31037     /**
31038      * Centers this dialog in the viewport
31039      * @return {Roo.BasicDialog} this
31040      */
31041     center : function(){
31042         var xy = this.el.getCenterXY(true);
31043         this.moveTo(xy[0], xy[1]);
31044         return this;
31045     },
31046
31047     /**
31048      * Moves the dialog's top-left corner to the specified point
31049      * @param {Number} x
31050      * @param {Number} y
31051      * @return {Roo.BasicDialog} this
31052      */
31053     moveTo : function(x, y){
31054         this.xy = [x,y];
31055         if(this.isVisible()){
31056             this.el.setXY(this.xy);
31057             this.adjustAssets();
31058         }
31059         return this;
31060     },
31061
31062     /**
31063      * Aligns the dialog to the specified element
31064      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31065      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
31066      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31067      * @return {Roo.BasicDialog} this
31068      */
31069     alignTo : function(element, position, offsets){
31070         this.xy = this.el.getAlignToXY(element, position, offsets);
31071         if(this.isVisible()){
31072             this.el.setXY(this.xy);
31073             this.adjustAssets();
31074         }
31075         return this;
31076     },
31077
31078     /**
31079      * Anchors an element to another element and realigns it when the window is resized.
31080      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31081      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
31082      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31083      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
31084      * is a number, it is used as the buffer delay (defaults to 50ms).
31085      * @return {Roo.BasicDialog} this
31086      */
31087     anchorTo : function(el, alignment, offsets, monitorScroll){
31088         var action = function(){
31089             this.alignTo(el, alignment, offsets);
31090         };
31091         Roo.EventManager.onWindowResize(action, this);
31092         var tm = typeof monitorScroll;
31093         if(tm != 'undefined'){
31094             Roo.EventManager.on(window, 'scroll', action, this,
31095                 {buffer: tm == 'number' ? monitorScroll : 50});
31096         }
31097         action.call(this);
31098         return this;
31099     },
31100
31101     /**
31102      * Returns true if the dialog is visible
31103      * @return {Boolean}
31104      */
31105     isVisible : function(){
31106         return this.el.isVisible();
31107     },
31108
31109     // private
31110     animHide : function(callback){
31111         var b = Roo.get(this.animateTarget).getBox();
31112         this.proxy.show();
31113         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
31114         this.el.hide();
31115         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
31116                     this.hideEl.createDelegate(this, [callback]));
31117     },
31118
31119     /**
31120      * Hides the dialog.
31121      * @param {Function} callback (optional) Function to call when the dialog is hidden
31122      * @return {Roo.BasicDialog} this
31123      */
31124     hide : function(callback){
31125         if (this.fireEvent("beforehide", this) === false){
31126             return;
31127         }
31128         if(this.shadow){
31129             this.shadow.hide();
31130         }
31131         if(this.shim) {
31132           this.shim.hide();
31133         }
31134         // sometimes animateTarget seems to get set.. causing problems...
31135         // this just double checks..
31136         if(this.animateTarget && Roo.get(this.animateTarget)) {
31137            this.animHide(callback);
31138         }else{
31139             this.el.hide();
31140             this.hideEl(callback);
31141         }
31142         return this;
31143     },
31144
31145     // private
31146     hideEl : function(callback){
31147         this.proxy.hide();
31148         if(this.modal){
31149             this.mask.hide();
31150             Roo.get(document.body).removeClass("x-body-masked");
31151         }
31152         this.fireEvent("hide", this);
31153         if(typeof callback == "function"){
31154             callback();
31155         }
31156     },
31157
31158     // private
31159     hideAction : function(){
31160         this.setLeft("-10000px");
31161         this.setTop("-10000px");
31162         this.setStyle("visibility", "hidden");
31163     },
31164
31165     // private
31166     refreshSize : function(){
31167         this.size = this.el.getSize();
31168         this.xy = this.el.getXY();
31169         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
31170     },
31171
31172     // private
31173     // z-index is managed by the DialogManager and may be overwritten at any time
31174     setZIndex : function(index){
31175         if(this.modal){
31176             this.mask.setStyle("z-index", index);
31177         }
31178         if(this.shim){
31179             this.shim.setStyle("z-index", ++index);
31180         }
31181         if(this.shadow){
31182             this.shadow.setZIndex(++index);
31183         }
31184         this.el.setStyle("z-index", ++index);
31185         if(this.proxy){
31186             this.proxy.setStyle("z-index", ++index);
31187         }
31188         if(this.resizer){
31189             this.resizer.proxy.setStyle("z-index", ++index);
31190         }
31191
31192         this.lastZIndex = index;
31193     },
31194
31195     /**
31196      * Returns the element for this dialog
31197      * @return {Roo.Element} The underlying dialog Element
31198      */
31199     getEl : function(){
31200         return this.el;
31201     }
31202 });
31203
31204 /**
31205  * @class Roo.DialogManager
31206  * Provides global access to BasicDialogs that have been created and
31207  * support for z-indexing (layering) multiple open dialogs.
31208  */
31209 Roo.DialogManager = function(){
31210     var list = {};
31211     var accessList = [];
31212     var front = null;
31213
31214     // private
31215     var sortDialogs = function(d1, d2){
31216         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
31217     };
31218
31219     // private
31220     var orderDialogs = function(){
31221         accessList.sort(sortDialogs);
31222         var seed = Roo.DialogManager.zseed;
31223         for(var i = 0, len = accessList.length; i < len; i++){
31224             var dlg = accessList[i];
31225             if(dlg){
31226                 dlg.setZIndex(seed + (i*10));
31227             }
31228         }
31229     };
31230
31231     return {
31232         /**
31233          * The starting z-index for BasicDialogs (defaults to 9000)
31234          * @type Number The z-index value
31235          */
31236         zseed : 9000,
31237
31238         // private
31239         register : function(dlg){
31240             list[dlg.id] = dlg;
31241             accessList.push(dlg);
31242         },
31243
31244         // private
31245         unregister : function(dlg){
31246             delete list[dlg.id];
31247             var i=0;
31248             var len=0;
31249             if(!accessList.indexOf){
31250                 for(  i = 0, len = accessList.length; i < len; i++){
31251                     if(accessList[i] == dlg){
31252                         accessList.splice(i, 1);
31253                         return;
31254                     }
31255                 }
31256             }else{
31257                  i = accessList.indexOf(dlg);
31258                 if(i != -1){
31259                     accessList.splice(i, 1);
31260                 }
31261             }
31262         },
31263
31264         /**
31265          * Gets a registered dialog by id
31266          * @param {String/Object} id The id of the dialog or a dialog
31267          * @return {Roo.BasicDialog} this
31268          */
31269         get : function(id){
31270             return typeof id == "object" ? id : list[id];
31271         },
31272
31273         /**
31274          * Brings the specified dialog to the front
31275          * @param {String/Object} dlg The id of the dialog or a dialog
31276          * @return {Roo.BasicDialog} this
31277          */
31278         bringToFront : function(dlg){
31279             dlg = this.get(dlg);
31280             if(dlg != front){
31281                 front = dlg;
31282                 dlg._lastAccess = new Date().getTime();
31283                 orderDialogs();
31284             }
31285             return dlg;
31286         },
31287
31288         /**
31289          * Sends the specified dialog to the back
31290          * @param {String/Object} dlg The id of the dialog or a dialog
31291          * @return {Roo.BasicDialog} this
31292          */
31293         sendToBack : function(dlg){
31294             dlg = this.get(dlg);
31295             dlg._lastAccess = -(new Date().getTime());
31296             orderDialogs();
31297             return dlg;
31298         },
31299
31300         /**
31301          * Hides all dialogs
31302          */
31303         hideAll : function(){
31304             for(var id in list){
31305                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31306                     list[id].hide();
31307                 }
31308             }
31309         }
31310     };
31311 }();
31312
31313 /**
31314  * @class Roo.LayoutDialog
31315  * @extends Roo.BasicDialog
31316  * Dialog which provides adjustments for working with a layout in a Dialog.
31317  * Add your necessary layout config options to the dialog's config.<br>
31318  * Example usage (including a nested layout):
31319  * <pre><code>
31320 if(!dialog){
31321     dialog = new Roo.LayoutDialog("download-dlg", {
31322         modal: true,
31323         width:600,
31324         height:450,
31325         shadow:true,
31326         minWidth:500,
31327         minHeight:350,
31328         autoTabs:true,
31329         proxyDrag:true,
31330         // layout config merges with the dialog config
31331         center:{
31332             tabPosition: "top",
31333             alwaysShowTabs: true
31334         }
31335     });
31336     dialog.addKeyListener(27, dialog.hide, dialog);
31337     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31338     dialog.addButton("Build It!", this.getDownload, this);
31339
31340     // we can even add nested layouts
31341     var innerLayout = new Roo.BorderLayout("dl-inner", {
31342         east: {
31343             initialSize: 200,
31344             autoScroll:true,
31345             split:true
31346         },
31347         center: {
31348             autoScroll:true
31349         }
31350     });
31351     innerLayout.beginUpdate();
31352     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31353     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31354     innerLayout.endUpdate(true);
31355
31356     var layout = dialog.getLayout();
31357     layout.beginUpdate();
31358     layout.add("center", new Roo.ContentPanel("standard-panel",
31359                         {title: "Download the Source", fitToFrame:true}));
31360     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31361                {title: "Build your own roo.js"}));
31362     layout.getRegion("center").showPanel(sp);
31363     layout.endUpdate();
31364 }
31365 </code></pre>
31366     * @constructor
31367     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31368     * @param {Object} config configuration options
31369   */
31370 Roo.LayoutDialog = function(el, cfg){
31371     
31372     var config=  cfg;
31373     if (typeof(cfg) == 'undefined') {
31374         config = Roo.apply({}, el);
31375         // not sure why we use documentElement here.. - it should always be body.
31376         // IE7 borks horribly if we use documentElement.
31377         // webkit also does not like documentElement - it creates a body element...
31378         el = Roo.get( document.body || document.documentElement ).createChild();
31379         //config.autoCreate = true;
31380     }
31381     
31382     
31383     config.autoTabs = false;
31384     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31385     this.body.setStyle({overflow:"hidden", position:"relative"});
31386     this.layout = new Roo.BorderLayout(this.body.dom, config);
31387     this.layout.monitorWindowResize = false;
31388     this.el.addClass("x-dlg-auto-layout");
31389     // fix case when center region overwrites center function
31390     this.center = Roo.BasicDialog.prototype.center;
31391     this.on("show", this.layout.layout, this.layout, true);
31392     if (config.items) {
31393         var xitems = config.items;
31394         delete config.items;
31395         Roo.each(xitems, this.addxtype, this);
31396     }
31397     
31398     
31399 };
31400 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31401     /**
31402      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31403      * @deprecated
31404      */
31405     endUpdate : function(){
31406         this.layout.endUpdate();
31407     },
31408
31409     /**
31410      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31411      *  @deprecated
31412      */
31413     beginUpdate : function(){
31414         this.layout.beginUpdate();
31415     },
31416
31417     /**
31418      * Get the BorderLayout for this dialog
31419      * @return {Roo.BorderLayout}
31420      */
31421     getLayout : function(){
31422         return this.layout;
31423     },
31424
31425     showEl : function(){
31426         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31427         if(Roo.isIE7){
31428             this.layout.layout();
31429         }
31430     },
31431
31432     // private
31433     // Use the syncHeightBeforeShow config option to control this automatically
31434     syncBodyHeight : function(){
31435         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31436         if(this.layout){this.layout.layout();}
31437     },
31438     
31439       /**
31440      * Add an xtype element (actually adds to the layout.)
31441      * @return {Object} xdata xtype object data.
31442      */
31443     
31444     addxtype : function(c) {
31445         return this.layout.addxtype(c);
31446     }
31447 });/*
31448  * Based on:
31449  * Ext JS Library 1.1.1
31450  * Copyright(c) 2006-2007, Ext JS, LLC.
31451  *
31452  * Originally Released Under LGPL - original licence link has changed is not relivant.
31453  *
31454  * Fork - LGPL
31455  * <script type="text/javascript">
31456  */
31457  
31458 /**
31459  * @class Roo.MessageBox
31460  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31461  * Example usage:
31462  *<pre><code>
31463 // Basic alert:
31464 Roo.Msg.alert('Status', 'Changes saved successfully.');
31465
31466 // Prompt for user data:
31467 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31468     if (btn == 'ok'){
31469         // process text value...
31470     }
31471 });
31472
31473 // Show a dialog using config options:
31474 Roo.Msg.show({
31475    title:'Save Changes?',
31476    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31477    buttons: Roo.Msg.YESNOCANCEL,
31478    fn: processResult,
31479    animEl: 'elId'
31480 });
31481 </code></pre>
31482  * @singleton
31483  */
31484 Roo.MessageBox = function(){
31485     var dlg, opt, mask, waitTimer;
31486     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31487     var buttons, activeTextEl, bwidth;
31488
31489     // private
31490     var handleButton = function(button){
31491         dlg.hide();
31492         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31493     };
31494
31495     // private
31496     var handleHide = function(){
31497         if(opt && opt.cls){
31498             dlg.el.removeClass(opt.cls);
31499         }
31500         if(waitTimer){
31501             Roo.TaskMgr.stop(waitTimer);
31502             waitTimer = null;
31503         }
31504     };
31505
31506     // private
31507     var updateButtons = function(b){
31508         var width = 0;
31509         if(!b){
31510             buttons["ok"].hide();
31511             buttons["cancel"].hide();
31512             buttons["yes"].hide();
31513             buttons["no"].hide();
31514             dlg.footer.dom.style.display = 'none';
31515             return width;
31516         }
31517         dlg.footer.dom.style.display = '';
31518         for(var k in buttons){
31519             if(typeof buttons[k] != "function"){
31520                 if(b[k]){
31521                     buttons[k].show();
31522                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31523                     width += buttons[k].el.getWidth()+15;
31524                 }else{
31525                     buttons[k].hide();
31526                 }
31527             }
31528         }
31529         return width;
31530     };
31531
31532     // private
31533     var handleEsc = function(d, k, e){
31534         if(opt && opt.closable !== false){
31535             dlg.hide();
31536         }
31537         if(e){
31538             e.stopEvent();
31539         }
31540     };
31541
31542     return {
31543         /**
31544          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31545          * @return {Roo.BasicDialog} The BasicDialog element
31546          */
31547         getDialog : function(){
31548            if(!dlg){
31549                 dlg = new Roo.BasicDialog("x-msg-box", {
31550                     autoCreate : true,
31551                     shadow: true,
31552                     draggable: true,
31553                     resizable:false,
31554                     constraintoviewport:false,
31555                     fixedcenter:true,
31556                     collapsible : false,
31557                     shim:true,
31558                     modal: true,
31559                     width:400, height:100,
31560                     buttonAlign:"center",
31561                     closeClick : function(){
31562                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31563                             handleButton("no");
31564                         }else{
31565                             handleButton("cancel");
31566                         }
31567                     }
31568                 });
31569                 dlg.on("hide", handleHide);
31570                 mask = dlg.mask;
31571                 dlg.addKeyListener(27, handleEsc);
31572                 buttons = {};
31573                 var bt = this.buttonText;
31574                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31575                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31576                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31577                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31578                 bodyEl = dlg.body.createChild({
31579
31580                     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>'
31581                 });
31582                 msgEl = bodyEl.dom.firstChild;
31583                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31584                 textboxEl.enableDisplayMode();
31585                 textboxEl.addKeyListener([10,13], function(){
31586                     if(dlg.isVisible() && opt && opt.buttons){
31587                         if(opt.buttons.ok){
31588                             handleButton("ok");
31589                         }else if(opt.buttons.yes){
31590                             handleButton("yes");
31591                         }
31592                     }
31593                 });
31594                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31595                 textareaEl.enableDisplayMode();
31596                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31597                 progressEl.enableDisplayMode();
31598                 var pf = progressEl.dom.firstChild;
31599                 if (pf) {
31600                     pp = Roo.get(pf.firstChild);
31601                     pp.setHeight(pf.offsetHeight);
31602                 }
31603                 
31604             }
31605             return dlg;
31606         },
31607
31608         /**
31609          * Updates the message box body text
31610          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31611          * the XHTML-compliant non-breaking space character '&amp;#160;')
31612          * @return {Roo.MessageBox} This message box
31613          */
31614         updateText : function(text){
31615             if(!dlg.isVisible() && !opt.width){
31616                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31617             }
31618             msgEl.innerHTML = text || '&#160;';
31619       
31620             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31621             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31622             var w = Math.max(
31623                     Math.min(opt.width || cw , this.maxWidth), 
31624                     Math.max(opt.minWidth || this.minWidth, bwidth)
31625             );
31626             if(opt.prompt){
31627                 activeTextEl.setWidth(w);
31628             }
31629             if(dlg.isVisible()){
31630                 dlg.fixedcenter = false;
31631             }
31632             // to big, make it scroll. = But as usual stupid IE does not support
31633             // !important..
31634             
31635             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31636                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31637                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31638             } else {
31639                 bodyEl.dom.style.height = '';
31640                 bodyEl.dom.style.overflowY = '';
31641             }
31642             if (cw > w) {
31643                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31644             } else {
31645                 bodyEl.dom.style.overflowX = '';
31646             }
31647             
31648             dlg.setContentSize(w, bodyEl.getHeight());
31649             if(dlg.isVisible()){
31650                 dlg.fixedcenter = true;
31651             }
31652             return this;
31653         },
31654
31655         /**
31656          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31657          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31658          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31659          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31660          * @return {Roo.MessageBox} This message box
31661          */
31662         updateProgress : function(value, text){
31663             if(text){
31664                 this.updateText(text);
31665             }
31666             if (pp) { // weird bug on my firefox - for some reason this is not defined
31667                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31668             }
31669             return this;
31670         },        
31671
31672         /**
31673          * Returns true if the message box is currently displayed
31674          * @return {Boolean} True if the message box is visible, else false
31675          */
31676         isVisible : function(){
31677             return dlg && dlg.isVisible();  
31678         },
31679
31680         /**
31681          * Hides the message box if it is displayed
31682          */
31683         hide : function(){
31684             if(this.isVisible()){
31685                 dlg.hide();
31686             }  
31687         },
31688
31689         /**
31690          * Displays a new message box, or reinitializes an existing message box, based on the config options
31691          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31692          * The following config object properties are supported:
31693          * <pre>
31694 Property    Type             Description
31695 ----------  ---------------  ------------------------------------------------------------------------------------
31696 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31697                                    closes (defaults to undefined)
31698 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31699                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31700 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31701                                    progress and wait dialogs will ignore this property and always hide the
31702                                    close button as they can only be closed programmatically.
31703 cls               String           A custom CSS class to apply to the message box element
31704 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31705                                    displayed (defaults to 75)
31706 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31707                                    function will be btn (the name of the button that was clicked, if applicable,
31708                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31709                                    Progress and wait dialogs will ignore this option since they do not respond to
31710                                    user actions and can only be closed programmatically, so any required function
31711                                    should be called by the same code after it closes the dialog.
31712 icon              String           A CSS class that provides a background image to be used as an icon for
31713                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31714 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31715 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31716 modal             Boolean          False to allow user interaction with the page while the message box is
31717                                    displayed (defaults to true)
31718 msg               String           A string that will replace the existing message box body text (defaults
31719                                    to the XHTML-compliant non-breaking space character '&#160;')
31720 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31721 progress          Boolean          True to display a progress bar (defaults to false)
31722 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31723 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31724 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31725 title             String           The title text
31726 value             String           The string value to set into the active textbox element if displayed
31727 wait              Boolean          True to display a progress bar (defaults to false)
31728 width             Number           The width of the dialog in pixels
31729 </pre>
31730          *
31731          * Example usage:
31732          * <pre><code>
31733 Roo.Msg.show({
31734    title: 'Address',
31735    msg: 'Please enter your address:',
31736    width: 300,
31737    buttons: Roo.MessageBox.OKCANCEL,
31738    multiline: true,
31739    fn: saveAddress,
31740    animEl: 'addAddressBtn'
31741 });
31742 </code></pre>
31743          * @param {Object} config Configuration options
31744          * @return {Roo.MessageBox} This message box
31745          */
31746         show : function(options)
31747         {
31748             
31749             // this causes nightmares if you show one dialog after another
31750             // especially on callbacks..
31751              
31752             if(this.isVisible()){
31753                 
31754                 this.hide();
31755                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31756                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31757                 Roo.log("New Dialog Message:" +  options.msg )
31758                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31759                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31760                 
31761             }
31762             var d = this.getDialog();
31763             opt = options;
31764             d.setTitle(opt.title || "&#160;");
31765             d.close.setDisplayed(opt.closable !== false);
31766             activeTextEl = textboxEl;
31767             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31768             if(opt.prompt){
31769                 if(opt.multiline){
31770                     textboxEl.hide();
31771                     textareaEl.show();
31772                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31773                         opt.multiline : this.defaultTextHeight);
31774                     activeTextEl = textareaEl;
31775                 }else{
31776                     textboxEl.show();
31777                     textareaEl.hide();
31778                 }
31779             }else{
31780                 textboxEl.hide();
31781                 textareaEl.hide();
31782             }
31783             progressEl.setDisplayed(opt.progress === true);
31784             this.updateProgress(0);
31785             activeTextEl.dom.value = opt.value || "";
31786             if(opt.prompt){
31787                 dlg.setDefaultButton(activeTextEl);
31788             }else{
31789                 var bs = opt.buttons;
31790                 var db = null;
31791                 if(bs && bs.ok){
31792                     db = buttons["ok"];
31793                 }else if(bs && bs.yes){
31794                     db = buttons["yes"];
31795                 }
31796                 dlg.setDefaultButton(db);
31797             }
31798             bwidth = updateButtons(opt.buttons);
31799             this.updateText(opt.msg);
31800             if(opt.cls){
31801                 d.el.addClass(opt.cls);
31802             }
31803             d.proxyDrag = opt.proxyDrag === true;
31804             d.modal = opt.modal !== false;
31805             d.mask = opt.modal !== false ? mask : false;
31806             if(!d.isVisible()){
31807                 // force it to the end of the z-index stack so it gets a cursor in FF
31808                 document.body.appendChild(dlg.el.dom);
31809                 d.animateTarget = null;
31810                 d.show(options.animEl);
31811             }
31812             return this;
31813         },
31814
31815         /**
31816          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31817          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31818          * and closing the message box when the process is complete.
31819          * @param {String} title The title bar text
31820          * @param {String} msg The message box body text
31821          * @return {Roo.MessageBox} This message box
31822          */
31823         progress : function(title, msg){
31824             this.show({
31825                 title : title,
31826                 msg : msg,
31827                 buttons: false,
31828                 progress:true,
31829                 closable:false,
31830                 minWidth: this.minProgressWidth,
31831                 modal : true
31832             });
31833             return this;
31834         },
31835
31836         /**
31837          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31838          * If a callback function is passed it will be called after the user clicks the button, and the
31839          * id of the button that was clicked will be passed as the only parameter to the callback
31840          * (could also be the top-right close button).
31841          * @param {String} title The title bar text
31842          * @param {String} msg The message box body text
31843          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31844          * @param {Object} scope (optional) The scope of the callback function
31845          * @return {Roo.MessageBox} This message box
31846          */
31847         alert : function(title, msg, fn, scope){
31848             this.show({
31849                 title : title,
31850                 msg : msg,
31851                 buttons: this.OK,
31852                 fn: fn,
31853                 scope : scope,
31854                 modal : true
31855             });
31856             return this;
31857         },
31858
31859         /**
31860          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31861          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31862          * You are responsible for closing the message box when the process is complete.
31863          * @param {String} msg The message box body text
31864          * @param {String} title (optional) The title bar text
31865          * @return {Roo.MessageBox} This message box
31866          */
31867         wait : function(msg, title){
31868             this.show({
31869                 title : title,
31870                 msg : msg,
31871                 buttons: false,
31872                 closable:false,
31873                 progress:true,
31874                 modal:true,
31875                 width:300,
31876                 wait:true
31877             });
31878             waitTimer = Roo.TaskMgr.start({
31879                 run: function(i){
31880                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31881                 },
31882                 interval: 1000
31883             });
31884             return this;
31885         },
31886
31887         /**
31888          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31889          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31890          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31891          * @param {String} title The title bar text
31892          * @param {String} msg The message box body text
31893          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31894          * @param {Object} scope (optional) The scope of the callback function
31895          * @return {Roo.MessageBox} This message box
31896          */
31897         confirm : function(title, msg, fn, scope){
31898             this.show({
31899                 title : title,
31900                 msg : msg,
31901                 buttons: this.YESNO,
31902                 fn: fn,
31903                 scope : scope,
31904                 modal : true
31905             });
31906             return this;
31907         },
31908
31909         /**
31910          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31911          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31912          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31913          * (could also be the top-right close button) and the text that was entered will be passed as the two
31914          * parameters to the callback.
31915          * @param {String} title The title bar text
31916          * @param {String} msg The message box body text
31917          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31918          * @param {Object} scope (optional) The scope of the callback function
31919          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31920          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31921          * @return {Roo.MessageBox} This message box
31922          */
31923         prompt : function(title, msg, fn, scope, multiline){
31924             this.show({
31925                 title : title,
31926                 msg : msg,
31927                 buttons: this.OKCANCEL,
31928                 fn: fn,
31929                 minWidth:250,
31930                 scope : scope,
31931                 prompt:true,
31932                 multiline: multiline,
31933                 modal : true
31934             });
31935             return this;
31936         },
31937
31938         /**
31939          * Button config that displays a single OK button
31940          * @type Object
31941          */
31942         OK : {ok:true},
31943         /**
31944          * Button config that displays Yes and No buttons
31945          * @type Object
31946          */
31947         YESNO : {yes:true, no:true},
31948         /**
31949          * Button config that displays OK and Cancel buttons
31950          * @type Object
31951          */
31952         OKCANCEL : {ok:true, cancel:true},
31953         /**
31954          * Button config that displays Yes, No and Cancel buttons
31955          * @type Object
31956          */
31957         YESNOCANCEL : {yes:true, no:true, cancel:true},
31958
31959         /**
31960          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31961          * @type Number
31962          */
31963         defaultTextHeight : 75,
31964         /**
31965          * The maximum width in pixels of the message box (defaults to 600)
31966          * @type Number
31967          */
31968         maxWidth : 600,
31969         /**
31970          * The minimum width in pixels of the message box (defaults to 100)
31971          * @type Number
31972          */
31973         minWidth : 100,
31974         /**
31975          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31976          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31977          * @type Number
31978          */
31979         minProgressWidth : 250,
31980         /**
31981          * An object containing the default button text strings that can be overriden for localized language support.
31982          * Supported properties are: ok, cancel, yes and no.
31983          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31984          * @type Object
31985          */
31986         buttonText : {
31987             ok : "OK",
31988             cancel : "Cancel",
31989             yes : "Yes",
31990             no : "No"
31991         }
31992     };
31993 }();
31994
31995 /**
31996  * Shorthand for {@link Roo.MessageBox}
31997  */
31998 Roo.Msg = Roo.MessageBox;/*
31999  * Based on:
32000  * Ext JS Library 1.1.1
32001  * Copyright(c) 2006-2007, Ext JS, LLC.
32002  *
32003  * Originally Released Under LGPL - original licence link has changed is not relivant.
32004  *
32005  * Fork - LGPL
32006  * <script type="text/javascript">
32007  */
32008 /**
32009  * @class Roo.QuickTips
32010  * Provides attractive and customizable tooltips for any element.
32011  * @singleton
32012  */
32013 Roo.QuickTips = function(){
32014     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
32015     var ce, bd, xy, dd;
32016     var visible = false, disabled = true, inited = false;
32017     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
32018     
32019     var onOver = function(e){
32020         if(disabled){
32021             return;
32022         }
32023         var t = e.getTarget();
32024         if(!t || t.nodeType !== 1 || t == document || t == document.body){
32025             return;
32026         }
32027         if(ce && t == ce.el){
32028             clearTimeout(hideProc);
32029             return;
32030         }
32031         if(t && tagEls[t.id]){
32032             tagEls[t.id].el = t;
32033             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
32034             return;
32035         }
32036         var ttp, et = Roo.fly(t);
32037         var ns = cfg.namespace;
32038         if(tm.interceptTitles && t.title){
32039             ttp = t.title;
32040             t.qtip = ttp;
32041             t.removeAttribute("title");
32042             e.preventDefault();
32043         }else{
32044             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
32045         }
32046         if(ttp){
32047             showProc = show.defer(tm.showDelay, tm, [{
32048                 el: t, 
32049                 text: ttp, 
32050                 width: et.getAttributeNS(ns, cfg.width),
32051                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
32052                 title: et.getAttributeNS(ns, cfg.title),
32053                     cls: et.getAttributeNS(ns, cfg.cls)
32054             }]);
32055         }
32056     };
32057     
32058     var onOut = function(e){
32059         clearTimeout(showProc);
32060         var t = e.getTarget();
32061         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
32062             hideProc = setTimeout(hide, tm.hideDelay);
32063         }
32064     };
32065     
32066     var onMove = function(e){
32067         if(disabled){
32068             return;
32069         }
32070         xy = e.getXY();
32071         xy[1] += 18;
32072         if(tm.trackMouse && ce){
32073             el.setXY(xy);
32074         }
32075     };
32076     
32077     var onDown = function(e){
32078         clearTimeout(showProc);
32079         clearTimeout(hideProc);
32080         if(!e.within(el)){
32081             if(tm.hideOnClick){
32082                 hide();
32083                 tm.disable();
32084                 tm.enable.defer(100, tm);
32085             }
32086         }
32087     };
32088     
32089     var getPad = function(){
32090         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
32091     };
32092
32093     var show = function(o){
32094         if(disabled){
32095             return;
32096         }
32097         clearTimeout(dismissProc);
32098         ce = o;
32099         if(removeCls){ // in case manually hidden
32100             el.removeClass(removeCls);
32101             removeCls = null;
32102         }
32103         if(ce.cls){
32104             el.addClass(ce.cls);
32105             removeCls = ce.cls;
32106         }
32107         if(ce.title){
32108             tipTitle.update(ce.title);
32109             tipTitle.show();
32110         }else{
32111             tipTitle.update('');
32112             tipTitle.hide();
32113         }
32114         el.dom.style.width  = tm.maxWidth+'px';
32115         //tipBody.dom.style.width = '';
32116         tipBodyText.update(o.text);
32117         var p = getPad(), w = ce.width;
32118         if(!w){
32119             var td = tipBodyText.dom;
32120             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
32121             if(aw > tm.maxWidth){
32122                 w = tm.maxWidth;
32123             }else if(aw < tm.minWidth){
32124                 w = tm.minWidth;
32125             }else{
32126                 w = aw;
32127             }
32128         }
32129         //tipBody.setWidth(w);
32130         el.setWidth(parseInt(w, 10) + p);
32131         if(ce.autoHide === false){
32132             close.setDisplayed(true);
32133             if(dd){
32134                 dd.unlock();
32135             }
32136         }else{
32137             close.setDisplayed(false);
32138             if(dd){
32139                 dd.lock();
32140             }
32141         }
32142         if(xy){
32143             el.avoidY = xy[1]-18;
32144             el.setXY(xy);
32145         }
32146         if(tm.animate){
32147             el.setOpacity(.1);
32148             el.setStyle("visibility", "visible");
32149             el.fadeIn({callback: afterShow});
32150         }else{
32151             afterShow();
32152         }
32153     };
32154     
32155     var afterShow = function(){
32156         if(ce){
32157             el.show();
32158             esc.enable();
32159             if(tm.autoDismiss && ce.autoHide !== false){
32160                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
32161             }
32162         }
32163     };
32164     
32165     var hide = function(noanim){
32166         clearTimeout(dismissProc);
32167         clearTimeout(hideProc);
32168         ce = null;
32169         if(el.isVisible()){
32170             esc.disable();
32171             if(noanim !== true && tm.animate){
32172                 el.fadeOut({callback: afterHide});
32173             }else{
32174                 afterHide();
32175             } 
32176         }
32177     };
32178     
32179     var afterHide = function(){
32180         el.hide();
32181         if(removeCls){
32182             el.removeClass(removeCls);
32183             removeCls = null;
32184         }
32185     };
32186     
32187     return {
32188         /**
32189         * @cfg {Number} minWidth
32190         * The minimum width of the quick tip (defaults to 40)
32191         */
32192        minWidth : 40,
32193         /**
32194         * @cfg {Number} maxWidth
32195         * The maximum width of the quick tip (defaults to 300)
32196         */
32197        maxWidth : 300,
32198         /**
32199         * @cfg {Boolean} interceptTitles
32200         * True to automatically use the element's DOM title value if available (defaults to false)
32201         */
32202        interceptTitles : false,
32203         /**
32204         * @cfg {Boolean} trackMouse
32205         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
32206         */
32207        trackMouse : false,
32208         /**
32209         * @cfg {Boolean} hideOnClick
32210         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
32211         */
32212        hideOnClick : true,
32213         /**
32214         * @cfg {Number} showDelay
32215         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
32216         */
32217        showDelay : 500,
32218         /**
32219         * @cfg {Number} hideDelay
32220         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
32221         */
32222        hideDelay : 200,
32223         /**
32224         * @cfg {Boolean} autoHide
32225         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32226         * Used in conjunction with hideDelay.
32227         */
32228        autoHide : true,
32229         /**
32230         * @cfg {Boolean}
32231         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32232         * (defaults to true).  Used in conjunction with autoDismissDelay.
32233         */
32234        autoDismiss : true,
32235         /**
32236         * @cfg {Number}
32237         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32238         */
32239        autoDismissDelay : 5000,
32240        /**
32241         * @cfg {Boolean} animate
32242         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32243         */
32244        animate : false,
32245
32246        /**
32247         * @cfg {String} title
32248         * Title text to display (defaults to '').  This can be any valid HTML markup.
32249         */
32250         title: '',
32251        /**
32252         * @cfg {String} text
32253         * Body text to display (defaults to '').  This can be any valid HTML markup.
32254         */
32255         text : '',
32256        /**
32257         * @cfg {String} cls
32258         * A CSS class to apply to the base quick tip element (defaults to '').
32259         */
32260         cls : '',
32261        /**
32262         * @cfg {Number} width
32263         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32264         * minWidth or maxWidth.
32265         */
32266         width : null,
32267
32268     /**
32269      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32270      * or display QuickTips in a page.
32271      */
32272        init : function(){
32273           tm = Roo.QuickTips;
32274           cfg = tm.tagConfig;
32275           if(!inited){
32276               if(!Roo.isReady){ // allow calling of init() before onReady
32277                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32278                   return;
32279               }
32280               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32281               el.fxDefaults = {stopFx: true};
32282               // maximum custom styling
32283               //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>');
32284               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>');              
32285               tipTitle = el.child('h3');
32286               tipTitle.enableDisplayMode("block");
32287               tipBody = el.child('div.x-tip-bd');
32288               tipBodyText = el.child('div.x-tip-bd-inner');
32289               //bdLeft = el.child('div.x-tip-bd-left');
32290               //bdRight = el.child('div.x-tip-bd-right');
32291               close = el.child('div.x-tip-close');
32292               close.enableDisplayMode("block");
32293               close.on("click", hide);
32294               var d = Roo.get(document);
32295               d.on("mousedown", onDown);
32296               d.on("mouseover", onOver);
32297               d.on("mouseout", onOut);
32298               d.on("mousemove", onMove);
32299               esc = d.addKeyListener(27, hide);
32300               esc.disable();
32301               if(Roo.dd.DD){
32302                   dd = el.initDD("default", null, {
32303                       onDrag : function(){
32304                           el.sync();  
32305                       }
32306                   });
32307                   dd.setHandleElId(tipTitle.id);
32308                   dd.lock();
32309               }
32310               inited = true;
32311           }
32312           this.enable(); 
32313        },
32314
32315     /**
32316      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32317      * are supported:
32318      * <pre>
32319 Property    Type                   Description
32320 ----------  ---------------------  ------------------------------------------------------------------------
32321 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32322      * </ul>
32323      * @param {Object} config The config object
32324      */
32325        register : function(config){
32326            var cs = config instanceof Array ? config : arguments;
32327            for(var i = 0, len = cs.length; i < len; i++) {
32328                var c = cs[i];
32329                var target = c.target;
32330                if(target){
32331                    if(target instanceof Array){
32332                        for(var j = 0, jlen = target.length; j < jlen; j++){
32333                            tagEls[target[j]] = c;
32334                        }
32335                    }else{
32336                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32337                    }
32338                }
32339            }
32340        },
32341
32342     /**
32343      * Removes this quick tip from its element and destroys it.
32344      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32345      */
32346        unregister : function(el){
32347            delete tagEls[Roo.id(el)];
32348        },
32349
32350     /**
32351      * Enable this quick tip.
32352      */
32353        enable : function(){
32354            if(inited && disabled){
32355                locks.pop();
32356                if(locks.length < 1){
32357                    disabled = false;
32358                }
32359            }
32360        },
32361
32362     /**
32363      * Disable this quick tip.
32364      */
32365        disable : function(){
32366           disabled = true;
32367           clearTimeout(showProc);
32368           clearTimeout(hideProc);
32369           clearTimeout(dismissProc);
32370           if(ce){
32371               hide(true);
32372           }
32373           locks.push(1);
32374        },
32375
32376     /**
32377      * Returns true if the quick tip is enabled, else false.
32378      */
32379        isEnabled : function(){
32380             return !disabled;
32381        },
32382
32383         // private
32384        tagConfig : {
32385            namespace : "ext",
32386            attribute : "qtip",
32387            width : "width",
32388            target : "target",
32389            title : "qtitle",
32390            hide : "hide",
32391            cls : "qclass"
32392        }
32393    };
32394 }();
32395
32396 // backwards compat
32397 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32398  * Based on:
32399  * Ext JS Library 1.1.1
32400  * Copyright(c) 2006-2007, Ext JS, LLC.
32401  *
32402  * Originally Released Under LGPL - original licence link has changed is not relivant.
32403  *
32404  * Fork - LGPL
32405  * <script type="text/javascript">
32406  */
32407  
32408
32409 /**
32410  * @class Roo.tree.TreePanel
32411  * @extends Roo.data.Tree
32412
32413  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32414  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32415  * @cfg {Boolean} enableDD true to enable drag and drop
32416  * @cfg {Boolean} enableDrag true to enable just drag
32417  * @cfg {Boolean} enableDrop true to enable just drop
32418  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32419  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32420  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32421  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32422  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32423  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32424  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32425  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32426  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32427  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32428  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32429  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32430  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32431  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32432  * @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>
32433  * @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>
32434  * 
32435  * @constructor
32436  * @param {String/HTMLElement/Element} el The container element
32437  * @param {Object} config
32438  */
32439 Roo.tree.TreePanel = function(el, config){
32440     var root = false;
32441     var loader = false;
32442     if (config.root) {
32443         root = config.root;
32444         delete config.root;
32445     }
32446     if (config.loader) {
32447         loader = config.loader;
32448         delete config.loader;
32449     }
32450     
32451     Roo.apply(this, config);
32452     Roo.tree.TreePanel.superclass.constructor.call(this);
32453     this.el = Roo.get(el);
32454     this.el.addClass('x-tree');
32455     //console.log(root);
32456     if (root) {
32457         this.setRootNode( Roo.factory(root, Roo.tree));
32458     }
32459     if (loader) {
32460         this.loader = Roo.factory(loader, Roo.tree);
32461     }
32462    /**
32463     * Read-only. The id of the container element becomes this TreePanel's id.
32464     */
32465     this.id = this.el.id;
32466     this.addEvents({
32467         /**
32468         * @event beforeload
32469         * Fires before a node is loaded, return false to cancel
32470         * @param {Node} node The node being loaded
32471         */
32472         "beforeload" : true,
32473         /**
32474         * @event load
32475         * Fires when a node is loaded
32476         * @param {Node} node The node that was loaded
32477         */
32478         "load" : true,
32479         /**
32480         * @event textchange
32481         * Fires when the text for a node is changed
32482         * @param {Node} node The node
32483         * @param {String} text The new text
32484         * @param {String} oldText The old text
32485         */
32486         "textchange" : true,
32487         /**
32488         * @event beforeexpand
32489         * Fires before a node is expanded, return false to cancel.
32490         * @param {Node} node The node
32491         * @param {Boolean} deep
32492         * @param {Boolean} anim
32493         */
32494         "beforeexpand" : true,
32495         /**
32496         * @event beforecollapse
32497         * Fires before a node is collapsed, return false to cancel.
32498         * @param {Node} node The node
32499         * @param {Boolean} deep
32500         * @param {Boolean} anim
32501         */
32502         "beforecollapse" : true,
32503         /**
32504         * @event expand
32505         * Fires when a node is expanded
32506         * @param {Node} node The node
32507         */
32508         "expand" : true,
32509         /**
32510         * @event disabledchange
32511         * Fires when the disabled status of a node changes
32512         * @param {Node} node The node
32513         * @param {Boolean} disabled
32514         */
32515         "disabledchange" : true,
32516         /**
32517         * @event collapse
32518         * Fires when a node is collapsed
32519         * @param {Node} node The node
32520         */
32521         "collapse" : true,
32522         /**
32523         * @event beforeclick
32524         * Fires before click processing on a node. Return false to cancel the default action.
32525         * @param {Node} node The node
32526         * @param {Roo.EventObject} e The event object
32527         */
32528         "beforeclick":true,
32529         /**
32530         * @event checkchange
32531         * Fires when a node with a checkbox's checked property changes
32532         * @param {Node} this This node
32533         * @param {Boolean} checked
32534         */
32535         "checkchange":true,
32536         /**
32537         * @event click
32538         * Fires when a node is clicked
32539         * @param {Node} node The node
32540         * @param {Roo.EventObject} e The event object
32541         */
32542         "click":true,
32543         /**
32544         * @event dblclick
32545         * Fires when a node is double clicked
32546         * @param {Node} node The node
32547         * @param {Roo.EventObject} e The event object
32548         */
32549         "dblclick":true,
32550         /**
32551         * @event contextmenu
32552         * Fires when a node is right clicked
32553         * @param {Node} node The node
32554         * @param {Roo.EventObject} e The event object
32555         */
32556         "contextmenu":true,
32557         /**
32558         * @event beforechildrenrendered
32559         * Fires right before the child nodes for a node are rendered
32560         * @param {Node} node The node
32561         */
32562         "beforechildrenrendered":true,
32563         /**
32564         * @event startdrag
32565         * Fires when a node starts being dragged
32566         * @param {Roo.tree.TreePanel} this
32567         * @param {Roo.tree.TreeNode} node
32568         * @param {event} e The raw browser event
32569         */ 
32570        "startdrag" : true,
32571        /**
32572         * @event enddrag
32573         * Fires when a drag operation is complete
32574         * @param {Roo.tree.TreePanel} this
32575         * @param {Roo.tree.TreeNode} node
32576         * @param {event} e The raw browser event
32577         */
32578        "enddrag" : true,
32579        /**
32580         * @event dragdrop
32581         * Fires when a dragged node is dropped on a valid DD target
32582         * @param {Roo.tree.TreePanel} this
32583         * @param {Roo.tree.TreeNode} node
32584         * @param {DD} dd The dd it was dropped on
32585         * @param {event} e The raw browser event
32586         */
32587        "dragdrop" : true,
32588        /**
32589         * @event beforenodedrop
32590         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32591         * passed to handlers has the following properties:<br />
32592         * <ul style="padding:5px;padding-left:16px;">
32593         * <li>tree - The TreePanel</li>
32594         * <li>target - The node being targeted for the drop</li>
32595         * <li>data - The drag data from the drag source</li>
32596         * <li>point - The point of the drop - append, above or below</li>
32597         * <li>source - The drag source</li>
32598         * <li>rawEvent - Raw mouse event</li>
32599         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32600         * to be inserted by setting them on this object.</li>
32601         * <li>cancel - Set this to true to cancel the drop.</li>
32602         * </ul>
32603         * @param {Object} dropEvent
32604         */
32605        "beforenodedrop" : true,
32606        /**
32607         * @event nodedrop
32608         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32609         * passed to handlers has the following properties:<br />
32610         * <ul style="padding:5px;padding-left:16px;">
32611         * <li>tree - The TreePanel</li>
32612         * <li>target - The node being targeted for the drop</li>
32613         * <li>data - The drag data from the drag source</li>
32614         * <li>point - The point of the drop - append, above or below</li>
32615         * <li>source - The drag source</li>
32616         * <li>rawEvent - Raw mouse event</li>
32617         * <li>dropNode - Dropped node(s).</li>
32618         * </ul>
32619         * @param {Object} dropEvent
32620         */
32621        "nodedrop" : true,
32622         /**
32623         * @event nodedragover
32624         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32625         * passed to handlers has the following properties:<br />
32626         * <ul style="padding:5px;padding-left:16px;">
32627         * <li>tree - The TreePanel</li>
32628         * <li>target - The node being targeted for the drop</li>
32629         * <li>data - The drag data from the drag source</li>
32630         * <li>point - The point of the drop - append, above or below</li>
32631         * <li>source - The drag source</li>
32632         * <li>rawEvent - Raw mouse event</li>
32633         * <li>dropNode - Drop node(s) provided by the source.</li>
32634         * <li>cancel - Set this to true to signal drop not allowed.</li>
32635         * </ul>
32636         * @param {Object} dragOverEvent
32637         */
32638        "nodedragover" : true
32639         
32640     });
32641     if(this.singleExpand){
32642        this.on("beforeexpand", this.restrictExpand, this);
32643     }
32644     if (this.editor) {
32645         this.editor.tree = this;
32646         this.editor = Roo.factory(this.editor, Roo.tree);
32647     }
32648     
32649     if (this.selModel) {
32650         this.selModel = Roo.factory(this.selModel, Roo.tree);
32651     }
32652    
32653 };
32654 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32655     rootVisible : true,
32656     animate: Roo.enableFx,
32657     lines : true,
32658     enableDD : false,
32659     hlDrop : Roo.enableFx,
32660   
32661     renderer: false,
32662     
32663     rendererTip: false,
32664     // private
32665     restrictExpand : function(node){
32666         var p = node.parentNode;
32667         if(p){
32668             if(p.expandedChild && p.expandedChild.parentNode == p){
32669                 p.expandedChild.collapse();
32670             }
32671             p.expandedChild = node;
32672         }
32673     },
32674
32675     // private override
32676     setRootNode : function(node){
32677         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32678         if(!this.rootVisible){
32679             node.ui = new Roo.tree.RootTreeNodeUI(node);
32680         }
32681         return node;
32682     },
32683
32684     /**
32685      * Returns the container element for this TreePanel
32686      */
32687     getEl : function(){
32688         return this.el;
32689     },
32690
32691     /**
32692      * Returns the default TreeLoader for this TreePanel
32693      */
32694     getLoader : function(){
32695         return this.loader;
32696     },
32697
32698     /**
32699      * Expand all nodes
32700      */
32701     expandAll : function(){
32702         this.root.expand(true);
32703     },
32704
32705     /**
32706      * Collapse all nodes
32707      */
32708     collapseAll : function(){
32709         this.root.collapse(true);
32710     },
32711
32712     /**
32713      * Returns the selection model used by this TreePanel
32714      */
32715     getSelectionModel : function(){
32716         if(!this.selModel){
32717             this.selModel = new Roo.tree.DefaultSelectionModel();
32718         }
32719         return this.selModel;
32720     },
32721
32722     /**
32723      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32724      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32725      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32726      * @return {Array}
32727      */
32728     getChecked : function(a, startNode){
32729         startNode = startNode || this.root;
32730         var r = [];
32731         var f = function(){
32732             if(this.attributes.checked){
32733                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32734             }
32735         }
32736         startNode.cascade(f);
32737         return r;
32738     },
32739
32740     /**
32741      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32742      * @param {String} path
32743      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32744      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32745      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32746      */
32747     expandPath : function(path, attr, callback){
32748         attr = attr || "id";
32749         var keys = path.split(this.pathSeparator);
32750         var curNode = this.root;
32751         if(curNode.attributes[attr] != keys[1]){ // invalid root
32752             if(callback){
32753                 callback(false, null);
32754             }
32755             return;
32756         }
32757         var index = 1;
32758         var f = function(){
32759             if(++index == keys.length){
32760                 if(callback){
32761                     callback(true, curNode);
32762                 }
32763                 return;
32764             }
32765             var c = curNode.findChild(attr, keys[index]);
32766             if(!c){
32767                 if(callback){
32768                     callback(false, curNode);
32769                 }
32770                 return;
32771             }
32772             curNode = c;
32773             c.expand(false, false, f);
32774         };
32775         curNode.expand(false, false, f);
32776     },
32777
32778     /**
32779      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32780      * @param {String} path
32781      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32782      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32783      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32784      */
32785     selectPath : function(path, attr, callback){
32786         attr = attr || "id";
32787         var keys = path.split(this.pathSeparator);
32788         var v = keys.pop();
32789         if(keys.length > 0){
32790             var f = function(success, node){
32791                 if(success && node){
32792                     var n = node.findChild(attr, v);
32793                     if(n){
32794                         n.select();
32795                         if(callback){
32796                             callback(true, n);
32797                         }
32798                     }else if(callback){
32799                         callback(false, n);
32800                     }
32801                 }else{
32802                     if(callback){
32803                         callback(false, n);
32804                     }
32805                 }
32806             };
32807             this.expandPath(keys.join(this.pathSeparator), attr, f);
32808         }else{
32809             this.root.select();
32810             if(callback){
32811                 callback(true, this.root);
32812             }
32813         }
32814     },
32815
32816     getTreeEl : function(){
32817         return this.el;
32818     },
32819
32820     /**
32821      * Trigger rendering of this TreePanel
32822      */
32823     render : function(){
32824         if (this.innerCt) {
32825             return this; // stop it rendering more than once!!
32826         }
32827         
32828         this.innerCt = this.el.createChild({tag:"ul",
32829                cls:"x-tree-root-ct " +
32830                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32831
32832         if(this.containerScroll){
32833             Roo.dd.ScrollManager.register(this.el);
32834         }
32835         if((this.enableDD || this.enableDrop) && !this.dropZone){
32836            /**
32837             * The dropZone used by this tree if drop is enabled
32838             * @type Roo.tree.TreeDropZone
32839             */
32840              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32841                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32842            });
32843         }
32844         if((this.enableDD || this.enableDrag) && !this.dragZone){
32845            /**
32846             * The dragZone used by this tree if drag is enabled
32847             * @type Roo.tree.TreeDragZone
32848             */
32849             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32850                ddGroup: this.ddGroup || "TreeDD",
32851                scroll: this.ddScroll
32852            });
32853         }
32854         this.getSelectionModel().init(this);
32855         if (!this.root) {
32856             Roo.log("ROOT not set in tree");
32857             return this;
32858         }
32859         this.root.render();
32860         if(!this.rootVisible){
32861             this.root.renderChildren();
32862         }
32863         return this;
32864     }
32865 });/*
32866  * Based on:
32867  * Ext JS Library 1.1.1
32868  * Copyright(c) 2006-2007, Ext JS, LLC.
32869  *
32870  * Originally Released Under LGPL - original licence link has changed is not relivant.
32871  *
32872  * Fork - LGPL
32873  * <script type="text/javascript">
32874  */
32875  
32876
32877 /**
32878  * @class Roo.tree.DefaultSelectionModel
32879  * @extends Roo.util.Observable
32880  * The default single selection for a TreePanel.
32881  * @param {Object} cfg Configuration
32882  */
32883 Roo.tree.DefaultSelectionModel = function(cfg){
32884    this.selNode = null;
32885    
32886    
32887    
32888    this.addEvents({
32889        /**
32890         * @event selectionchange
32891         * Fires when the selected node changes
32892         * @param {DefaultSelectionModel} this
32893         * @param {TreeNode} node the new selection
32894         */
32895        "selectionchange" : true,
32896
32897        /**
32898         * @event beforeselect
32899         * Fires before the selected node changes, return false to cancel the change
32900         * @param {DefaultSelectionModel} this
32901         * @param {TreeNode} node the new selection
32902         * @param {TreeNode} node the old selection
32903         */
32904        "beforeselect" : true
32905    });
32906    
32907     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32908 };
32909
32910 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32911     init : function(tree){
32912         this.tree = tree;
32913         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32914         tree.on("click", this.onNodeClick, this);
32915     },
32916     
32917     onNodeClick : function(node, e){
32918         if (e.ctrlKey && this.selNode == node)  {
32919             this.unselect(node);
32920             return;
32921         }
32922         this.select(node);
32923     },
32924     
32925     /**
32926      * Select a node.
32927      * @param {TreeNode} node The node to select
32928      * @return {TreeNode} The selected node
32929      */
32930     select : function(node){
32931         var last = this.selNode;
32932         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32933             if(last){
32934                 last.ui.onSelectedChange(false);
32935             }
32936             this.selNode = node;
32937             node.ui.onSelectedChange(true);
32938             this.fireEvent("selectionchange", this, node, last);
32939         }
32940         return node;
32941     },
32942     
32943     /**
32944      * Deselect a node.
32945      * @param {TreeNode} node The node to unselect
32946      */
32947     unselect : function(node){
32948         if(this.selNode == node){
32949             this.clearSelections();
32950         }    
32951     },
32952     
32953     /**
32954      * Clear all selections
32955      */
32956     clearSelections : function(){
32957         var n = this.selNode;
32958         if(n){
32959             n.ui.onSelectedChange(false);
32960             this.selNode = null;
32961             this.fireEvent("selectionchange", this, null);
32962         }
32963         return n;
32964     },
32965     
32966     /**
32967      * Get the selected node
32968      * @return {TreeNode} The selected node
32969      */
32970     getSelectedNode : function(){
32971         return this.selNode;    
32972     },
32973     
32974     /**
32975      * Returns true if the node is selected
32976      * @param {TreeNode} node The node to check
32977      * @return {Boolean}
32978      */
32979     isSelected : function(node){
32980         return this.selNode == node;  
32981     },
32982
32983     /**
32984      * Selects the node above the selected node in the tree, intelligently walking the nodes
32985      * @return TreeNode The new selection
32986      */
32987     selectPrevious : function(){
32988         var s = this.selNode || this.lastSelNode;
32989         if(!s){
32990             return null;
32991         }
32992         var ps = s.previousSibling;
32993         if(ps){
32994             if(!ps.isExpanded() || ps.childNodes.length < 1){
32995                 return this.select(ps);
32996             } else{
32997                 var lc = ps.lastChild;
32998                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32999                     lc = lc.lastChild;
33000                 }
33001                 return this.select(lc);
33002             }
33003         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
33004             return this.select(s.parentNode);
33005         }
33006         return null;
33007     },
33008
33009     /**
33010      * Selects the node above the selected node in the tree, intelligently walking the nodes
33011      * @return TreeNode The new selection
33012      */
33013     selectNext : function(){
33014         var s = this.selNode || this.lastSelNode;
33015         if(!s){
33016             return null;
33017         }
33018         if(s.firstChild && s.isExpanded()){
33019              return this.select(s.firstChild);
33020          }else if(s.nextSibling){
33021              return this.select(s.nextSibling);
33022          }else if(s.parentNode){
33023             var newS = null;
33024             s.parentNode.bubble(function(){
33025                 if(this.nextSibling){
33026                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
33027                     return false;
33028                 }
33029             });
33030             return newS;
33031          }
33032         return null;
33033     },
33034
33035     onKeyDown : function(e){
33036         var s = this.selNode || this.lastSelNode;
33037         // undesirable, but required
33038         var sm = this;
33039         if(!s){
33040             return;
33041         }
33042         var k = e.getKey();
33043         switch(k){
33044              case e.DOWN:
33045                  e.stopEvent();
33046                  this.selectNext();
33047              break;
33048              case e.UP:
33049                  e.stopEvent();
33050                  this.selectPrevious();
33051              break;
33052              case e.RIGHT:
33053                  e.preventDefault();
33054                  if(s.hasChildNodes()){
33055                      if(!s.isExpanded()){
33056                          s.expand();
33057                      }else if(s.firstChild){
33058                          this.select(s.firstChild, e);
33059                      }
33060                  }
33061              break;
33062              case e.LEFT:
33063                  e.preventDefault();
33064                  if(s.hasChildNodes() && s.isExpanded()){
33065                      s.collapse();
33066                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
33067                      this.select(s.parentNode, e);
33068                  }
33069              break;
33070         };
33071     }
33072 });
33073
33074 /**
33075  * @class Roo.tree.MultiSelectionModel
33076  * @extends Roo.util.Observable
33077  * Multi selection for a TreePanel.
33078  * @param {Object} cfg Configuration
33079  */
33080 Roo.tree.MultiSelectionModel = function(){
33081    this.selNodes = [];
33082    this.selMap = {};
33083    this.addEvents({
33084        /**
33085         * @event selectionchange
33086         * Fires when the selected nodes change
33087         * @param {MultiSelectionModel} this
33088         * @param {Array} nodes Array of the selected nodes
33089         */
33090        "selectionchange" : true
33091    });
33092    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
33093    
33094 };
33095
33096 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
33097     init : function(tree){
33098         this.tree = tree;
33099         tree.getTreeEl().on("keydown", this.onKeyDown, this);
33100         tree.on("click", this.onNodeClick, this);
33101     },
33102     
33103     onNodeClick : function(node, e){
33104         this.select(node, e, e.ctrlKey);
33105     },
33106     
33107     /**
33108      * Select a node.
33109      * @param {TreeNode} node The node to select
33110      * @param {EventObject} e (optional) An event associated with the selection
33111      * @param {Boolean} keepExisting True to retain existing selections
33112      * @return {TreeNode} The selected node
33113      */
33114     select : function(node, e, keepExisting){
33115         if(keepExisting !== true){
33116             this.clearSelections(true);
33117         }
33118         if(this.isSelected(node)){
33119             this.lastSelNode = node;
33120             return node;
33121         }
33122         this.selNodes.push(node);
33123         this.selMap[node.id] = node;
33124         this.lastSelNode = node;
33125         node.ui.onSelectedChange(true);
33126         this.fireEvent("selectionchange", this, this.selNodes);
33127         return node;
33128     },
33129     
33130     /**
33131      * Deselect a node.
33132      * @param {TreeNode} node The node to unselect
33133      */
33134     unselect : function(node){
33135         if(this.selMap[node.id]){
33136             node.ui.onSelectedChange(false);
33137             var sn = this.selNodes;
33138             var index = -1;
33139             if(sn.indexOf){
33140                 index = sn.indexOf(node);
33141             }else{
33142                 for(var i = 0, len = sn.length; i < len; i++){
33143                     if(sn[i] == node){
33144                         index = i;
33145                         break;
33146                     }
33147                 }
33148             }
33149             if(index != -1){
33150                 this.selNodes.splice(index, 1);
33151             }
33152             delete this.selMap[node.id];
33153             this.fireEvent("selectionchange", this, this.selNodes);
33154         }
33155     },
33156     
33157     /**
33158      * Clear all selections
33159      */
33160     clearSelections : function(suppressEvent){
33161         var sn = this.selNodes;
33162         if(sn.length > 0){
33163             for(var i = 0, len = sn.length; i < len; i++){
33164                 sn[i].ui.onSelectedChange(false);
33165             }
33166             this.selNodes = [];
33167             this.selMap = {};
33168             if(suppressEvent !== true){
33169                 this.fireEvent("selectionchange", this, this.selNodes);
33170             }
33171         }
33172     },
33173     
33174     /**
33175      * Returns true if the node is selected
33176      * @param {TreeNode} node The node to check
33177      * @return {Boolean}
33178      */
33179     isSelected : function(node){
33180         return this.selMap[node.id] ? true : false;  
33181     },
33182     
33183     /**
33184      * Returns an array of the selected nodes
33185      * @return {Array}
33186      */
33187     getSelectedNodes : function(){
33188         return this.selNodes;    
33189     },
33190
33191     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
33192
33193     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
33194
33195     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
33196 });/*
33197  * Based on:
33198  * Ext JS Library 1.1.1
33199  * Copyright(c) 2006-2007, Ext JS, LLC.
33200  *
33201  * Originally Released Under LGPL - original licence link has changed is not relivant.
33202  *
33203  * Fork - LGPL
33204  * <script type="text/javascript">
33205  */
33206  
33207 /**
33208  * @class Roo.tree.TreeNode
33209  * @extends Roo.data.Node
33210  * @cfg {String} text The text for this node
33211  * @cfg {Boolean} expanded true to start the node expanded
33212  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
33213  * @cfg {Boolean} allowDrop false if this node cannot be drop on
33214  * @cfg {Boolean} disabled true to start the node disabled
33215  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
33216  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
33217  * @cfg {String} cls A css class to be added to the node
33218  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
33219  * @cfg {String} href URL of the link used for the node (defaults to #)
33220  * @cfg {String} hrefTarget target frame for the link
33221  * @cfg {String} qtip An Ext QuickTip for the node
33222  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33223  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33224  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33225  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33226  * (defaults to undefined with no checkbox rendered)
33227  * @constructor
33228  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33229  */
33230 Roo.tree.TreeNode = function(attributes){
33231     attributes = attributes || {};
33232     if(typeof attributes == "string"){
33233         attributes = {text: attributes};
33234     }
33235     this.childrenRendered = false;
33236     this.rendered = false;
33237     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33238     this.expanded = attributes.expanded === true;
33239     this.isTarget = attributes.isTarget !== false;
33240     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33241     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33242
33243     /**
33244      * Read-only. The text for this node. To change it use setText().
33245      * @type String
33246      */
33247     this.text = attributes.text;
33248     /**
33249      * True if this node is disabled.
33250      * @type Boolean
33251      */
33252     this.disabled = attributes.disabled === true;
33253
33254     this.addEvents({
33255         /**
33256         * @event textchange
33257         * Fires when the text for this node is changed
33258         * @param {Node} this This node
33259         * @param {String} text The new text
33260         * @param {String} oldText The old text
33261         */
33262         "textchange" : true,
33263         /**
33264         * @event beforeexpand
33265         * Fires before this node is expanded, return false to cancel.
33266         * @param {Node} this This node
33267         * @param {Boolean} deep
33268         * @param {Boolean} anim
33269         */
33270         "beforeexpand" : true,
33271         /**
33272         * @event beforecollapse
33273         * Fires before this node is collapsed, return false to cancel.
33274         * @param {Node} this This node
33275         * @param {Boolean} deep
33276         * @param {Boolean} anim
33277         */
33278         "beforecollapse" : true,
33279         /**
33280         * @event expand
33281         * Fires when this node is expanded
33282         * @param {Node} this This node
33283         */
33284         "expand" : true,
33285         /**
33286         * @event disabledchange
33287         * Fires when the disabled status of this node changes
33288         * @param {Node} this This node
33289         * @param {Boolean} disabled
33290         */
33291         "disabledchange" : true,
33292         /**
33293         * @event collapse
33294         * Fires when this node is collapsed
33295         * @param {Node} this This node
33296         */
33297         "collapse" : true,
33298         /**
33299         * @event beforeclick
33300         * Fires before click processing. Return false to cancel the default action.
33301         * @param {Node} this This node
33302         * @param {Roo.EventObject} e The event object
33303         */
33304         "beforeclick":true,
33305         /**
33306         * @event checkchange
33307         * Fires when a node with a checkbox's checked property changes
33308         * @param {Node} this This node
33309         * @param {Boolean} checked
33310         */
33311         "checkchange":true,
33312         /**
33313         * @event click
33314         * Fires when this node is clicked
33315         * @param {Node} this This node
33316         * @param {Roo.EventObject} e The event object
33317         */
33318         "click":true,
33319         /**
33320         * @event dblclick
33321         * Fires when this node is double clicked
33322         * @param {Node} this This node
33323         * @param {Roo.EventObject} e The event object
33324         */
33325         "dblclick":true,
33326         /**
33327         * @event contextmenu
33328         * Fires when this node is right clicked
33329         * @param {Node} this This node
33330         * @param {Roo.EventObject} e The event object
33331         */
33332         "contextmenu":true,
33333         /**
33334         * @event beforechildrenrendered
33335         * Fires right before the child nodes for this node are rendered
33336         * @param {Node} this This node
33337         */
33338         "beforechildrenrendered":true
33339     });
33340
33341     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33342
33343     /**
33344      * Read-only. The UI for this node
33345      * @type TreeNodeUI
33346      */
33347     this.ui = new uiClass(this);
33348     
33349     // finally support items[]
33350     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33351         return;
33352     }
33353     
33354     
33355     Roo.each(this.attributes.items, function(c) {
33356         this.appendChild(Roo.factory(c,Roo.Tree));
33357     }, this);
33358     delete this.attributes.items;
33359     
33360     
33361     
33362 };
33363 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33364     preventHScroll: true,
33365     /**
33366      * Returns true if this node is expanded
33367      * @return {Boolean}
33368      */
33369     isExpanded : function(){
33370         return this.expanded;
33371     },
33372
33373     /**
33374      * Returns the UI object for this node
33375      * @return {TreeNodeUI}
33376      */
33377     getUI : function(){
33378         return this.ui;
33379     },
33380
33381     // private override
33382     setFirstChild : function(node){
33383         var of = this.firstChild;
33384         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33385         if(this.childrenRendered && of && node != of){
33386             of.renderIndent(true, true);
33387         }
33388         if(this.rendered){
33389             this.renderIndent(true, true);
33390         }
33391     },
33392
33393     // private override
33394     setLastChild : function(node){
33395         var ol = this.lastChild;
33396         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33397         if(this.childrenRendered && ol && node != ol){
33398             ol.renderIndent(true, true);
33399         }
33400         if(this.rendered){
33401             this.renderIndent(true, true);
33402         }
33403     },
33404
33405     // these methods are overridden to provide lazy rendering support
33406     // private override
33407     appendChild : function()
33408     {
33409         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33410         if(node && this.childrenRendered){
33411             node.render();
33412         }
33413         this.ui.updateExpandIcon();
33414         return node;
33415     },
33416
33417     // private override
33418     removeChild : function(node){
33419         this.ownerTree.getSelectionModel().unselect(node);
33420         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33421         // if it's been rendered remove dom node
33422         if(this.childrenRendered){
33423             node.ui.remove();
33424         }
33425         if(this.childNodes.length < 1){
33426             this.collapse(false, false);
33427         }else{
33428             this.ui.updateExpandIcon();
33429         }
33430         if(!this.firstChild) {
33431             this.childrenRendered = false;
33432         }
33433         return node;
33434     },
33435
33436     // private override
33437     insertBefore : function(node, refNode){
33438         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33439         if(newNode && refNode && this.childrenRendered){
33440             node.render();
33441         }
33442         this.ui.updateExpandIcon();
33443         return newNode;
33444     },
33445
33446     /**
33447      * Sets the text for this node
33448      * @param {String} text
33449      */
33450     setText : function(text){
33451         var oldText = this.text;
33452         this.text = text;
33453         this.attributes.text = text;
33454         if(this.rendered){ // event without subscribing
33455             this.ui.onTextChange(this, text, oldText);
33456         }
33457         this.fireEvent("textchange", this, text, oldText);
33458     },
33459
33460     /**
33461      * Triggers selection of this node
33462      */
33463     select : function(){
33464         this.getOwnerTree().getSelectionModel().select(this);
33465     },
33466
33467     /**
33468      * Triggers deselection of this node
33469      */
33470     unselect : function(){
33471         this.getOwnerTree().getSelectionModel().unselect(this);
33472     },
33473
33474     /**
33475      * Returns true if this node is selected
33476      * @return {Boolean}
33477      */
33478     isSelected : function(){
33479         return this.getOwnerTree().getSelectionModel().isSelected(this);
33480     },
33481
33482     /**
33483      * Expand this node.
33484      * @param {Boolean} deep (optional) True to expand all children as well
33485      * @param {Boolean} anim (optional) false to cancel the default animation
33486      * @param {Function} callback (optional) A callback to be called when
33487      * expanding this node completes (does not wait for deep expand to complete).
33488      * Called with 1 parameter, this node.
33489      */
33490     expand : function(deep, anim, callback){
33491         if(!this.expanded){
33492             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33493                 return;
33494             }
33495             if(!this.childrenRendered){
33496                 this.renderChildren();
33497             }
33498             this.expanded = true;
33499             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33500                 this.ui.animExpand(function(){
33501                     this.fireEvent("expand", this);
33502                     if(typeof callback == "function"){
33503                         callback(this);
33504                     }
33505                     if(deep === true){
33506                         this.expandChildNodes(true);
33507                     }
33508                 }.createDelegate(this));
33509                 return;
33510             }else{
33511                 this.ui.expand();
33512                 this.fireEvent("expand", this);
33513                 if(typeof callback == "function"){
33514                     callback(this);
33515                 }
33516             }
33517         }else{
33518            if(typeof callback == "function"){
33519                callback(this);
33520            }
33521         }
33522         if(deep === true){
33523             this.expandChildNodes(true);
33524         }
33525     },
33526
33527     isHiddenRoot : function(){
33528         return this.isRoot && !this.getOwnerTree().rootVisible;
33529     },
33530
33531     /**
33532      * Collapse this node.
33533      * @param {Boolean} deep (optional) True to collapse all children as well
33534      * @param {Boolean} anim (optional) false to cancel the default animation
33535      */
33536     collapse : function(deep, anim){
33537         if(this.expanded && !this.isHiddenRoot()){
33538             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33539                 return;
33540             }
33541             this.expanded = false;
33542             if((this.getOwnerTree().animate && anim !== false) || anim){
33543                 this.ui.animCollapse(function(){
33544                     this.fireEvent("collapse", this);
33545                     if(deep === true){
33546                         this.collapseChildNodes(true);
33547                     }
33548                 }.createDelegate(this));
33549                 return;
33550             }else{
33551                 this.ui.collapse();
33552                 this.fireEvent("collapse", this);
33553             }
33554         }
33555         if(deep === true){
33556             var cs = this.childNodes;
33557             for(var i = 0, len = cs.length; i < len; i++) {
33558                 cs[i].collapse(true, false);
33559             }
33560         }
33561     },
33562
33563     // private
33564     delayedExpand : function(delay){
33565         if(!this.expandProcId){
33566             this.expandProcId = this.expand.defer(delay, this);
33567         }
33568     },
33569
33570     // private
33571     cancelExpand : function(){
33572         if(this.expandProcId){
33573             clearTimeout(this.expandProcId);
33574         }
33575         this.expandProcId = false;
33576     },
33577
33578     /**
33579      * Toggles expanded/collapsed state of the node
33580      */
33581     toggle : function(){
33582         if(this.expanded){
33583             this.collapse();
33584         }else{
33585             this.expand();
33586         }
33587     },
33588
33589     /**
33590      * Ensures all parent nodes are expanded
33591      */
33592     ensureVisible : function(callback){
33593         var tree = this.getOwnerTree();
33594         tree.expandPath(this.parentNode.getPath(), false, function(){
33595             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33596             Roo.callback(callback);
33597         }.createDelegate(this));
33598     },
33599
33600     /**
33601      * Expand all child nodes
33602      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33603      */
33604     expandChildNodes : function(deep){
33605         var cs = this.childNodes;
33606         for(var i = 0, len = cs.length; i < len; i++) {
33607                 cs[i].expand(deep);
33608         }
33609     },
33610
33611     /**
33612      * Collapse all child nodes
33613      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33614      */
33615     collapseChildNodes : function(deep){
33616         var cs = this.childNodes;
33617         for(var i = 0, len = cs.length; i < len; i++) {
33618                 cs[i].collapse(deep);
33619         }
33620     },
33621
33622     /**
33623      * Disables this node
33624      */
33625     disable : function(){
33626         this.disabled = true;
33627         this.unselect();
33628         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33629             this.ui.onDisableChange(this, true);
33630         }
33631         this.fireEvent("disabledchange", this, true);
33632     },
33633
33634     /**
33635      * Enables this node
33636      */
33637     enable : function(){
33638         this.disabled = false;
33639         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33640             this.ui.onDisableChange(this, false);
33641         }
33642         this.fireEvent("disabledchange", this, false);
33643     },
33644
33645     // private
33646     renderChildren : function(suppressEvent){
33647         if(suppressEvent !== false){
33648             this.fireEvent("beforechildrenrendered", this);
33649         }
33650         var cs = this.childNodes;
33651         for(var i = 0, len = cs.length; i < len; i++){
33652             cs[i].render(true);
33653         }
33654         this.childrenRendered = true;
33655     },
33656
33657     // private
33658     sort : function(fn, scope){
33659         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33660         if(this.childrenRendered){
33661             var cs = this.childNodes;
33662             for(var i = 0, len = cs.length; i < len; i++){
33663                 cs[i].render(true);
33664             }
33665         }
33666     },
33667
33668     // private
33669     render : function(bulkRender){
33670         this.ui.render(bulkRender);
33671         if(!this.rendered){
33672             this.rendered = true;
33673             if(this.expanded){
33674                 this.expanded = false;
33675                 this.expand(false, false);
33676             }
33677         }
33678     },
33679
33680     // private
33681     renderIndent : function(deep, refresh){
33682         if(refresh){
33683             this.ui.childIndent = null;
33684         }
33685         this.ui.renderIndent();
33686         if(deep === true && this.childrenRendered){
33687             var cs = this.childNodes;
33688             for(var i = 0, len = cs.length; i < len; i++){
33689                 cs[i].renderIndent(true, refresh);
33690             }
33691         }
33692     }
33693 });/*
33694  * Based on:
33695  * Ext JS Library 1.1.1
33696  * Copyright(c) 2006-2007, Ext JS, LLC.
33697  *
33698  * Originally Released Under LGPL - original licence link has changed is not relivant.
33699  *
33700  * Fork - LGPL
33701  * <script type="text/javascript">
33702  */
33703  
33704 /**
33705  * @class Roo.tree.AsyncTreeNode
33706  * @extends Roo.tree.TreeNode
33707  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33708  * @constructor
33709  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33710  */
33711  Roo.tree.AsyncTreeNode = function(config){
33712     this.loaded = false;
33713     this.loading = false;
33714     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33715     /**
33716     * @event beforeload
33717     * Fires before this node is loaded, return false to cancel
33718     * @param {Node} this This node
33719     */
33720     this.addEvents({'beforeload':true, 'load': true});
33721     /**
33722     * @event load
33723     * Fires when this node is loaded
33724     * @param {Node} this This node
33725     */
33726     /**
33727      * The loader used by this node (defaults to using the tree's defined loader)
33728      * @type TreeLoader
33729      * @property loader
33730      */
33731 };
33732 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33733     expand : function(deep, anim, callback){
33734         if(this.loading){ // if an async load is already running, waiting til it's done
33735             var timer;
33736             var f = function(){
33737                 if(!this.loading){ // done loading
33738                     clearInterval(timer);
33739                     this.expand(deep, anim, callback);
33740                 }
33741             }.createDelegate(this);
33742             timer = setInterval(f, 200);
33743             return;
33744         }
33745         if(!this.loaded){
33746             if(this.fireEvent("beforeload", this) === false){
33747                 return;
33748             }
33749             this.loading = true;
33750             this.ui.beforeLoad(this);
33751             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33752             if(loader){
33753                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33754                 return;
33755             }
33756         }
33757         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33758     },
33759     
33760     /**
33761      * Returns true if this node is currently loading
33762      * @return {Boolean}
33763      */
33764     isLoading : function(){
33765         return this.loading;  
33766     },
33767     
33768     loadComplete : function(deep, anim, callback){
33769         this.loading = false;
33770         this.loaded = true;
33771         this.ui.afterLoad(this);
33772         this.fireEvent("load", this);
33773         this.expand(deep, anim, callback);
33774     },
33775     
33776     /**
33777      * Returns true if this node has been loaded
33778      * @return {Boolean}
33779      */
33780     isLoaded : function(){
33781         return this.loaded;
33782     },
33783     
33784     hasChildNodes : function(){
33785         if(!this.isLeaf() && !this.loaded){
33786             return true;
33787         }else{
33788             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33789         }
33790     },
33791
33792     /**
33793      * Trigger a reload for this node
33794      * @param {Function} callback
33795      */
33796     reload : function(callback){
33797         this.collapse(false, false);
33798         while(this.firstChild){
33799             this.removeChild(this.firstChild);
33800         }
33801         this.childrenRendered = false;
33802         this.loaded = false;
33803         if(this.isHiddenRoot()){
33804             this.expanded = false;
33805         }
33806         this.expand(false, false, callback);
33807     }
33808 });/*
33809  * Based on:
33810  * Ext JS Library 1.1.1
33811  * Copyright(c) 2006-2007, Ext JS, LLC.
33812  *
33813  * Originally Released Under LGPL - original licence link has changed is not relivant.
33814  *
33815  * Fork - LGPL
33816  * <script type="text/javascript">
33817  */
33818  
33819 /**
33820  * @class Roo.tree.TreeNodeUI
33821  * @constructor
33822  * @param {Object} node The node to render
33823  * The TreeNode UI implementation is separate from the
33824  * tree implementation. Unless you are customizing the tree UI,
33825  * you should never have to use this directly.
33826  */
33827 Roo.tree.TreeNodeUI = function(node){
33828     this.node = node;
33829     this.rendered = false;
33830     this.animating = false;
33831     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33832 };
33833
33834 Roo.tree.TreeNodeUI.prototype = {
33835     removeChild : function(node){
33836         if(this.rendered){
33837             this.ctNode.removeChild(node.ui.getEl());
33838         }
33839     },
33840
33841     beforeLoad : function(){
33842          this.addClass("x-tree-node-loading");
33843     },
33844
33845     afterLoad : function(){
33846          this.removeClass("x-tree-node-loading");
33847     },
33848
33849     onTextChange : function(node, text, oldText){
33850         if(this.rendered){
33851             this.textNode.innerHTML = text;
33852         }
33853     },
33854
33855     onDisableChange : function(node, state){
33856         this.disabled = state;
33857         if(state){
33858             this.addClass("x-tree-node-disabled");
33859         }else{
33860             this.removeClass("x-tree-node-disabled");
33861         }
33862     },
33863
33864     onSelectedChange : function(state){
33865         if(state){
33866             this.focus();
33867             this.addClass("x-tree-selected");
33868         }else{
33869             //this.blur();
33870             this.removeClass("x-tree-selected");
33871         }
33872     },
33873
33874     onMove : function(tree, node, oldParent, newParent, index, refNode){
33875         this.childIndent = null;
33876         if(this.rendered){
33877             var targetNode = newParent.ui.getContainer();
33878             if(!targetNode){//target not rendered
33879                 this.holder = document.createElement("div");
33880                 this.holder.appendChild(this.wrap);
33881                 return;
33882             }
33883             var insertBefore = refNode ? refNode.ui.getEl() : null;
33884             if(insertBefore){
33885                 targetNode.insertBefore(this.wrap, insertBefore);
33886             }else{
33887                 targetNode.appendChild(this.wrap);
33888             }
33889             this.node.renderIndent(true);
33890         }
33891     },
33892
33893     addClass : function(cls){
33894         if(this.elNode){
33895             Roo.fly(this.elNode).addClass(cls);
33896         }
33897     },
33898
33899     removeClass : function(cls){
33900         if(this.elNode){
33901             Roo.fly(this.elNode).removeClass(cls);
33902         }
33903     },
33904
33905     remove : function(){
33906         if(this.rendered){
33907             this.holder = document.createElement("div");
33908             this.holder.appendChild(this.wrap);
33909         }
33910     },
33911
33912     fireEvent : function(){
33913         return this.node.fireEvent.apply(this.node, arguments);
33914     },
33915
33916     initEvents : function(){
33917         this.node.on("move", this.onMove, this);
33918         var E = Roo.EventManager;
33919         var a = this.anchor;
33920
33921         var el = Roo.fly(a, '_treeui');
33922
33923         if(Roo.isOpera){ // opera render bug ignores the CSS
33924             el.setStyle("text-decoration", "none");
33925         }
33926
33927         el.on("click", this.onClick, this);
33928         el.on("dblclick", this.onDblClick, this);
33929
33930         if(this.checkbox){
33931             Roo.EventManager.on(this.checkbox,
33932                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33933         }
33934
33935         el.on("contextmenu", this.onContextMenu, this);
33936
33937         var icon = Roo.fly(this.iconNode);
33938         icon.on("click", this.onClick, this);
33939         icon.on("dblclick", this.onDblClick, this);
33940         icon.on("contextmenu", this.onContextMenu, this);
33941         E.on(this.ecNode, "click", this.ecClick, this, true);
33942
33943         if(this.node.disabled){
33944             this.addClass("x-tree-node-disabled");
33945         }
33946         if(this.node.hidden){
33947             this.addClass("x-tree-node-disabled");
33948         }
33949         var ot = this.node.getOwnerTree();
33950         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33951         if(dd && (!this.node.isRoot || ot.rootVisible)){
33952             Roo.dd.Registry.register(this.elNode, {
33953                 node: this.node,
33954                 handles: this.getDDHandles(),
33955                 isHandle: false
33956             });
33957         }
33958     },
33959
33960     getDDHandles : function(){
33961         return [this.iconNode, this.textNode];
33962     },
33963
33964     hide : function(){
33965         if(this.rendered){
33966             this.wrap.style.display = "none";
33967         }
33968     },
33969
33970     show : function(){
33971         if(this.rendered){
33972             this.wrap.style.display = "";
33973         }
33974     },
33975
33976     onContextMenu : function(e){
33977         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33978             e.preventDefault();
33979             this.focus();
33980             this.fireEvent("contextmenu", this.node, e);
33981         }
33982     },
33983
33984     onClick : function(e){
33985         if(this.dropping){
33986             e.stopEvent();
33987             return;
33988         }
33989         if(this.fireEvent("beforeclick", this.node, e) !== false){
33990             if(!this.disabled && this.node.attributes.href){
33991                 this.fireEvent("click", this.node, e);
33992                 return;
33993             }
33994             e.preventDefault();
33995             if(this.disabled){
33996                 return;
33997             }
33998
33999             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
34000                 this.node.toggle();
34001             }
34002
34003             this.fireEvent("click", this.node, e);
34004         }else{
34005             e.stopEvent();
34006         }
34007     },
34008
34009     onDblClick : function(e){
34010         e.preventDefault();
34011         if(this.disabled){
34012             return;
34013         }
34014         if(this.checkbox){
34015             this.toggleCheck();
34016         }
34017         if(!this.animating && this.node.hasChildNodes()){
34018             this.node.toggle();
34019         }
34020         this.fireEvent("dblclick", this.node, e);
34021     },
34022
34023     onCheckChange : function(){
34024         var checked = this.checkbox.checked;
34025         this.node.attributes.checked = checked;
34026         this.fireEvent('checkchange', this.node, checked);
34027     },
34028
34029     ecClick : function(e){
34030         if(!this.animating && this.node.hasChildNodes()){
34031             this.node.toggle();
34032         }
34033     },
34034
34035     startDrop : function(){
34036         this.dropping = true;
34037     },
34038
34039     // delayed drop so the click event doesn't get fired on a drop
34040     endDrop : function(){
34041        setTimeout(function(){
34042            this.dropping = false;
34043        }.createDelegate(this), 50);
34044     },
34045
34046     expand : function(){
34047         this.updateExpandIcon();
34048         this.ctNode.style.display = "";
34049     },
34050
34051     focus : function(){
34052         if(!this.node.preventHScroll){
34053             try{this.anchor.focus();
34054             }catch(e){}
34055         }else if(!Roo.isIE){
34056             try{
34057                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
34058                 var l = noscroll.scrollLeft;
34059                 this.anchor.focus();
34060                 noscroll.scrollLeft = l;
34061             }catch(e){}
34062         }
34063     },
34064
34065     toggleCheck : function(value){
34066         var cb = this.checkbox;
34067         if(cb){
34068             cb.checked = (value === undefined ? !cb.checked : value);
34069         }
34070     },
34071
34072     blur : function(){
34073         try{
34074             this.anchor.blur();
34075         }catch(e){}
34076     },
34077
34078     animExpand : function(callback){
34079         var ct = Roo.get(this.ctNode);
34080         ct.stopFx();
34081         if(!this.node.hasChildNodes()){
34082             this.updateExpandIcon();
34083             this.ctNode.style.display = "";
34084             Roo.callback(callback);
34085             return;
34086         }
34087         this.animating = true;
34088         this.updateExpandIcon();
34089
34090         ct.slideIn('t', {
34091            callback : function(){
34092                this.animating = false;
34093                Roo.callback(callback);
34094             },
34095             scope: this,
34096             duration: this.node.ownerTree.duration || .25
34097         });
34098     },
34099
34100     highlight : function(){
34101         var tree = this.node.getOwnerTree();
34102         Roo.fly(this.wrap).highlight(
34103             tree.hlColor || "C3DAF9",
34104             {endColor: tree.hlBaseColor}
34105         );
34106     },
34107
34108     collapse : function(){
34109         this.updateExpandIcon();
34110         this.ctNode.style.display = "none";
34111     },
34112
34113     animCollapse : function(callback){
34114         var ct = Roo.get(this.ctNode);
34115         ct.enableDisplayMode('block');
34116         ct.stopFx();
34117
34118         this.animating = true;
34119         this.updateExpandIcon();
34120
34121         ct.slideOut('t', {
34122             callback : function(){
34123                this.animating = false;
34124                Roo.callback(callback);
34125             },
34126             scope: this,
34127             duration: this.node.ownerTree.duration || .25
34128         });
34129     },
34130
34131     getContainer : function(){
34132         return this.ctNode;
34133     },
34134
34135     getEl : function(){
34136         return this.wrap;
34137     },
34138
34139     appendDDGhost : function(ghostNode){
34140         ghostNode.appendChild(this.elNode.cloneNode(true));
34141     },
34142
34143     getDDRepairXY : function(){
34144         return Roo.lib.Dom.getXY(this.iconNode);
34145     },
34146
34147     onRender : function(){
34148         this.render();
34149     },
34150
34151     render : function(bulkRender){
34152         var n = this.node, a = n.attributes;
34153         var targetNode = n.parentNode ?
34154               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
34155
34156         if(!this.rendered){
34157             this.rendered = true;
34158
34159             this.renderElements(n, a, targetNode, bulkRender);
34160
34161             if(a.qtip){
34162                if(this.textNode.setAttributeNS){
34163                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
34164                    if(a.qtipTitle){
34165                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
34166                    }
34167                }else{
34168                    this.textNode.setAttribute("ext:qtip", a.qtip);
34169                    if(a.qtipTitle){
34170                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
34171                    }
34172                }
34173             }else if(a.qtipCfg){
34174                 a.qtipCfg.target = Roo.id(this.textNode);
34175                 Roo.QuickTips.register(a.qtipCfg);
34176             }
34177             this.initEvents();
34178             if(!this.node.expanded){
34179                 this.updateExpandIcon();
34180             }
34181         }else{
34182             if(bulkRender === true) {
34183                 targetNode.appendChild(this.wrap);
34184             }
34185         }
34186     },
34187
34188     renderElements : function(n, a, targetNode, bulkRender)
34189     {
34190         // add some indent caching, this helps performance when rendering a large tree
34191         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34192         var t = n.getOwnerTree();
34193         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
34194         if (typeof(n.attributes.html) != 'undefined') {
34195             txt = n.attributes.html;
34196         }
34197         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
34198         var cb = typeof a.checked == 'boolean';
34199         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34200         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
34201             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
34202             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
34203             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
34204             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
34205             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
34206              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
34207                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
34208             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34209             "</li>"];
34210
34211         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34212             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34213                                 n.nextSibling.ui.getEl(), buf.join(""));
34214         }else{
34215             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34216         }
34217
34218         this.elNode = this.wrap.childNodes[0];
34219         this.ctNode = this.wrap.childNodes[1];
34220         var cs = this.elNode.childNodes;
34221         this.indentNode = cs[0];
34222         this.ecNode = cs[1];
34223         this.iconNode = cs[2];
34224         var index = 3;
34225         if(cb){
34226             this.checkbox = cs[3];
34227             index++;
34228         }
34229         this.anchor = cs[index];
34230         this.textNode = cs[index].firstChild;
34231     },
34232
34233     getAnchor : function(){
34234         return this.anchor;
34235     },
34236
34237     getTextEl : function(){
34238         return this.textNode;
34239     },
34240
34241     getIconEl : function(){
34242         return this.iconNode;
34243     },
34244
34245     isChecked : function(){
34246         return this.checkbox ? this.checkbox.checked : false;
34247     },
34248
34249     updateExpandIcon : function(){
34250         if(this.rendered){
34251             var n = this.node, c1, c2;
34252             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34253             var hasChild = n.hasChildNodes();
34254             if(hasChild){
34255                 if(n.expanded){
34256                     cls += "-minus";
34257                     c1 = "x-tree-node-collapsed";
34258                     c2 = "x-tree-node-expanded";
34259                 }else{
34260                     cls += "-plus";
34261                     c1 = "x-tree-node-expanded";
34262                     c2 = "x-tree-node-collapsed";
34263                 }
34264                 if(this.wasLeaf){
34265                     this.removeClass("x-tree-node-leaf");
34266                     this.wasLeaf = false;
34267                 }
34268                 if(this.c1 != c1 || this.c2 != c2){
34269                     Roo.fly(this.elNode).replaceClass(c1, c2);
34270                     this.c1 = c1; this.c2 = c2;
34271                 }
34272             }else{
34273                 // this changes non-leafs into leafs if they have no children.
34274                 // it's not very rational behaviour..
34275                 
34276                 if(!this.wasLeaf && this.node.leaf){
34277                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34278                     delete this.c1;
34279                     delete this.c2;
34280                     this.wasLeaf = true;
34281                 }
34282             }
34283             var ecc = "x-tree-ec-icon "+cls;
34284             if(this.ecc != ecc){
34285                 this.ecNode.className = ecc;
34286                 this.ecc = ecc;
34287             }
34288         }
34289     },
34290
34291     getChildIndent : function(){
34292         if(!this.childIndent){
34293             var buf = [];
34294             var p = this.node;
34295             while(p){
34296                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34297                     if(!p.isLast()) {
34298                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34299                     } else {
34300                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34301                     }
34302                 }
34303                 p = p.parentNode;
34304             }
34305             this.childIndent = buf.join("");
34306         }
34307         return this.childIndent;
34308     },
34309
34310     renderIndent : function(){
34311         if(this.rendered){
34312             var indent = "";
34313             var p = this.node.parentNode;
34314             if(p){
34315                 indent = p.ui.getChildIndent();
34316             }
34317             if(this.indentMarkup != indent){ // don't rerender if not required
34318                 this.indentNode.innerHTML = indent;
34319                 this.indentMarkup = indent;
34320             }
34321             this.updateExpandIcon();
34322         }
34323     }
34324 };
34325
34326 Roo.tree.RootTreeNodeUI = function(){
34327     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34328 };
34329 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34330     render : function(){
34331         if(!this.rendered){
34332             var targetNode = this.node.ownerTree.innerCt.dom;
34333             this.node.expanded = true;
34334             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34335             this.wrap = this.ctNode = targetNode.firstChild;
34336         }
34337     },
34338     collapse : function(){
34339     },
34340     expand : function(){
34341     }
34342 });/*
34343  * Based on:
34344  * Ext JS Library 1.1.1
34345  * Copyright(c) 2006-2007, Ext JS, LLC.
34346  *
34347  * Originally Released Under LGPL - original licence link has changed is not relivant.
34348  *
34349  * Fork - LGPL
34350  * <script type="text/javascript">
34351  */
34352 /**
34353  * @class Roo.tree.TreeLoader
34354  * @extends Roo.util.Observable
34355  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34356  * nodes from a specified URL. The response must be a javascript Array definition
34357  * who's elements are node definition objects. eg:
34358  * <pre><code>
34359 {  success : true,
34360    data :      [
34361    
34362     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34363     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34364     ]
34365 }
34366
34367
34368 </code></pre>
34369  * <br><br>
34370  * The old style respose with just an array is still supported, but not recommended.
34371  * <br><br>
34372  *
34373  * A server request is sent, and child nodes are loaded only when a node is expanded.
34374  * The loading node's id is passed to the server under the parameter name "node" to
34375  * enable the server to produce the correct child nodes.
34376  * <br><br>
34377  * To pass extra parameters, an event handler may be attached to the "beforeload"
34378  * event, and the parameters specified in the TreeLoader's baseParams property:
34379  * <pre><code>
34380     myTreeLoader.on("beforeload", function(treeLoader, node) {
34381         this.baseParams.category = node.attributes.category;
34382     }, this);
34383 </code></pre><
34384  * This would pass an HTTP parameter called "category" to the server containing
34385  * the value of the Node's "category" attribute.
34386  * @constructor
34387  * Creates a new Treeloader.
34388  * @param {Object} config A config object containing config properties.
34389  */
34390 Roo.tree.TreeLoader = function(config){
34391     this.baseParams = {};
34392     this.requestMethod = "POST";
34393     Roo.apply(this, config);
34394
34395     this.addEvents({
34396     
34397         /**
34398          * @event beforeload
34399          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34400          * @param {Object} This TreeLoader object.
34401          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34402          * @param {Object} callback The callback function specified in the {@link #load} call.
34403          */
34404         beforeload : true,
34405         /**
34406          * @event load
34407          * Fires when the node has been successfuly loaded.
34408          * @param {Object} This TreeLoader object.
34409          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34410          * @param {Object} response The response object containing the data from the server.
34411          */
34412         load : true,
34413         /**
34414          * @event loadexception
34415          * Fires if the network request failed.
34416          * @param {Object} This TreeLoader object.
34417          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34418          * @param {Object} response The response object containing the data from the server.
34419          */
34420         loadexception : true,
34421         /**
34422          * @event create
34423          * Fires before a node is created, enabling you to return custom Node types 
34424          * @param {Object} This TreeLoader object.
34425          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34426          */
34427         create : true
34428     });
34429
34430     Roo.tree.TreeLoader.superclass.constructor.call(this);
34431 };
34432
34433 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34434     /**
34435     * @cfg {String} dataUrl The URL from which to request a Json string which
34436     * specifies an array of node definition object representing the child nodes
34437     * to be loaded.
34438     */
34439     /**
34440     * @cfg {String} requestMethod either GET or POST
34441     * defaults to POST (due to BC)
34442     * to be loaded.
34443     */
34444     /**
34445     * @cfg {Object} baseParams (optional) An object containing properties which
34446     * specify HTTP parameters to be passed to each request for child nodes.
34447     */
34448     /**
34449     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34450     * created by this loader. If the attributes sent by the server have an attribute in this object,
34451     * they take priority.
34452     */
34453     /**
34454     * @cfg {Object} uiProviders (optional) An object containing properties which
34455     * 
34456     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34457     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34458     * <i>uiProvider</i> attribute of a returned child node is a string rather
34459     * than a reference to a TreeNodeUI implementation, this that string value
34460     * is used as a property name in the uiProviders object. You can define the provider named
34461     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34462     */
34463     uiProviders : {},
34464
34465     /**
34466     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34467     * child nodes before loading.
34468     */
34469     clearOnLoad : true,
34470
34471     /**
34472     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34473     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34474     * Grid query { data : [ .....] }
34475     */
34476     
34477     root : false,
34478      /**
34479     * @cfg {String} queryParam (optional) 
34480     * Name of the query as it will be passed on the querystring (defaults to 'node')
34481     * eg. the request will be ?node=[id]
34482     */
34483     
34484     
34485     queryParam: false,
34486     
34487     /**
34488      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34489      * This is called automatically when a node is expanded, but may be used to reload
34490      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34491      * @param {Roo.tree.TreeNode} node
34492      * @param {Function} callback
34493      */
34494     load : function(node, callback){
34495         if(this.clearOnLoad){
34496             while(node.firstChild){
34497                 node.removeChild(node.firstChild);
34498             }
34499         }
34500         if(node.attributes.children){ // preloaded json children
34501             var cs = node.attributes.children;
34502             for(var i = 0, len = cs.length; i < len; i++){
34503                 node.appendChild(this.createNode(cs[i]));
34504             }
34505             if(typeof callback == "function"){
34506                 callback();
34507             }
34508         }else if(this.dataUrl){
34509             this.requestData(node, callback);
34510         }
34511     },
34512
34513     getParams: function(node){
34514         var buf = [], bp = this.baseParams;
34515         for(var key in bp){
34516             if(typeof bp[key] != "function"){
34517                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34518             }
34519         }
34520         var n = this.queryParam === false ? 'node' : this.queryParam;
34521         buf.push(n + "=", encodeURIComponent(node.id));
34522         return buf.join("");
34523     },
34524
34525     requestData : function(node, callback){
34526         if(this.fireEvent("beforeload", this, node, callback) !== false){
34527             this.transId = Roo.Ajax.request({
34528                 method:this.requestMethod,
34529                 url: this.dataUrl||this.url,
34530                 success: this.handleResponse,
34531                 failure: this.handleFailure,
34532                 scope: this,
34533                 argument: {callback: callback, node: node},
34534                 params: this.getParams(node)
34535             });
34536         }else{
34537             // if the load is cancelled, make sure we notify
34538             // the node that we are done
34539             if(typeof callback == "function"){
34540                 callback();
34541             }
34542         }
34543     },
34544
34545     isLoading : function(){
34546         return this.transId ? true : false;
34547     },
34548
34549     abort : function(){
34550         if(this.isLoading()){
34551             Roo.Ajax.abort(this.transId);
34552         }
34553     },
34554
34555     // private
34556     createNode : function(attr)
34557     {
34558         // apply baseAttrs, nice idea Corey!
34559         if(this.baseAttrs){
34560             Roo.applyIf(attr, this.baseAttrs);
34561         }
34562         if(this.applyLoader !== false){
34563             attr.loader = this;
34564         }
34565         // uiProvider = depreciated..
34566         
34567         if(typeof(attr.uiProvider) == 'string'){
34568            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34569                 /**  eval:var:attr */ eval(attr.uiProvider);
34570         }
34571         if(typeof(this.uiProviders['default']) != 'undefined') {
34572             attr.uiProvider = this.uiProviders['default'];
34573         }
34574         
34575         this.fireEvent('create', this, attr);
34576         
34577         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34578         return(attr.leaf ?
34579                         new Roo.tree.TreeNode(attr) :
34580                         new Roo.tree.AsyncTreeNode(attr));
34581     },
34582
34583     processResponse : function(response, node, callback)
34584     {
34585         var json = response.responseText;
34586         try {
34587             
34588             var o = Roo.decode(json);
34589             
34590             if (this.root === false && typeof(o.success) != undefined) {
34591                 this.root = 'data'; // the default behaviour for list like data..
34592                 }
34593                 
34594             if (this.root !== false &&  !o.success) {
34595                 // it's a failure condition.
34596                 var a = response.argument;
34597                 this.fireEvent("loadexception", this, a.node, response);
34598                 Roo.log("Load failed - should have a handler really");
34599                 return;
34600             }
34601             
34602             
34603             
34604             if (this.root !== false) {
34605                  o = o[this.root];
34606             }
34607             
34608             for(var i = 0, len = o.length; i < len; i++){
34609                 var n = this.createNode(o[i]);
34610                 if(n){
34611                     node.appendChild(n);
34612                 }
34613             }
34614             if(typeof callback == "function"){
34615                 callback(this, node);
34616             }
34617         }catch(e){
34618             this.handleFailure(response);
34619         }
34620     },
34621
34622     handleResponse : function(response){
34623         this.transId = false;
34624         var a = response.argument;
34625         this.processResponse(response, a.node, a.callback);
34626         this.fireEvent("load", this, a.node, response);
34627     },
34628
34629     handleFailure : function(response)
34630     {
34631         // should handle failure better..
34632         this.transId = false;
34633         var a = response.argument;
34634         this.fireEvent("loadexception", this, a.node, response);
34635         if(typeof a.callback == "function"){
34636             a.callback(this, a.node);
34637         }
34638     }
34639 });/*
34640  * Based on:
34641  * Ext JS Library 1.1.1
34642  * Copyright(c) 2006-2007, Ext JS, LLC.
34643  *
34644  * Originally Released Under LGPL - original licence link has changed is not relivant.
34645  *
34646  * Fork - LGPL
34647  * <script type="text/javascript">
34648  */
34649
34650 /**
34651 * @class Roo.tree.TreeFilter
34652 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34653 * @param {TreePanel} tree
34654 * @param {Object} config (optional)
34655  */
34656 Roo.tree.TreeFilter = function(tree, config){
34657     this.tree = tree;
34658     this.filtered = {};
34659     Roo.apply(this, config);
34660 };
34661
34662 Roo.tree.TreeFilter.prototype = {
34663     clearBlank:false,
34664     reverse:false,
34665     autoClear:false,
34666     remove:false,
34667
34668      /**
34669      * Filter the data by a specific attribute.
34670      * @param {String/RegExp} value Either string that the attribute value
34671      * should start with or a RegExp to test against the attribute
34672      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34673      * @param {TreeNode} startNode (optional) The node to start the filter at.
34674      */
34675     filter : function(value, attr, startNode){
34676         attr = attr || "text";
34677         var f;
34678         if(typeof value == "string"){
34679             var vlen = value.length;
34680             // auto clear empty filter
34681             if(vlen == 0 && this.clearBlank){
34682                 this.clear();
34683                 return;
34684             }
34685             value = value.toLowerCase();
34686             f = function(n){
34687                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34688             };
34689         }else if(value.exec){ // regex?
34690             f = function(n){
34691                 return value.test(n.attributes[attr]);
34692             };
34693         }else{
34694             throw 'Illegal filter type, must be string or regex';
34695         }
34696         this.filterBy(f, null, startNode);
34697         },
34698
34699     /**
34700      * Filter by a function. The passed function will be called with each
34701      * node in the tree (or from the startNode). If the function returns true, the node is kept
34702      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34703      * @param {Function} fn The filter function
34704      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34705      */
34706     filterBy : function(fn, scope, startNode){
34707         startNode = startNode || this.tree.root;
34708         if(this.autoClear){
34709             this.clear();
34710         }
34711         var af = this.filtered, rv = this.reverse;
34712         var f = function(n){
34713             if(n == startNode){
34714                 return true;
34715             }
34716             if(af[n.id]){
34717                 return false;
34718             }
34719             var m = fn.call(scope || n, n);
34720             if(!m || rv){
34721                 af[n.id] = n;
34722                 n.ui.hide();
34723                 return false;
34724             }
34725             return true;
34726         };
34727         startNode.cascade(f);
34728         if(this.remove){
34729            for(var id in af){
34730                if(typeof id != "function"){
34731                    var n = af[id];
34732                    if(n && n.parentNode){
34733                        n.parentNode.removeChild(n);
34734                    }
34735                }
34736            }
34737         }
34738     },
34739
34740     /**
34741      * Clears the current filter. Note: with the "remove" option
34742      * set a filter cannot be cleared.
34743      */
34744     clear : function(){
34745         var t = this.tree;
34746         var af = this.filtered;
34747         for(var id in af){
34748             if(typeof id != "function"){
34749                 var n = af[id];
34750                 if(n){
34751                     n.ui.show();
34752                 }
34753             }
34754         }
34755         this.filtered = {};
34756     }
34757 };
34758 /*
34759  * Based on:
34760  * Ext JS Library 1.1.1
34761  * Copyright(c) 2006-2007, Ext JS, LLC.
34762  *
34763  * Originally Released Under LGPL - original licence link has changed is not relivant.
34764  *
34765  * Fork - LGPL
34766  * <script type="text/javascript">
34767  */
34768  
34769
34770 /**
34771  * @class Roo.tree.TreeSorter
34772  * Provides sorting of nodes in a TreePanel
34773  * 
34774  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34775  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34776  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34777  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34778  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34779  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34780  * @constructor
34781  * @param {TreePanel} tree
34782  * @param {Object} config
34783  */
34784 Roo.tree.TreeSorter = function(tree, config){
34785     Roo.apply(this, config);
34786     tree.on("beforechildrenrendered", this.doSort, this);
34787     tree.on("append", this.updateSort, this);
34788     tree.on("insert", this.updateSort, this);
34789     
34790     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34791     var p = this.property || "text";
34792     var sortType = this.sortType;
34793     var fs = this.folderSort;
34794     var cs = this.caseSensitive === true;
34795     var leafAttr = this.leafAttr || 'leaf';
34796
34797     this.sortFn = function(n1, n2){
34798         if(fs){
34799             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34800                 return 1;
34801             }
34802             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34803                 return -1;
34804             }
34805         }
34806         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34807         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34808         if(v1 < v2){
34809                         return dsc ? +1 : -1;
34810                 }else if(v1 > v2){
34811                         return dsc ? -1 : +1;
34812         }else{
34813                 return 0;
34814         }
34815     };
34816 };
34817
34818 Roo.tree.TreeSorter.prototype = {
34819     doSort : function(node){
34820         node.sort(this.sortFn);
34821     },
34822     
34823     compareNodes : function(n1, n2){
34824         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34825     },
34826     
34827     updateSort : function(tree, node){
34828         if(node.childrenRendered){
34829             this.doSort.defer(1, this, [node]);
34830         }
34831     }
34832 };/*
34833  * Based on:
34834  * Ext JS Library 1.1.1
34835  * Copyright(c) 2006-2007, Ext JS, LLC.
34836  *
34837  * Originally Released Under LGPL - original licence link has changed is not relivant.
34838  *
34839  * Fork - LGPL
34840  * <script type="text/javascript">
34841  */
34842
34843 if(Roo.dd.DropZone){
34844     
34845 Roo.tree.TreeDropZone = function(tree, config){
34846     this.allowParentInsert = false;
34847     this.allowContainerDrop = false;
34848     this.appendOnly = false;
34849     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34850     this.tree = tree;
34851     this.lastInsertClass = "x-tree-no-status";
34852     this.dragOverData = {};
34853 };
34854
34855 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34856     ddGroup : "TreeDD",
34857     scroll:  true,
34858     
34859     expandDelay : 1000,
34860     
34861     expandNode : function(node){
34862         if(node.hasChildNodes() && !node.isExpanded()){
34863             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34864         }
34865     },
34866     
34867     queueExpand : function(node){
34868         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34869     },
34870     
34871     cancelExpand : function(){
34872         if(this.expandProcId){
34873             clearTimeout(this.expandProcId);
34874             this.expandProcId = false;
34875         }
34876     },
34877     
34878     isValidDropPoint : function(n, pt, dd, e, data){
34879         if(!n || !data){ return false; }
34880         var targetNode = n.node;
34881         var dropNode = data.node;
34882         // default drop rules
34883         if(!(targetNode && targetNode.isTarget && pt)){
34884             return false;
34885         }
34886         if(pt == "append" && targetNode.allowChildren === false){
34887             return false;
34888         }
34889         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34890             return false;
34891         }
34892         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34893             return false;
34894         }
34895         // reuse the object
34896         var overEvent = this.dragOverData;
34897         overEvent.tree = this.tree;
34898         overEvent.target = targetNode;
34899         overEvent.data = data;
34900         overEvent.point = pt;
34901         overEvent.source = dd;
34902         overEvent.rawEvent = e;
34903         overEvent.dropNode = dropNode;
34904         overEvent.cancel = false;  
34905         var result = this.tree.fireEvent("nodedragover", overEvent);
34906         return overEvent.cancel === false && result !== false;
34907     },
34908     
34909     getDropPoint : function(e, n, dd)
34910     {
34911         var tn = n.node;
34912         if(tn.isRoot){
34913             return tn.allowChildren !== false ? "append" : false; // always append for root
34914         }
34915         var dragEl = n.ddel;
34916         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34917         var y = Roo.lib.Event.getPageY(e);
34918         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34919         
34920         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34921         var noAppend = tn.allowChildren === false;
34922         if(this.appendOnly || tn.parentNode.allowChildren === false){
34923             return noAppend ? false : "append";
34924         }
34925         var noBelow = false;
34926         if(!this.allowParentInsert){
34927             noBelow = tn.hasChildNodes() && tn.isExpanded();
34928         }
34929         var q = (b - t) / (noAppend ? 2 : 3);
34930         if(y >= t && y < (t + q)){
34931             return "above";
34932         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34933             return "below";
34934         }else{
34935             return "append";
34936         }
34937     },
34938     
34939     onNodeEnter : function(n, dd, e, data)
34940     {
34941         this.cancelExpand();
34942     },
34943     
34944     onNodeOver : function(n, dd, e, data)
34945     {
34946        
34947         var pt = this.getDropPoint(e, n, dd);
34948         var node = n.node;
34949         
34950         // auto node expand check
34951         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34952             this.queueExpand(node);
34953         }else if(pt != "append"){
34954             this.cancelExpand();
34955         }
34956         
34957         // set the insert point style on the target node
34958         var returnCls = this.dropNotAllowed;
34959         if(this.isValidDropPoint(n, pt, dd, e, data)){
34960            if(pt){
34961                var el = n.ddel;
34962                var cls;
34963                if(pt == "above"){
34964                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34965                    cls = "x-tree-drag-insert-above";
34966                }else if(pt == "below"){
34967                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34968                    cls = "x-tree-drag-insert-below";
34969                }else{
34970                    returnCls = "x-tree-drop-ok-append";
34971                    cls = "x-tree-drag-append";
34972                }
34973                if(this.lastInsertClass != cls){
34974                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34975                    this.lastInsertClass = cls;
34976                }
34977            }
34978        }
34979        return returnCls;
34980     },
34981     
34982     onNodeOut : function(n, dd, e, data){
34983         
34984         this.cancelExpand();
34985         this.removeDropIndicators(n);
34986     },
34987     
34988     onNodeDrop : function(n, dd, e, data){
34989         var point = this.getDropPoint(e, n, dd);
34990         var targetNode = n.node;
34991         targetNode.ui.startDrop();
34992         if(!this.isValidDropPoint(n, point, dd, e, data)){
34993             targetNode.ui.endDrop();
34994             return false;
34995         }
34996         // first try to find the drop node
34997         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34998         var dropEvent = {
34999             tree : this.tree,
35000             target: targetNode,
35001             data: data,
35002             point: point,
35003             source: dd,
35004             rawEvent: e,
35005             dropNode: dropNode,
35006             cancel: !dropNode   
35007         };
35008         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
35009         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
35010             targetNode.ui.endDrop();
35011             return false;
35012         }
35013         // allow target changing
35014         targetNode = dropEvent.target;
35015         if(point == "append" && !targetNode.isExpanded()){
35016             targetNode.expand(false, null, function(){
35017                 this.completeDrop(dropEvent);
35018             }.createDelegate(this));
35019         }else{
35020             this.completeDrop(dropEvent);
35021         }
35022         return true;
35023     },
35024     
35025     completeDrop : function(de){
35026         var ns = de.dropNode, p = de.point, t = de.target;
35027         if(!(ns instanceof Array)){
35028             ns = [ns];
35029         }
35030         var n;
35031         for(var i = 0, len = ns.length; i < len; i++){
35032             n = ns[i];
35033             if(p == "above"){
35034                 t.parentNode.insertBefore(n, t);
35035             }else if(p == "below"){
35036                 t.parentNode.insertBefore(n, t.nextSibling);
35037             }else{
35038                 t.appendChild(n);
35039             }
35040         }
35041         n.ui.focus();
35042         if(this.tree.hlDrop){
35043             n.ui.highlight();
35044         }
35045         t.ui.endDrop();
35046         this.tree.fireEvent("nodedrop", de);
35047     },
35048     
35049     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
35050         if(this.tree.hlDrop){
35051             dropNode.ui.focus();
35052             dropNode.ui.highlight();
35053         }
35054         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
35055     },
35056     
35057     getTree : function(){
35058         return this.tree;
35059     },
35060     
35061     removeDropIndicators : function(n){
35062         if(n && n.ddel){
35063             var el = n.ddel;
35064             Roo.fly(el).removeClass([
35065                     "x-tree-drag-insert-above",
35066                     "x-tree-drag-insert-below",
35067                     "x-tree-drag-append"]);
35068             this.lastInsertClass = "_noclass";
35069         }
35070     },
35071     
35072     beforeDragDrop : function(target, e, id){
35073         this.cancelExpand();
35074         return true;
35075     },
35076     
35077     afterRepair : function(data){
35078         if(data && Roo.enableFx){
35079             data.node.ui.highlight();
35080         }
35081         this.hideProxy();
35082     } 
35083     
35084 });
35085
35086 }
35087 /*
35088  * Based on:
35089  * Ext JS Library 1.1.1
35090  * Copyright(c) 2006-2007, Ext JS, LLC.
35091  *
35092  * Originally Released Under LGPL - original licence link has changed is not relivant.
35093  *
35094  * Fork - LGPL
35095  * <script type="text/javascript">
35096  */
35097  
35098
35099 if(Roo.dd.DragZone){
35100 Roo.tree.TreeDragZone = function(tree, config){
35101     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
35102     this.tree = tree;
35103 };
35104
35105 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
35106     ddGroup : "TreeDD",
35107    
35108     onBeforeDrag : function(data, e){
35109         var n = data.node;
35110         return n && n.draggable && !n.disabled;
35111     },
35112      
35113     
35114     onInitDrag : function(e){
35115         var data = this.dragData;
35116         this.tree.getSelectionModel().select(data.node);
35117         this.proxy.update("");
35118         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
35119         this.tree.fireEvent("startdrag", this.tree, data.node, e);
35120     },
35121     
35122     getRepairXY : function(e, data){
35123         return data.node.ui.getDDRepairXY();
35124     },
35125     
35126     onEndDrag : function(data, e){
35127         this.tree.fireEvent("enddrag", this.tree, data.node, e);
35128         
35129         
35130     },
35131     
35132     onValidDrop : function(dd, e, id){
35133         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
35134         this.hideProxy();
35135     },
35136     
35137     beforeInvalidDrop : function(e, id){
35138         // this scrolls the original position back into view
35139         var sm = this.tree.getSelectionModel();
35140         sm.clearSelections();
35141         sm.select(this.dragData.node);
35142     }
35143 });
35144 }/*
35145  * Based on:
35146  * Ext JS Library 1.1.1
35147  * Copyright(c) 2006-2007, Ext JS, LLC.
35148  *
35149  * Originally Released Under LGPL - original licence link has changed is not relivant.
35150  *
35151  * Fork - LGPL
35152  * <script type="text/javascript">
35153  */
35154 /**
35155  * @class Roo.tree.TreeEditor
35156  * @extends Roo.Editor
35157  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
35158  * as the editor field.
35159  * @constructor
35160  * @param {Object} config (used to be the tree panel.)
35161  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
35162  * 
35163  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
35164  * @cfg {Roo.form.TextField|Object} field The field configuration
35165  *
35166  * 
35167  */
35168 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
35169     var tree = config;
35170     var field;
35171     if (oldconfig) { // old style..
35172         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
35173     } else {
35174         // new style..
35175         tree = config.tree;
35176         config.field = config.field  || {};
35177         config.field.xtype = 'TextField';
35178         field = Roo.factory(config.field, Roo.form);
35179     }
35180     config = config || {};
35181     
35182     
35183     this.addEvents({
35184         /**
35185          * @event beforenodeedit
35186          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35187          * false from the handler of this event.
35188          * @param {Editor} this
35189          * @param {Roo.tree.Node} node 
35190          */
35191         "beforenodeedit" : true
35192     });
35193     
35194     //Roo.log(config);
35195     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
35196
35197     this.tree = tree;
35198
35199     tree.on('beforeclick', this.beforeNodeClick, this);
35200     tree.getTreeEl().on('mousedown', this.hide, this);
35201     this.on('complete', this.updateNode, this);
35202     this.on('beforestartedit', this.fitToTree, this);
35203     this.on('startedit', this.bindScroll, this, {delay:10});
35204     this.on('specialkey', this.onSpecialKey, this);
35205 };
35206
35207 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
35208     /**
35209      * @cfg {String} alignment
35210      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
35211      */
35212     alignment: "l-l",
35213     // inherit
35214     autoSize: false,
35215     /**
35216      * @cfg {Boolean} hideEl
35217      * True to hide the bound element while the editor is displayed (defaults to false)
35218      */
35219     hideEl : false,
35220     /**
35221      * @cfg {String} cls
35222      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35223      */
35224     cls: "x-small-editor x-tree-editor",
35225     /**
35226      * @cfg {Boolean} shim
35227      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35228      */
35229     shim:false,
35230     // inherit
35231     shadow:"frame",
35232     /**
35233      * @cfg {Number} maxWidth
35234      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35235      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35236      * scroll and client offsets into account prior to each edit.
35237      */
35238     maxWidth: 250,
35239
35240     editDelay : 350,
35241
35242     // private
35243     fitToTree : function(ed, el){
35244         var td = this.tree.getTreeEl().dom, nd = el.dom;
35245         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35246             td.scrollLeft = nd.offsetLeft;
35247         }
35248         var w = Math.min(
35249                 this.maxWidth,
35250                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35251         this.setSize(w, '');
35252         
35253         return this.fireEvent('beforenodeedit', this, this.editNode);
35254         
35255     },
35256
35257     // private
35258     triggerEdit : function(node){
35259         this.completeEdit();
35260         this.editNode = node;
35261         this.startEdit(node.ui.textNode, node.text);
35262     },
35263
35264     // private
35265     bindScroll : function(){
35266         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35267     },
35268
35269     // private
35270     beforeNodeClick : function(node, e){
35271         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35272         this.lastClick = new Date();
35273         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35274             e.stopEvent();
35275             this.triggerEdit(node);
35276             return false;
35277         }
35278         return true;
35279     },
35280
35281     // private
35282     updateNode : function(ed, value){
35283         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35284         this.editNode.setText(value);
35285     },
35286
35287     // private
35288     onHide : function(){
35289         Roo.tree.TreeEditor.superclass.onHide.call(this);
35290         if(this.editNode){
35291             this.editNode.ui.focus();
35292         }
35293     },
35294
35295     // private
35296     onSpecialKey : function(field, e){
35297         var k = e.getKey();
35298         if(k == e.ESC){
35299             e.stopEvent();
35300             this.cancelEdit();
35301         }else if(k == e.ENTER && !e.hasModifier()){
35302             e.stopEvent();
35303             this.completeEdit();
35304         }
35305     }
35306 });//<Script type="text/javascript">
35307 /*
35308  * Based on:
35309  * Ext JS Library 1.1.1
35310  * Copyright(c) 2006-2007, Ext JS, LLC.
35311  *
35312  * Originally Released Under LGPL - original licence link has changed is not relivant.
35313  *
35314  * Fork - LGPL
35315  * <script type="text/javascript">
35316  */
35317  
35318 /**
35319  * Not documented??? - probably should be...
35320  */
35321
35322 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35323     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35324     
35325     renderElements : function(n, a, targetNode, bulkRender){
35326         //consel.log("renderElements?");
35327         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35328
35329         var t = n.getOwnerTree();
35330         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35331         
35332         var cols = t.columns;
35333         var bw = t.borderWidth;
35334         var c = cols[0];
35335         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35336          var cb = typeof a.checked == "boolean";
35337         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35338         var colcls = 'x-t-' + tid + '-c0';
35339         var buf = [
35340             '<li class="x-tree-node">',
35341             
35342                 
35343                 '<div class="x-tree-node-el ', a.cls,'">',
35344                     // extran...
35345                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35346                 
35347                 
35348                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35349                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35350                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35351                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35352                            (a.iconCls ? ' '+a.iconCls : ''),
35353                            '" unselectable="on" />',
35354                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35355                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35356                              
35357                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35358                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35359                             '<span unselectable="on" qtip="' + tx + '">',
35360                              tx,
35361                              '</span></a>' ,
35362                     '</div>',
35363                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35364                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35365                  ];
35366         for(var i = 1, len = cols.length; i < len; i++){
35367             c = cols[i];
35368             colcls = 'x-t-' + tid + '-c' +i;
35369             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35370             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35371                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35372                       "</div>");
35373          }
35374          
35375          buf.push(
35376             '</a>',
35377             '<div class="x-clear"></div></div>',
35378             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35379             "</li>");
35380         
35381         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35382             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35383                                 n.nextSibling.ui.getEl(), buf.join(""));
35384         }else{
35385             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35386         }
35387         var el = this.wrap.firstChild;
35388         this.elRow = el;
35389         this.elNode = el.firstChild;
35390         this.ranchor = el.childNodes[1];
35391         this.ctNode = this.wrap.childNodes[1];
35392         var cs = el.firstChild.childNodes;
35393         this.indentNode = cs[0];
35394         this.ecNode = cs[1];
35395         this.iconNode = cs[2];
35396         var index = 3;
35397         if(cb){
35398             this.checkbox = cs[3];
35399             index++;
35400         }
35401         this.anchor = cs[index];
35402         
35403         this.textNode = cs[index].firstChild;
35404         
35405         //el.on("click", this.onClick, this);
35406         //el.on("dblclick", this.onDblClick, this);
35407         
35408         
35409        // console.log(this);
35410     },
35411     initEvents : function(){
35412         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35413         
35414             
35415         var a = this.ranchor;
35416
35417         var el = Roo.get(a);
35418
35419         if(Roo.isOpera){ // opera render bug ignores the CSS
35420             el.setStyle("text-decoration", "none");
35421         }
35422
35423         el.on("click", this.onClick, this);
35424         el.on("dblclick", this.onDblClick, this);
35425         el.on("contextmenu", this.onContextMenu, this);
35426         
35427     },
35428     
35429     /*onSelectedChange : function(state){
35430         if(state){
35431             this.focus();
35432             this.addClass("x-tree-selected");
35433         }else{
35434             //this.blur();
35435             this.removeClass("x-tree-selected");
35436         }
35437     },*/
35438     addClass : function(cls){
35439         if(this.elRow){
35440             Roo.fly(this.elRow).addClass(cls);
35441         }
35442         
35443     },
35444     
35445     
35446     removeClass : function(cls){
35447         if(this.elRow){
35448             Roo.fly(this.elRow).removeClass(cls);
35449         }
35450     }
35451
35452     
35453     
35454 });//<Script type="text/javascript">
35455
35456 /*
35457  * Based on:
35458  * Ext JS Library 1.1.1
35459  * Copyright(c) 2006-2007, Ext JS, LLC.
35460  *
35461  * Originally Released Under LGPL - original licence link has changed is not relivant.
35462  *
35463  * Fork - LGPL
35464  * <script type="text/javascript">
35465  */
35466  
35467
35468 /**
35469  * @class Roo.tree.ColumnTree
35470  * @extends Roo.data.TreePanel
35471  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35472  * @cfg {int} borderWidth  compined right/left border allowance
35473  * @constructor
35474  * @param {String/HTMLElement/Element} el The container element
35475  * @param {Object} config
35476  */
35477 Roo.tree.ColumnTree =  function(el, config)
35478 {
35479    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35480    this.addEvents({
35481         /**
35482         * @event resize
35483         * Fire this event on a container when it resizes
35484         * @param {int} w Width
35485         * @param {int} h Height
35486         */
35487        "resize" : true
35488     });
35489     this.on('resize', this.onResize, this);
35490 };
35491
35492 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35493     //lines:false,
35494     
35495     
35496     borderWidth: Roo.isBorderBox ? 0 : 2, 
35497     headEls : false,
35498     
35499     render : function(){
35500         // add the header.....
35501        
35502         Roo.tree.ColumnTree.superclass.render.apply(this);
35503         
35504         this.el.addClass('x-column-tree');
35505         
35506         this.headers = this.el.createChild(
35507             {cls:'x-tree-headers'},this.innerCt.dom);
35508    
35509         var cols = this.columns, c;
35510         var totalWidth = 0;
35511         this.headEls = [];
35512         var  len = cols.length;
35513         for(var i = 0; i < len; i++){
35514              c = cols[i];
35515              totalWidth += c.width;
35516             this.headEls.push(this.headers.createChild({
35517                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35518                  cn: {
35519                      cls:'x-tree-hd-text',
35520                      html: c.header
35521                  },
35522                  style:'width:'+(c.width-this.borderWidth)+'px;'
35523              }));
35524         }
35525         this.headers.createChild({cls:'x-clear'});
35526         // prevent floats from wrapping when clipped
35527         this.headers.setWidth(totalWidth);
35528         //this.innerCt.setWidth(totalWidth);
35529         this.innerCt.setStyle({ overflow: 'auto' });
35530         this.onResize(this.width, this.height);
35531              
35532         
35533     },
35534     onResize : function(w,h)
35535     {
35536         this.height = h;
35537         this.width = w;
35538         // resize cols..
35539         this.innerCt.setWidth(this.width);
35540         this.innerCt.setHeight(this.height-20);
35541         
35542         // headers...
35543         var cols = this.columns, c;
35544         var totalWidth = 0;
35545         var expEl = false;
35546         var len = cols.length;
35547         for(var i = 0; i < len; i++){
35548             c = cols[i];
35549             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35550                 // it's the expander..
35551                 expEl  = this.headEls[i];
35552                 continue;
35553             }
35554             totalWidth += c.width;
35555             
35556         }
35557         if (expEl) {
35558             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35559         }
35560         this.headers.setWidth(w-20);
35561
35562         
35563         
35564         
35565     }
35566 });
35567 /*
35568  * Based on:
35569  * Ext JS Library 1.1.1
35570  * Copyright(c) 2006-2007, Ext JS, LLC.
35571  *
35572  * Originally Released Under LGPL - original licence link has changed is not relivant.
35573  *
35574  * Fork - LGPL
35575  * <script type="text/javascript">
35576  */
35577  
35578 /**
35579  * @class Roo.menu.Menu
35580  * @extends Roo.util.Observable
35581  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35582  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35583  * @constructor
35584  * Creates a new Menu
35585  * @param {Object} config Configuration options
35586  */
35587 Roo.menu.Menu = function(config){
35588     Roo.apply(this, config);
35589     this.id = this.id || Roo.id();
35590     this.addEvents({
35591         /**
35592          * @event beforeshow
35593          * Fires before this menu is displayed
35594          * @param {Roo.menu.Menu} this
35595          */
35596         beforeshow : true,
35597         /**
35598          * @event beforehide
35599          * Fires before this menu is hidden
35600          * @param {Roo.menu.Menu} this
35601          */
35602         beforehide : true,
35603         /**
35604          * @event show
35605          * Fires after this menu is displayed
35606          * @param {Roo.menu.Menu} this
35607          */
35608         show : true,
35609         /**
35610          * @event hide
35611          * Fires after this menu is hidden
35612          * @param {Roo.menu.Menu} this
35613          */
35614         hide : true,
35615         /**
35616          * @event click
35617          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35618          * @param {Roo.menu.Menu} this
35619          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35620          * @param {Roo.EventObject} e
35621          */
35622         click : true,
35623         /**
35624          * @event mouseover
35625          * Fires when the mouse is hovering over this menu
35626          * @param {Roo.menu.Menu} this
35627          * @param {Roo.EventObject} e
35628          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35629          */
35630         mouseover : true,
35631         /**
35632          * @event mouseout
35633          * Fires when the mouse exits this menu
35634          * @param {Roo.menu.Menu} this
35635          * @param {Roo.EventObject} e
35636          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35637          */
35638         mouseout : true,
35639         /**
35640          * @event itemclick
35641          * Fires when a menu item contained in this menu is clicked
35642          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35643          * @param {Roo.EventObject} e
35644          */
35645         itemclick: true
35646     });
35647     if (this.registerMenu) {
35648         Roo.menu.MenuMgr.register(this);
35649     }
35650     
35651     var mis = this.items;
35652     this.items = new Roo.util.MixedCollection();
35653     if(mis){
35654         this.add.apply(this, mis);
35655     }
35656 };
35657
35658 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35659     /**
35660      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35661      */
35662     minWidth : 120,
35663     /**
35664      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35665      * for bottom-right shadow (defaults to "sides")
35666      */
35667     shadow : "sides",
35668     /**
35669      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35670      * this menu (defaults to "tl-tr?")
35671      */
35672     subMenuAlign : "tl-tr?",
35673     /**
35674      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35675      * relative to its element of origin (defaults to "tl-bl?")
35676      */
35677     defaultAlign : "tl-bl?",
35678     /**
35679      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35680      */
35681     allowOtherMenus : false,
35682     /**
35683      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35684      */
35685     registerMenu : true,
35686
35687     hidden:true,
35688
35689     // private
35690     render : function(){
35691         if(this.el){
35692             return;
35693         }
35694         var el = this.el = new Roo.Layer({
35695             cls: "x-menu",
35696             shadow:this.shadow,
35697             constrain: false,
35698             parentEl: this.parentEl || document.body,
35699             zindex:15000
35700         });
35701
35702         this.keyNav = new Roo.menu.MenuNav(this);
35703
35704         if(this.plain){
35705             el.addClass("x-menu-plain");
35706         }
35707         if(this.cls){
35708             el.addClass(this.cls);
35709         }
35710         // generic focus element
35711         this.focusEl = el.createChild({
35712             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35713         });
35714         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35715         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35716         
35717         ul.on("mouseover", this.onMouseOver, this);
35718         ul.on("mouseout", this.onMouseOut, this);
35719         this.items.each(function(item){
35720             if (item.hidden) {
35721                 return;
35722             }
35723             
35724             var li = document.createElement("li");
35725             li.className = "x-menu-list-item";
35726             ul.dom.appendChild(li);
35727             item.render(li, this);
35728         }, this);
35729         this.ul = ul;
35730         this.autoWidth();
35731     },
35732
35733     // private
35734     autoWidth : function(){
35735         var el = this.el, ul = this.ul;
35736         if(!el){
35737             return;
35738         }
35739         var w = this.width;
35740         if(w){
35741             el.setWidth(w);
35742         }else if(Roo.isIE){
35743             el.setWidth(this.minWidth);
35744             var t = el.dom.offsetWidth; // force recalc
35745             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35746         }
35747     },
35748
35749     // private
35750     delayAutoWidth : function(){
35751         if(this.rendered){
35752             if(!this.awTask){
35753                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35754             }
35755             this.awTask.delay(20);
35756         }
35757     },
35758
35759     // private
35760     findTargetItem : function(e){
35761         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35762         if(t && t.menuItemId){
35763             return this.items.get(t.menuItemId);
35764         }
35765     },
35766
35767     // private
35768     onClick : function(e){
35769         Roo.log("menu.onClick");
35770         var t = this.findTargetItem(e);
35771         if(!t){
35772             return;
35773         }
35774         Roo.log(e);
35775         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35776             if(t == this.activeItem && t.shouldDeactivate(e)){
35777                 this.activeItem.deactivate();
35778                 delete this.activeItem;
35779                 return;
35780             }
35781             if(t.canActivate){
35782                 this.setActiveItem(t, true);
35783             }
35784             return;
35785             
35786             
35787         }
35788         
35789         t.onClick(e);
35790         this.fireEvent("click", this, t, e);
35791     },
35792
35793     // private
35794     setActiveItem : function(item, autoExpand){
35795         if(item != this.activeItem){
35796             if(this.activeItem){
35797                 this.activeItem.deactivate();
35798             }
35799             this.activeItem = item;
35800             item.activate(autoExpand);
35801         }else if(autoExpand){
35802             item.expandMenu();
35803         }
35804     },
35805
35806     // private
35807     tryActivate : function(start, step){
35808         var items = this.items;
35809         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35810             var item = items.get(i);
35811             if(!item.disabled && item.canActivate){
35812                 this.setActiveItem(item, false);
35813                 return item;
35814             }
35815         }
35816         return false;
35817     },
35818
35819     // private
35820     onMouseOver : function(e){
35821         var t;
35822         if(t = this.findTargetItem(e)){
35823             if(t.canActivate && !t.disabled){
35824                 this.setActiveItem(t, true);
35825             }
35826         }
35827         this.fireEvent("mouseover", this, e, t);
35828     },
35829
35830     // private
35831     onMouseOut : function(e){
35832         var t;
35833         if(t = this.findTargetItem(e)){
35834             if(t == this.activeItem && t.shouldDeactivate(e)){
35835                 this.activeItem.deactivate();
35836                 delete this.activeItem;
35837             }
35838         }
35839         this.fireEvent("mouseout", this, e, t);
35840     },
35841
35842     /**
35843      * Read-only.  Returns true if the menu is currently displayed, else false.
35844      * @type Boolean
35845      */
35846     isVisible : function(){
35847         return this.el && !this.hidden;
35848     },
35849
35850     /**
35851      * Displays this menu relative to another element
35852      * @param {String/HTMLElement/Roo.Element} element The element to align to
35853      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35854      * the element (defaults to this.defaultAlign)
35855      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35856      */
35857     show : function(el, pos, parentMenu){
35858         this.parentMenu = parentMenu;
35859         if(!this.el){
35860             this.render();
35861         }
35862         this.fireEvent("beforeshow", this);
35863         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35864     },
35865
35866     /**
35867      * Displays this menu at a specific xy position
35868      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35869      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35870      */
35871     showAt : function(xy, parentMenu, /* private: */_e){
35872         this.parentMenu = parentMenu;
35873         if(!this.el){
35874             this.render();
35875         }
35876         if(_e !== false){
35877             this.fireEvent("beforeshow", this);
35878             xy = this.el.adjustForConstraints(xy);
35879         }
35880         this.el.setXY(xy);
35881         this.el.show();
35882         this.hidden = false;
35883         this.focus();
35884         this.fireEvent("show", this);
35885     },
35886
35887     focus : function(){
35888         if(!this.hidden){
35889             this.doFocus.defer(50, this);
35890         }
35891     },
35892
35893     doFocus : function(){
35894         if(!this.hidden){
35895             this.focusEl.focus();
35896         }
35897     },
35898
35899     /**
35900      * Hides this menu and optionally all parent menus
35901      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35902      */
35903     hide : function(deep){
35904         if(this.el && this.isVisible()){
35905             this.fireEvent("beforehide", this);
35906             if(this.activeItem){
35907                 this.activeItem.deactivate();
35908                 this.activeItem = null;
35909             }
35910             this.el.hide();
35911             this.hidden = true;
35912             this.fireEvent("hide", this);
35913         }
35914         if(deep === true && this.parentMenu){
35915             this.parentMenu.hide(true);
35916         }
35917     },
35918
35919     /**
35920      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35921      * Any of the following are valid:
35922      * <ul>
35923      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35924      * <li>An HTMLElement object which will be converted to a menu item</li>
35925      * <li>A menu item config object that will be created as a new menu item</li>
35926      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35927      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35928      * </ul>
35929      * Usage:
35930      * <pre><code>
35931 // Create the menu
35932 var menu = new Roo.menu.Menu();
35933
35934 // Create a menu item to add by reference
35935 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35936
35937 // Add a bunch of items at once using different methods.
35938 // Only the last item added will be returned.
35939 var item = menu.add(
35940     menuItem,                // add existing item by ref
35941     'Dynamic Item',          // new TextItem
35942     '-',                     // new separator
35943     { text: 'Config Item' }  // new item by config
35944 );
35945 </code></pre>
35946      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35947      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35948      */
35949     add : function(){
35950         var a = arguments, l = a.length, item;
35951         for(var i = 0; i < l; i++){
35952             var el = a[i];
35953             if ((typeof(el) == "object") && el.xtype && el.xns) {
35954                 el = Roo.factory(el, Roo.menu);
35955             }
35956             
35957             if(el.render){ // some kind of Item
35958                 item = this.addItem(el);
35959             }else if(typeof el == "string"){ // string
35960                 if(el == "separator" || el == "-"){
35961                     item = this.addSeparator();
35962                 }else{
35963                     item = this.addText(el);
35964                 }
35965             }else if(el.tagName || el.el){ // element
35966                 item = this.addElement(el);
35967             }else if(typeof el == "object"){ // must be menu item config?
35968                 item = this.addMenuItem(el);
35969             }
35970         }
35971         return item;
35972     },
35973
35974     /**
35975      * Returns this menu's underlying {@link Roo.Element} object
35976      * @return {Roo.Element} The element
35977      */
35978     getEl : function(){
35979         if(!this.el){
35980             this.render();
35981         }
35982         return this.el;
35983     },
35984
35985     /**
35986      * Adds a separator bar to the menu
35987      * @return {Roo.menu.Item} The menu item that was added
35988      */
35989     addSeparator : function(){
35990         return this.addItem(new Roo.menu.Separator());
35991     },
35992
35993     /**
35994      * Adds an {@link Roo.Element} object to the menu
35995      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35996      * @return {Roo.menu.Item} The menu item that was added
35997      */
35998     addElement : function(el){
35999         return this.addItem(new Roo.menu.BaseItem(el));
36000     },
36001
36002     /**
36003      * Adds an existing object based on {@link Roo.menu.Item} to the menu
36004      * @param {Roo.menu.Item} item The menu item to add
36005      * @return {Roo.menu.Item} The menu item that was added
36006      */
36007     addItem : function(item){
36008         this.items.add(item);
36009         if(this.ul){
36010             var li = document.createElement("li");
36011             li.className = "x-menu-list-item";
36012             this.ul.dom.appendChild(li);
36013             item.render(li, this);
36014             this.delayAutoWidth();
36015         }
36016         return item;
36017     },
36018
36019     /**
36020      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
36021      * @param {Object} config A MenuItem config object
36022      * @return {Roo.menu.Item} The menu item that was added
36023      */
36024     addMenuItem : function(config){
36025         if(!(config instanceof Roo.menu.Item)){
36026             if(typeof config.checked == "boolean"){ // must be check menu item config?
36027                 config = new Roo.menu.CheckItem(config);
36028             }else{
36029                 config = new Roo.menu.Item(config);
36030             }
36031         }
36032         return this.addItem(config);
36033     },
36034
36035     /**
36036      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
36037      * @param {String} text The text to display in the menu item
36038      * @return {Roo.menu.Item} The menu item that was added
36039      */
36040     addText : function(text){
36041         return this.addItem(new Roo.menu.TextItem({ text : text }));
36042     },
36043
36044     /**
36045      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
36046      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
36047      * @param {Roo.menu.Item} item The menu item to add
36048      * @return {Roo.menu.Item} The menu item that was added
36049      */
36050     insert : function(index, item){
36051         this.items.insert(index, item);
36052         if(this.ul){
36053             var li = document.createElement("li");
36054             li.className = "x-menu-list-item";
36055             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
36056             item.render(li, this);
36057             this.delayAutoWidth();
36058         }
36059         return item;
36060     },
36061
36062     /**
36063      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
36064      * @param {Roo.menu.Item} item The menu item to remove
36065      */
36066     remove : function(item){
36067         this.items.removeKey(item.id);
36068         item.destroy();
36069     },
36070
36071     /**
36072      * Removes and destroys all items in the menu
36073      */
36074     removeAll : function(){
36075         var f;
36076         while(f = this.items.first()){
36077             this.remove(f);
36078         }
36079     }
36080 });
36081
36082 // MenuNav is a private utility class used internally by the Menu
36083 Roo.menu.MenuNav = function(menu){
36084     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
36085     this.scope = this.menu = menu;
36086 };
36087
36088 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
36089     doRelay : function(e, h){
36090         var k = e.getKey();
36091         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
36092             this.menu.tryActivate(0, 1);
36093             return false;
36094         }
36095         return h.call(this.scope || this, e, this.menu);
36096     },
36097
36098     up : function(e, m){
36099         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
36100             m.tryActivate(m.items.length-1, -1);
36101         }
36102     },
36103
36104     down : function(e, m){
36105         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
36106             m.tryActivate(0, 1);
36107         }
36108     },
36109
36110     right : function(e, m){
36111         if(m.activeItem){
36112             m.activeItem.expandMenu(true);
36113         }
36114     },
36115
36116     left : function(e, m){
36117         m.hide();
36118         if(m.parentMenu && m.parentMenu.activeItem){
36119             m.parentMenu.activeItem.activate();
36120         }
36121     },
36122
36123     enter : function(e, m){
36124         if(m.activeItem){
36125             e.stopPropagation();
36126             m.activeItem.onClick(e);
36127             m.fireEvent("click", this, m.activeItem);
36128             return true;
36129         }
36130     }
36131 });/*
36132  * Based on:
36133  * Ext JS Library 1.1.1
36134  * Copyright(c) 2006-2007, Ext JS, LLC.
36135  *
36136  * Originally Released Under LGPL - original licence link has changed is not relivant.
36137  *
36138  * Fork - LGPL
36139  * <script type="text/javascript">
36140  */
36141  
36142 /**
36143  * @class Roo.menu.MenuMgr
36144  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
36145  * @singleton
36146  */
36147 Roo.menu.MenuMgr = function(){
36148    var menus, active, groups = {}, attached = false, lastShow = new Date();
36149
36150    // private - called when first menu is created
36151    function init(){
36152        menus = {};
36153        active = new Roo.util.MixedCollection();
36154        Roo.get(document).addKeyListener(27, function(){
36155            if(active.length > 0){
36156                hideAll();
36157            }
36158        });
36159    }
36160
36161    // private
36162    function hideAll(){
36163        if(active && active.length > 0){
36164            var c = active.clone();
36165            c.each(function(m){
36166                m.hide();
36167            });
36168        }
36169    }
36170
36171    // private
36172    function onHide(m){
36173        active.remove(m);
36174        if(active.length < 1){
36175            Roo.get(document).un("mousedown", onMouseDown);
36176            attached = false;
36177        }
36178    }
36179
36180    // private
36181    function onShow(m){
36182        var last = active.last();
36183        lastShow = new Date();
36184        active.add(m);
36185        if(!attached){
36186            Roo.get(document).on("mousedown", onMouseDown);
36187            attached = true;
36188        }
36189        if(m.parentMenu){
36190           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
36191           m.parentMenu.activeChild = m;
36192        }else if(last && last.isVisible()){
36193           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
36194        }
36195    }
36196
36197    // private
36198    function onBeforeHide(m){
36199        if(m.activeChild){
36200            m.activeChild.hide();
36201        }
36202        if(m.autoHideTimer){
36203            clearTimeout(m.autoHideTimer);
36204            delete m.autoHideTimer;
36205        }
36206    }
36207
36208    // private
36209    function onBeforeShow(m){
36210        var pm = m.parentMenu;
36211        if(!pm && !m.allowOtherMenus){
36212            hideAll();
36213        }else if(pm && pm.activeChild && active != m){
36214            pm.activeChild.hide();
36215        }
36216    }
36217
36218    // private
36219    function onMouseDown(e){
36220        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36221            hideAll();
36222        }
36223    }
36224
36225    // private
36226    function onBeforeCheck(mi, state){
36227        if(state){
36228            var g = groups[mi.group];
36229            for(var i = 0, l = g.length; i < l; i++){
36230                if(g[i] != mi){
36231                    g[i].setChecked(false);
36232                }
36233            }
36234        }
36235    }
36236
36237    return {
36238
36239        /**
36240         * Hides all menus that are currently visible
36241         */
36242        hideAll : function(){
36243             hideAll();  
36244        },
36245
36246        // private
36247        register : function(menu){
36248            if(!menus){
36249                init();
36250            }
36251            menus[menu.id] = menu;
36252            menu.on("beforehide", onBeforeHide);
36253            menu.on("hide", onHide);
36254            menu.on("beforeshow", onBeforeShow);
36255            menu.on("show", onShow);
36256            var g = menu.group;
36257            if(g && menu.events["checkchange"]){
36258                if(!groups[g]){
36259                    groups[g] = [];
36260                }
36261                groups[g].push(menu);
36262                menu.on("checkchange", onCheck);
36263            }
36264        },
36265
36266         /**
36267          * Returns a {@link Roo.menu.Menu} object
36268          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36269          * be used to generate and return a new Menu instance.
36270          */
36271        get : function(menu){
36272            if(typeof menu == "string"){ // menu id
36273                return menus[menu];
36274            }else if(menu.events){  // menu instance
36275                return menu;
36276            }else if(typeof menu.length == 'number'){ // array of menu items?
36277                return new Roo.menu.Menu({items:menu});
36278            }else{ // otherwise, must be a config
36279                return new Roo.menu.Menu(menu);
36280            }
36281        },
36282
36283        // private
36284        unregister : function(menu){
36285            delete menus[menu.id];
36286            menu.un("beforehide", onBeforeHide);
36287            menu.un("hide", onHide);
36288            menu.un("beforeshow", onBeforeShow);
36289            menu.un("show", onShow);
36290            var g = menu.group;
36291            if(g && menu.events["checkchange"]){
36292                groups[g].remove(menu);
36293                menu.un("checkchange", onCheck);
36294            }
36295        },
36296
36297        // private
36298        registerCheckable : function(menuItem){
36299            var g = menuItem.group;
36300            if(g){
36301                if(!groups[g]){
36302                    groups[g] = [];
36303                }
36304                groups[g].push(menuItem);
36305                menuItem.on("beforecheckchange", onBeforeCheck);
36306            }
36307        },
36308
36309        // private
36310        unregisterCheckable : function(menuItem){
36311            var g = menuItem.group;
36312            if(g){
36313                groups[g].remove(menuItem);
36314                menuItem.un("beforecheckchange", onBeforeCheck);
36315            }
36316        }
36317    };
36318 }();/*
36319  * Based on:
36320  * Ext JS Library 1.1.1
36321  * Copyright(c) 2006-2007, Ext JS, LLC.
36322  *
36323  * Originally Released Under LGPL - original licence link has changed is not relivant.
36324  *
36325  * Fork - LGPL
36326  * <script type="text/javascript">
36327  */
36328  
36329
36330 /**
36331  * @class Roo.menu.BaseItem
36332  * @extends Roo.Component
36333  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36334  * management and base configuration options shared by all menu components.
36335  * @constructor
36336  * Creates a new BaseItem
36337  * @param {Object} config Configuration options
36338  */
36339 Roo.menu.BaseItem = function(config){
36340     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36341
36342     this.addEvents({
36343         /**
36344          * @event click
36345          * Fires when this item is clicked
36346          * @param {Roo.menu.BaseItem} this
36347          * @param {Roo.EventObject} e
36348          */
36349         click: true,
36350         /**
36351          * @event activate
36352          * Fires when this item is activated
36353          * @param {Roo.menu.BaseItem} this
36354          */
36355         activate : true,
36356         /**
36357          * @event deactivate
36358          * Fires when this item is deactivated
36359          * @param {Roo.menu.BaseItem} this
36360          */
36361         deactivate : true
36362     });
36363
36364     if(this.handler){
36365         this.on("click", this.handler, this.scope, true);
36366     }
36367 };
36368
36369 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36370     /**
36371      * @cfg {Function} handler
36372      * A function that will handle the click event of this menu item (defaults to undefined)
36373      */
36374     /**
36375      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36376      */
36377     canActivate : false,
36378     
36379      /**
36380      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36381      */
36382     hidden: false,
36383     
36384     /**
36385      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36386      */
36387     activeClass : "x-menu-item-active",
36388     /**
36389      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36390      */
36391     hideOnClick : true,
36392     /**
36393      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36394      */
36395     hideDelay : 100,
36396
36397     // private
36398     ctype: "Roo.menu.BaseItem",
36399
36400     // private
36401     actionMode : "container",
36402
36403     // private
36404     render : function(container, parentMenu){
36405         this.parentMenu = parentMenu;
36406         Roo.menu.BaseItem.superclass.render.call(this, container);
36407         this.container.menuItemId = this.id;
36408     },
36409
36410     // private
36411     onRender : function(container, position){
36412         this.el = Roo.get(this.el);
36413         container.dom.appendChild(this.el.dom);
36414     },
36415
36416     // private
36417     onClick : function(e){
36418         if(!this.disabled && this.fireEvent("click", this, e) !== false
36419                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36420             this.handleClick(e);
36421         }else{
36422             e.stopEvent();
36423         }
36424     },
36425
36426     // private
36427     activate : function(){
36428         if(this.disabled){
36429             return false;
36430         }
36431         var li = this.container;
36432         li.addClass(this.activeClass);
36433         this.region = li.getRegion().adjust(2, 2, -2, -2);
36434         this.fireEvent("activate", this);
36435         return true;
36436     },
36437
36438     // private
36439     deactivate : function(){
36440         this.container.removeClass(this.activeClass);
36441         this.fireEvent("deactivate", this);
36442     },
36443
36444     // private
36445     shouldDeactivate : function(e){
36446         return !this.region || !this.region.contains(e.getPoint());
36447     },
36448
36449     // private
36450     handleClick : function(e){
36451         if(this.hideOnClick){
36452             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36453         }
36454     },
36455
36456     // private
36457     expandMenu : function(autoActivate){
36458         // do nothing
36459     },
36460
36461     // private
36462     hideMenu : function(){
36463         // do nothing
36464     }
36465 });/*
36466  * Based on:
36467  * Ext JS Library 1.1.1
36468  * Copyright(c) 2006-2007, Ext JS, LLC.
36469  *
36470  * Originally Released Under LGPL - original licence link has changed is not relivant.
36471  *
36472  * Fork - LGPL
36473  * <script type="text/javascript">
36474  */
36475  
36476 /**
36477  * @class Roo.menu.Adapter
36478  * @extends Roo.menu.BaseItem
36479  * 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.
36480  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36481  * @constructor
36482  * Creates a new Adapter
36483  * @param {Object} config Configuration options
36484  */
36485 Roo.menu.Adapter = function(component, config){
36486     Roo.menu.Adapter.superclass.constructor.call(this, config);
36487     this.component = component;
36488 };
36489 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36490     // private
36491     canActivate : true,
36492
36493     // private
36494     onRender : function(container, position){
36495         this.component.render(container);
36496         this.el = this.component.getEl();
36497     },
36498
36499     // private
36500     activate : function(){
36501         if(this.disabled){
36502             return false;
36503         }
36504         this.component.focus();
36505         this.fireEvent("activate", this);
36506         return true;
36507     },
36508
36509     // private
36510     deactivate : function(){
36511         this.fireEvent("deactivate", this);
36512     },
36513
36514     // private
36515     disable : function(){
36516         this.component.disable();
36517         Roo.menu.Adapter.superclass.disable.call(this);
36518     },
36519
36520     // private
36521     enable : function(){
36522         this.component.enable();
36523         Roo.menu.Adapter.superclass.enable.call(this);
36524     }
36525 });/*
36526  * Based on:
36527  * Ext JS Library 1.1.1
36528  * Copyright(c) 2006-2007, Ext JS, LLC.
36529  *
36530  * Originally Released Under LGPL - original licence link has changed is not relivant.
36531  *
36532  * Fork - LGPL
36533  * <script type="text/javascript">
36534  */
36535
36536 /**
36537  * @class Roo.menu.TextItem
36538  * @extends Roo.menu.BaseItem
36539  * Adds a static text string to a menu, usually used as either a heading or group separator.
36540  * Note: old style constructor with text is still supported.
36541  * 
36542  * @constructor
36543  * Creates a new TextItem
36544  * @param {Object} cfg Configuration
36545  */
36546 Roo.menu.TextItem = function(cfg){
36547     if (typeof(cfg) == 'string') {
36548         this.text = cfg;
36549     } else {
36550         Roo.apply(this,cfg);
36551     }
36552     
36553     Roo.menu.TextItem.superclass.constructor.call(this);
36554 };
36555
36556 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36557     /**
36558      * @cfg {Boolean} text Text to show on item.
36559      */
36560     text : '',
36561     
36562     /**
36563      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36564      */
36565     hideOnClick : false,
36566     /**
36567      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36568      */
36569     itemCls : "x-menu-text",
36570
36571     // private
36572     onRender : function(){
36573         var s = document.createElement("span");
36574         s.className = this.itemCls;
36575         s.innerHTML = this.text;
36576         this.el = s;
36577         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36578     }
36579 });/*
36580  * Based on:
36581  * Ext JS Library 1.1.1
36582  * Copyright(c) 2006-2007, Ext JS, LLC.
36583  *
36584  * Originally Released Under LGPL - original licence link has changed is not relivant.
36585  *
36586  * Fork - LGPL
36587  * <script type="text/javascript">
36588  */
36589
36590 /**
36591  * @class Roo.menu.Separator
36592  * @extends Roo.menu.BaseItem
36593  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36594  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36595  * @constructor
36596  * @param {Object} config Configuration options
36597  */
36598 Roo.menu.Separator = function(config){
36599     Roo.menu.Separator.superclass.constructor.call(this, config);
36600 };
36601
36602 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36603     /**
36604      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36605      */
36606     itemCls : "x-menu-sep",
36607     /**
36608      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36609      */
36610     hideOnClick : false,
36611
36612     // private
36613     onRender : function(li){
36614         var s = document.createElement("span");
36615         s.className = this.itemCls;
36616         s.innerHTML = "&#160;";
36617         this.el = s;
36618         li.addClass("x-menu-sep-li");
36619         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36620     }
36621 });/*
36622  * Based on:
36623  * Ext JS Library 1.1.1
36624  * Copyright(c) 2006-2007, Ext JS, LLC.
36625  *
36626  * Originally Released Under LGPL - original licence link has changed is not relivant.
36627  *
36628  * Fork - LGPL
36629  * <script type="text/javascript">
36630  */
36631 /**
36632  * @class Roo.menu.Item
36633  * @extends Roo.menu.BaseItem
36634  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36635  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36636  * activation and click handling.
36637  * @constructor
36638  * Creates a new Item
36639  * @param {Object} config Configuration options
36640  */
36641 Roo.menu.Item = function(config){
36642     Roo.menu.Item.superclass.constructor.call(this, config);
36643     if(this.menu){
36644         this.menu = Roo.menu.MenuMgr.get(this.menu);
36645     }
36646 };
36647 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36648     
36649     /**
36650      * @cfg {String} text
36651      * The text to show on the menu item.
36652      */
36653     text: '',
36654      /**
36655      * @cfg {String} HTML to render in menu
36656      * The text to show on the menu item (HTML version).
36657      */
36658     html: '',
36659     /**
36660      * @cfg {String} icon
36661      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36662      */
36663     icon: undefined,
36664     /**
36665      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36666      */
36667     itemCls : "x-menu-item",
36668     /**
36669      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36670      */
36671     canActivate : true,
36672     /**
36673      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36674      */
36675     showDelay: 200,
36676     // doc'd in BaseItem
36677     hideDelay: 200,
36678
36679     // private
36680     ctype: "Roo.menu.Item",
36681     
36682     // private
36683     onRender : function(container, position){
36684         var el = document.createElement("a");
36685         el.hideFocus = true;
36686         el.unselectable = "on";
36687         el.href = this.href || "#";
36688         if(this.hrefTarget){
36689             el.target = this.hrefTarget;
36690         }
36691         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36692         
36693         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36694         
36695         el.innerHTML = String.format(
36696                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36697                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36698         this.el = el;
36699         Roo.menu.Item.superclass.onRender.call(this, container, position);
36700     },
36701
36702     /**
36703      * Sets the text to display in this menu item
36704      * @param {String} text The text to display
36705      * @param {Boolean} isHTML true to indicate text is pure html.
36706      */
36707     setText : function(text, isHTML){
36708         if (isHTML) {
36709             this.html = text;
36710         } else {
36711             this.text = text;
36712             this.html = '';
36713         }
36714         if(this.rendered){
36715             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36716      
36717             this.el.update(String.format(
36718                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36719                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36720             this.parentMenu.autoWidth();
36721         }
36722     },
36723
36724     // private
36725     handleClick : function(e){
36726         if(!this.href){ // if no link defined, stop the event automatically
36727             e.stopEvent();
36728         }
36729         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36730     },
36731
36732     // private
36733     activate : function(autoExpand){
36734         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36735             this.focus();
36736             if(autoExpand){
36737                 this.expandMenu();
36738             }
36739         }
36740         return true;
36741     },
36742
36743     // private
36744     shouldDeactivate : function(e){
36745         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36746             if(this.menu && this.menu.isVisible()){
36747                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36748             }
36749             return true;
36750         }
36751         return false;
36752     },
36753
36754     // private
36755     deactivate : function(){
36756         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36757         this.hideMenu();
36758     },
36759
36760     // private
36761     expandMenu : function(autoActivate){
36762         if(!this.disabled && this.menu){
36763             clearTimeout(this.hideTimer);
36764             delete this.hideTimer;
36765             if(!this.menu.isVisible() && !this.showTimer){
36766                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36767             }else if (this.menu.isVisible() && autoActivate){
36768                 this.menu.tryActivate(0, 1);
36769             }
36770         }
36771     },
36772
36773     // private
36774     deferExpand : function(autoActivate){
36775         delete this.showTimer;
36776         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36777         if(autoActivate){
36778             this.menu.tryActivate(0, 1);
36779         }
36780     },
36781
36782     // private
36783     hideMenu : function(){
36784         clearTimeout(this.showTimer);
36785         delete this.showTimer;
36786         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36787             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36788         }
36789     },
36790
36791     // private
36792     deferHide : function(){
36793         delete this.hideTimer;
36794         this.menu.hide();
36795     }
36796 });/*
36797  * Based on:
36798  * Ext JS Library 1.1.1
36799  * Copyright(c) 2006-2007, Ext JS, LLC.
36800  *
36801  * Originally Released Under LGPL - original licence link has changed is not relivant.
36802  *
36803  * Fork - LGPL
36804  * <script type="text/javascript">
36805  */
36806  
36807 /**
36808  * @class Roo.menu.CheckItem
36809  * @extends Roo.menu.Item
36810  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36811  * @constructor
36812  * Creates a new CheckItem
36813  * @param {Object} config Configuration options
36814  */
36815 Roo.menu.CheckItem = function(config){
36816     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36817     this.addEvents({
36818         /**
36819          * @event beforecheckchange
36820          * Fires before the checked value is set, providing an opportunity to cancel if needed
36821          * @param {Roo.menu.CheckItem} this
36822          * @param {Boolean} checked The new checked value that will be set
36823          */
36824         "beforecheckchange" : true,
36825         /**
36826          * @event checkchange
36827          * Fires after the checked value has been set
36828          * @param {Roo.menu.CheckItem} this
36829          * @param {Boolean} checked The checked value that was set
36830          */
36831         "checkchange" : true
36832     });
36833     if(this.checkHandler){
36834         this.on('checkchange', this.checkHandler, this.scope);
36835     }
36836 };
36837 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36838     /**
36839      * @cfg {String} group
36840      * All check items with the same group name will automatically be grouped into a single-select
36841      * radio button group (defaults to '')
36842      */
36843     /**
36844      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36845      */
36846     itemCls : "x-menu-item x-menu-check-item",
36847     /**
36848      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36849      */
36850     groupClass : "x-menu-group-item",
36851
36852     /**
36853      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36854      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36855      * initialized with checked = true will be rendered as checked.
36856      */
36857     checked: false,
36858
36859     // private
36860     ctype: "Roo.menu.CheckItem",
36861
36862     // private
36863     onRender : function(c){
36864         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36865         if(this.group){
36866             this.el.addClass(this.groupClass);
36867         }
36868         Roo.menu.MenuMgr.registerCheckable(this);
36869         if(this.checked){
36870             this.checked = false;
36871             this.setChecked(true, true);
36872         }
36873     },
36874
36875     // private
36876     destroy : function(){
36877         if(this.rendered){
36878             Roo.menu.MenuMgr.unregisterCheckable(this);
36879         }
36880         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36881     },
36882
36883     /**
36884      * Set the checked state of this item
36885      * @param {Boolean} checked The new checked value
36886      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36887      */
36888     setChecked : function(state, suppressEvent){
36889         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36890             if(this.container){
36891                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36892             }
36893             this.checked = state;
36894             if(suppressEvent !== true){
36895                 this.fireEvent("checkchange", this, state);
36896             }
36897         }
36898     },
36899
36900     // private
36901     handleClick : function(e){
36902        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36903            this.setChecked(!this.checked);
36904        }
36905        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36906     }
36907 });/*
36908  * Based on:
36909  * Ext JS Library 1.1.1
36910  * Copyright(c) 2006-2007, Ext JS, LLC.
36911  *
36912  * Originally Released Under LGPL - original licence link has changed is not relivant.
36913  *
36914  * Fork - LGPL
36915  * <script type="text/javascript">
36916  */
36917  
36918 /**
36919  * @class Roo.menu.DateItem
36920  * @extends Roo.menu.Adapter
36921  * A menu item that wraps the {@link Roo.DatPicker} component.
36922  * @constructor
36923  * Creates a new DateItem
36924  * @param {Object} config Configuration options
36925  */
36926 Roo.menu.DateItem = function(config){
36927     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36928     /** The Roo.DatePicker object @type Roo.DatePicker */
36929     this.picker = this.component;
36930     this.addEvents({select: true});
36931     
36932     this.picker.on("render", function(picker){
36933         picker.getEl().swallowEvent("click");
36934         picker.container.addClass("x-menu-date-item");
36935     });
36936
36937     this.picker.on("select", this.onSelect, this);
36938 };
36939
36940 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36941     // private
36942     onSelect : function(picker, date){
36943         this.fireEvent("select", this, date, picker);
36944         Roo.menu.DateItem.superclass.handleClick.call(this);
36945     }
36946 });/*
36947  * Based on:
36948  * Ext JS Library 1.1.1
36949  * Copyright(c) 2006-2007, Ext JS, LLC.
36950  *
36951  * Originally Released Under LGPL - original licence link has changed is not relivant.
36952  *
36953  * Fork - LGPL
36954  * <script type="text/javascript">
36955  */
36956  
36957 /**
36958  * @class Roo.menu.ColorItem
36959  * @extends Roo.menu.Adapter
36960  * A menu item that wraps the {@link Roo.ColorPalette} component.
36961  * @constructor
36962  * Creates a new ColorItem
36963  * @param {Object} config Configuration options
36964  */
36965 Roo.menu.ColorItem = function(config){
36966     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36967     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36968     this.palette = this.component;
36969     this.relayEvents(this.palette, ["select"]);
36970     if(this.selectHandler){
36971         this.on('select', this.selectHandler, this.scope);
36972     }
36973 };
36974 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36975  * Based on:
36976  * Ext JS Library 1.1.1
36977  * Copyright(c) 2006-2007, Ext JS, LLC.
36978  *
36979  * Originally Released Under LGPL - original licence link has changed is not relivant.
36980  *
36981  * Fork - LGPL
36982  * <script type="text/javascript">
36983  */
36984  
36985
36986 /**
36987  * @class Roo.menu.DateMenu
36988  * @extends Roo.menu.Menu
36989  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36990  * @constructor
36991  * Creates a new DateMenu
36992  * @param {Object} config Configuration options
36993  */
36994 Roo.menu.DateMenu = function(config){
36995     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36996     this.plain = true;
36997     var di = new Roo.menu.DateItem(config);
36998     this.add(di);
36999     /**
37000      * The {@link Roo.DatePicker} instance for this DateMenu
37001      * @type DatePicker
37002      */
37003     this.picker = di.picker;
37004     /**
37005      * @event select
37006      * @param {DatePicker} picker
37007      * @param {Date} date
37008      */
37009     this.relayEvents(di, ["select"]);
37010     this.on('beforeshow', function(){
37011         if(this.picker){
37012             this.picker.hideMonthPicker(false);
37013         }
37014     }, this);
37015 };
37016 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
37017     cls:'x-date-menu'
37018 });/*
37019  * Based on:
37020  * Ext JS Library 1.1.1
37021  * Copyright(c) 2006-2007, Ext JS, LLC.
37022  *
37023  * Originally Released Under LGPL - original licence link has changed is not relivant.
37024  *
37025  * Fork - LGPL
37026  * <script type="text/javascript">
37027  */
37028  
37029
37030 /**
37031  * @class Roo.menu.ColorMenu
37032  * @extends Roo.menu.Menu
37033  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
37034  * @constructor
37035  * Creates a new ColorMenu
37036  * @param {Object} config Configuration options
37037  */
37038 Roo.menu.ColorMenu = function(config){
37039     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
37040     this.plain = true;
37041     var ci = new Roo.menu.ColorItem(config);
37042     this.add(ci);
37043     /**
37044      * The {@link Roo.ColorPalette} instance for this ColorMenu
37045      * @type ColorPalette
37046      */
37047     this.palette = ci.palette;
37048     /**
37049      * @event select
37050      * @param {ColorPalette} palette
37051      * @param {String} color
37052      */
37053     this.relayEvents(ci, ["select"]);
37054 };
37055 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
37056  * Based on:
37057  * Ext JS Library 1.1.1
37058  * Copyright(c) 2006-2007, Ext JS, LLC.
37059  *
37060  * Originally Released Under LGPL - original licence link has changed is not relivant.
37061  *
37062  * Fork - LGPL
37063  * <script type="text/javascript">
37064  */
37065  
37066 /**
37067  * @class Roo.form.Field
37068  * @extends Roo.BoxComponent
37069  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
37070  * @constructor
37071  * Creates a new Field
37072  * @param {Object} config Configuration options
37073  */
37074 Roo.form.Field = function(config){
37075     Roo.form.Field.superclass.constructor.call(this, config);
37076 };
37077
37078 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
37079     /**
37080      * @cfg {String} fieldLabel Label to use when rendering a form.
37081      */
37082        /**
37083      * @cfg {String} qtip Mouse over tip
37084      */
37085      
37086     /**
37087      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
37088      */
37089     invalidClass : "x-form-invalid",
37090     /**
37091      * @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")
37092      */
37093     invalidText : "The value in this field is invalid",
37094     /**
37095      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
37096      */
37097     focusClass : "x-form-focus",
37098     /**
37099      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
37100       automatic validation (defaults to "keyup").
37101      */
37102     validationEvent : "keyup",
37103     /**
37104      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
37105      */
37106     validateOnBlur : true,
37107     /**
37108      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
37109      */
37110     validationDelay : 250,
37111     /**
37112      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37113      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
37114      */
37115     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
37116     /**
37117      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
37118      */
37119     fieldClass : "x-form-field",
37120     /**
37121      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
37122      *<pre>
37123 Value         Description
37124 -----------   ----------------------------------------------------------------------
37125 qtip          Display a quick tip when the user hovers over the field
37126 title         Display a default browser title attribute popup
37127 under         Add a block div beneath the field containing the error text
37128 side          Add an error icon to the right of the field with a popup on hover
37129 [element id]  Add the error text directly to the innerHTML of the specified element
37130 </pre>
37131      */
37132     msgTarget : 'qtip',
37133     /**
37134      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
37135      */
37136     msgFx : 'normal',
37137
37138     /**
37139      * @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.
37140      */
37141     readOnly : false,
37142
37143     /**
37144      * @cfg {Boolean} disabled True to disable the field (defaults to false).
37145      */
37146     disabled : false,
37147
37148     /**
37149      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
37150      */
37151     inputType : undefined,
37152     
37153     /**
37154      * @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).
37155          */
37156         tabIndex : undefined,
37157         
37158     // private
37159     isFormField : true,
37160
37161     // private
37162     hasFocus : false,
37163     /**
37164      * @property {Roo.Element} fieldEl
37165      * Element Containing the rendered Field (with label etc.)
37166      */
37167     /**
37168      * @cfg {Mixed} value A value to initialize this field with.
37169      */
37170     value : undefined,
37171
37172     /**
37173      * @cfg {String} name The field's HTML name attribute.
37174      */
37175     /**
37176      * @cfg {String} cls A CSS class to apply to the field's underlying element.
37177      */
37178
37179         // private ??
37180         initComponent : function(){
37181         Roo.form.Field.superclass.initComponent.call(this);
37182         this.addEvents({
37183             /**
37184              * @event focus
37185              * Fires when this field receives input focus.
37186              * @param {Roo.form.Field} this
37187              */
37188             focus : true,
37189             /**
37190              * @event blur
37191              * Fires when this field loses input focus.
37192              * @param {Roo.form.Field} this
37193              */
37194             blur : true,
37195             /**
37196              * @event specialkey
37197              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
37198              * {@link Roo.EventObject#getKey} to determine which key was pressed.
37199              * @param {Roo.form.Field} this
37200              * @param {Roo.EventObject} e The event object
37201              */
37202             specialkey : true,
37203             /**
37204              * @event change
37205              * Fires just before the field blurs if the field value has changed.
37206              * @param {Roo.form.Field} this
37207              * @param {Mixed} newValue The new value
37208              * @param {Mixed} oldValue The original value
37209              */
37210             change : true,
37211             /**
37212              * @event invalid
37213              * Fires after the field has been marked as invalid.
37214              * @param {Roo.form.Field} this
37215              * @param {String} msg The validation message
37216              */
37217             invalid : true,
37218             /**
37219              * @event valid
37220              * Fires after the field has been validated with no errors.
37221              * @param {Roo.form.Field} this
37222              */
37223             valid : true,
37224              /**
37225              * @event keyup
37226              * Fires after the key up
37227              * @param {Roo.form.Field} this
37228              * @param {Roo.EventObject}  e The event Object
37229              */
37230             keyup : true
37231         });
37232     },
37233
37234     /**
37235      * Returns the name attribute of the field if available
37236      * @return {String} name The field name
37237      */
37238     getName: function(){
37239          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37240     },
37241
37242     // private
37243     onRender : function(ct, position){
37244         Roo.form.Field.superclass.onRender.call(this, ct, position);
37245         if(!this.el){
37246             var cfg = this.getAutoCreate();
37247             if(!cfg.name){
37248                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37249             }
37250             if (!cfg.name.length) {
37251                 delete cfg.name;
37252             }
37253             if(this.inputType){
37254                 cfg.type = this.inputType;
37255             }
37256             this.el = ct.createChild(cfg, position);
37257         }
37258         var type = this.el.dom.type;
37259         if(type){
37260             if(type == 'password'){
37261                 type = 'text';
37262             }
37263             this.el.addClass('x-form-'+type);
37264         }
37265         if(this.readOnly){
37266             this.el.dom.readOnly = true;
37267         }
37268         if(this.tabIndex !== undefined){
37269             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37270         }
37271
37272         this.el.addClass([this.fieldClass, this.cls]);
37273         this.initValue();
37274     },
37275
37276     /**
37277      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37278      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37279      * @return {Roo.form.Field} this
37280      */
37281     applyTo : function(target){
37282         this.allowDomMove = false;
37283         this.el = Roo.get(target);
37284         this.render(this.el.dom.parentNode);
37285         return this;
37286     },
37287
37288     // private
37289     initValue : function(){
37290         if(this.value !== undefined){
37291             this.setValue(this.value);
37292         }else if(this.el.dom.value.length > 0){
37293             this.setValue(this.el.dom.value);
37294         }
37295     },
37296
37297     /**
37298      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37299      */
37300     isDirty : function() {
37301         if(this.disabled) {
37302             return false;
37303         }
37304         return String(this.getValue()) !== String(this.originalValue);
37305     },
37306
37307     // private
37308     afterRender : function(){
37309         Roo.form.Field.superclass.afterRender.call(this);
37310         this.initEvents();
37311     },
37312
37313     // private
37314     fireKey : function(e){
37315         //Roo.log('field ' + e.getKey());
37316         if(e.isNavKeyPress()){
37317             this.fireEvent("specialkey", this, e);
37318         }
37319     },
37320
37321     /**
37322      * Resets the current field value to the originally loaded value and clears any validation messages
37323      */
37324     reset : function(){
37325         this.setValue(this.resetValue);
37326         this.clearInvalid();
37327     },
37328
37329     // private
37330     initEvents : function(){
37331         // safari killled keypress - so keydown is now used..
37332         this.el.on("keydown" , this.fireKey,  this);
37333         this.el.on("focus", this.onFocus,  this);
37334         this.el.on("blur", this.onBlur,  this);
37335         this.el.relayEvent('keyup', this);
37336
37337         // reference to original value for reset
37338         this.originalValue = this.getValue();
37339         this.resetValue =  this.getValue();
37340     },
37341
37342     // private
37343     onFocus : function(){
37344         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37345             this.el.addClass(this.focusClass);
37346         }
37347         if(!this.hasFocus){
37348             this.hasFocus = true;
37349             this.startValue = this.getValue();
37350             this.fireEvent("focus", this);
37351         }
37352     },
37353
37354     beforeBlur : Roo.emptyFn,
37355
37356     // private
37357     onBlur : function(){
37358         this.beforeBlur();
37359         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37360             this.el.removeClass(this.focusClass);
37361         }
37362         this.hasFocus = false;
37363         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37364             this.validate();
37365         }
37366         var v = this.getValue();
37367         if(String(v) !== String(this.startValue)){
37368             this.fireEvent('change', this, v, this.startValue);
37369         }
37370         this.fireEvent("blur", this);
37371     },
37372
37373     /**
37374      * Returns whether or not the field value is currently valid
37375      * @param {Boolean} preventMark True to disable marking the field invalid
37376      * @return {Boolean} True if the value is valid, else false
37377      */
37378     isValid : function(preventMark){
37379         if(this.disabled){
37380             return true;
37381         }
37382         var restore = this.preventMark;
37383         this.preventMark = preventMark === true;
37384         var v = this.validateValue(this.processValue(this.getRawValue()));
37385         this.preventMark = restore;
37386         return v;
37387     },
37388
37389     /**
37390      * Validates the field value
37391      * @return {Boolean} True if the value is valid, else false
37392      */
37393     validate : function(){
37394         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37395             this.clearInvalid();
37396             return true;
37397         }
37398         return false;
37399     },
37400
37401     processValue : function(value){
37402         return value;
37403     },
37404
37405     // private
37406     // Subclasses should provide the validation implementation by overriding this
37407     validateValue : function(value){
37408         return true;
37409     },
37410
37411     /**
37412      * Mark this field as invalid
37413      * @param {String} msg The validation message
37414      */
37415     markInvalid : function(msg){
37416         if(!this.rendered || this.preventMark){ // not rendered
37417             return;
37418         }
37419         
37420         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37421         
37422         obj.el.addClass(this.invalidClass);
37423         msg = msg || this.invalidText;
37424         switch(this.msgTarget){
37425             case 'qtip':
37426                 obj.el.dom.qtip = msg;
37427                 obj.el.dom.qclass = 'x-form-invalid-tip';
37428                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37429                     Roo.QuickTips.enable();
37430                 }
37431                 break;
37432             case 'title':
37433                 this.el.dom.title = msg;
37434                 break;
37435             case 'under':
37436                 if(!this.errorEl){
37437                     var elp = this.el.findParent('.x-form-element', 5, true);
37438                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37439                     this.errorEl.setWidth(elp.getWidth(true)-20);
37440                 }
37441                 this.errorEl.update(msg);
37442                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37443                 break;
37444             case 'side':
37445                 if(!this.errorIcon){
37446                     var elp = this.el.findParent('.x-form-element', 5, true);
37447                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37448                 }
37449                 this.alignErrorIcon();
37450                 this.errorIcon.dom.qtip = msg;
37451                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37452                 this.errorIcon.show();
37453                 this.on('resize', this.alignErrorIcon, this);
37454                 break;
37455             default:
37456                 var t = Roo.getDom(this.msgTarget);
37457                 t.innerHTML = msg;
37458                 t.style.display = this.msgDisplay;
37459                 break;
37460         }
37461         this.fireEvent('invalid', this, msg);
37462     },
37463
37464     // private
37465     alignErrorIcon : function(){
37466         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37467     },
37468
37469     /**
37470      * Clear any invalid styles/messages for this field
37471      */
37472     clearInvalid : function(){
37473         if(!this.rendered || this.preventMark){ // not rendered
37474             return;
37475         }
37476         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37477         
37478         obj.el.removeClass(this.invalidClass);
37479         switch(this.msgTarget){
37480             case 'qtip':
37481                 obj.el.dom.qtip = '';
37482                 break;
37483             case 'title':
37484                 this.el.dom.title = '';
37485                 break;
37486             case 'under':
37487                 if(this.errorEl){
37488                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37489                 }
37490                 break;
37491             case 'side':
37492                 if(this.errorIcon){
37493                     this.errorIcon.dom.qtip = '';
37494                     this.errorIcon.hide();
37495                     this.un('resize', this.alignErrorIcon, this);
37496                 }
37497                 break;
37498             default:
37499                 var t = Roo.getDom(this.msgTarget);
37500                 t.innerHTML = '';
37501                 t.style.display = 'none';
37502                 break;
37503         }
37504         this.fireEvent('valid', this);
37505     },
37506
37507     /**
37508      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37509      * @return {Mixed} value The field value
37510      */
37511     getRawValue : function(){
37512         var v = this.el.getValue();
37513         
37514         return v;
37515     },
37516
37517     /**
37518      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37519      * @return {Mixed} value The field value
37520      */
37521     getValue : function(){
37522         var v = this.el.getValue();
37523          
37524         return v;
37525     },
37526
37527     /**
37528      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37529      * @param {Mixed} value The value to set
37530      */
37531     setRawValue : function(v){
37532         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37533     },
37534
37535     /**
37536      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37537      * @param {Mixed} value The value to set
37538      */
37539     setValue : function(v){
37540         this.value = v;
37541         if(this.rendered){
37542             this.el.dom.value = (v === null || v === undefined ? '' : v);
37543              this.validate();
37544         }
37545     },
37546
37547     adjustSize : function(w, h){
37548         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37549         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37550         return s;
37551     },
37552
37553     adjustWidth : function(tag, w){
37554         tag = tag.toLowerCase();
37555         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37556             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37557                 if(tag == 'input'){
37558                     return w + 2;
37559                 }
37560                 if(tag == 'textarea'){
37561                     return w-2;
37562                 }
37563             }else if(Roo.isOpera){
37564                 if(tag == 'input'){
37565                     return w + 2;
37566                 }
37567                 if(tag == 'textarea'){
37568                     return w-2;
37569                 }
37570             }
37571         }
37572         return w;
37573     }
37574 });
37575
37576
37577 // anything other than normal should be considered experimental
37578 Roo.form.Field.msgFx = {
37579     normal : {
37580         show: function(msgEl, f){
37581             msgEl.setDisplayed('block');
37582         },
37583
37584         hide : function(msgEl, f){
37585             msgEl.setDisplayed(false).update('');
37586         }
37587     },
37588
37589     slide : {
37590         show: function(msgEl, f){
37591             msgEl.slideIn('t', {stopFx:true});
37592         },
37593
37594         hide : function(msgEl, f){
37595             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37596         }
37597     },
37598
37599     slideRight : {
37600         show: function(msgEl, f){
37601             msgEl.fixDisplay();
37602             msgEl.alignTo(f.el, 'tl-tr');
37603             msgEl.slideIn('l', {stopFx:true});
37604         },
37605
37606         hide : function(msgEl, f){
37607             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37608         }
37609     }
37610 };/*
37611  * Based on:
37612  * Ext JS Library 1.1.1
37613  * Copyright(c) 2006-2007, Ext JS, LLC.
37614  *
37615  * Originally Released Under LGPL - original licence link has changed is not relivant.
37616  *
37617  * Fork - LGPL
37618  * <script type="text/javascript">
37619  */
37620  
37621
37622 /**
37623  * @class Roo.form.TextField
37624  * @extends Roo.form.Field
37625  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37626  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37627  * @constructor
37628  * Creates a new TextField
37629  * @param {Object} config Configuration options
37630  */
37631 Roo.form.TextField = function(config){
37632     Roo.form.TextField.superclass.constructor.call(this, config);
37633     this.addEvents({
37634         /**
37635          * @event autosize
37636          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37637          * according to the default logic, but this event provides a hook for the developer to apply additional
37638          * logic at runtime to resize the field if needed.
37639              * @param {Roo.form.Field} this This text field
37640              * @param {Number} width The new field width
37641              */
37642         autosize : true
37643     });
37644 };
37645
37646 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37647     /**
37648      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37649      */
37650     grow : false,
37651     /**
37652      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37653      */
37654     growMin : 30,
37655     /**
37656      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37657      */
37658     growMax : 800,
37659     /**
37660      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37661      */
37662     vtype : null,
37663     /**
37664      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37665      */
37666     maskRe : null,
37667     /**
37668      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37669      */
37670     disableKeyFilter : false,
37671     /**
37672      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37673      */
37674     allowBlank : true,
37675     /**
37676      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37677      */
37678     minLength : 0,
37679     /**
37680      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37681      */
37682     maxLength : Number.MAX_VALUE,
37683     /**
37684      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37685      */
37686     minLengthText : "The minimum length for this field is {0}",
37687     /**
37688      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37689      */
37690     maxLengthText : "The maximum length for this field is {0}",
37691     /**
37692      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37693      */
37694     selectOnFocus : false,
37695     /**
37696      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37697      */
37698     blankText : "This field is required",
37699     /**
37700      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37701      * If available, this function will be called only after the basic validators all return true, and will be passed the
37702      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37703      */
37704     validator : null,
37705     /**
37706      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37707      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37708      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37709      */
37710     regex : null,
37711     /**
37712      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37713      */
37714     regexText : "",
37715     /**
37716      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37717      */
37718     emptyText : null,
37719    
37720
37721     // private
37722     initEvents : function()
37723     {
37724         if (this.emptyText) {
37725             this.el.attr('placeholder', this.emptyText);
37726         }
37727         
37728         Roo.form.TextField.superclass.initEvents.call(this);
37729         if(this.validationEvent == 'keyup'){
37730             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37731             this.el.on('keyup', this.filterValidation, this);
37732         }
37733         else if(this.validationEvent !== false){
37734             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37735         }
37736         
37737         if(this.selectOnFocus){
37738             this.on("focus", this.preFocus, this);
37739             
37740         }
37741         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37742             this.el.on("keypress", this.filterKeys, this);
37743         }
37744         if(this.grow){
37745             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37746             this.el.on("click", this.autoSize,  this);
37747         }
37748         if(this.el.is('input[type=password]') && Roo.isSafari){
37749             this.el.on('keydown', this.SafariOnKeyDown, this);
37750         }
37751     },
37752
37753     processValue : function(value){
37754         if(this.stripCharsRe){
37755             var newValue = value.replace(this.stripCharsRe, '');
37756             if(newValue !== value){
37757                 this.setRawValue(newValue);
37758                 return newValue;
37759             }
37760         }
37761         return value;
37762     },
37763
37764     filterValidation : function(e){
37765         if(!e.isNavKeyPress()){
37766             this.validationTask.delay(this.validationDelay);
37767         }
37768     },
37769
37770     // private
37771     onKeyUp : function(e){
37772         if(!e.isNavKeyPress()){
37773             this.autoSize();
37774         }
37775     },
37776
37777     /**
37778      * Resets the current field value to the originally-loaded value and clears any validation messages.
37779      *  
37780      */
37781     reset : function(){
37782         Roo.form.TextField.superclass.reset.call(this);
37783        
37784     },
37785
37786     
37787     // private
37788     preFocus : function(){
37789         
37790         if(this.selectOnFocus){
37791             this.el.dom.select();
37792         }
37793     },
37794
37795     
37796     // private
37797     filterKeys : function(e){
37798         var k = e.getKey();
37799         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37800             return;
37801         }
37802         var c = e.getCharCode(), cc = String.fromCharCode(c);
37803         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37804             return;
37805         }
37806         if(!this.maskRe.test(cc)){
37807             e.stopEvent();
37808         }
37809     },
37810
37811     setValue : function(v){
37812         
37813         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37814         
37815         this.autoSize();
37816     },
37817
37818     /**
37819      * Validates a value according to the field's validation rules and marks the field as invalid
37820      * if the validation fails
37821      * @param {Mixed} value The value to validate
37822      * @return {Boolean} True if the value is valid, else false
37823      */
37824     validateValue : function(value){
37825         if(value.length < 1)  { // if it's blank
37826              if(this.allowBlank){
37827                 this.clearInvalid();
37828                 return true;
37829              }else{
37830                 this.markInvalid(this.blankText);
37831                 return false;
37832              }
37833         }
37834         if(value.length < this.minLength){
37835             this.markInvalid(String.format(this.minLengthText, this.minLength));
37836             return false;
37837         }
37838         if(value.length > this.maxLength){
37839             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37840             return false;
37841         }
37842         if(this.vtype){
37843             var vt = Roo.form.VTypes;
37844             if(!vt[this.vtype](value, this)){
37845                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37846                 return false;
37847             }
37848         }
37849         if(typeof this.validator == "function"){
37850             var msg = this.validator(value);
37851             if(msg !== true){
37852                 this.markInvalid(msg);
37853                 return false;
37854             }
37855         }
37856         if(this.regex && !this.regex.test(value)){
37857             this.markInvalid(this.regexText);
37858             return false;
37859         }
37860         return true;
37861     },
37862
37863     /**
37864      * Selects text in this field
37865      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37866      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37867      */
37868     selectText : function(start, end){
37869         var v = this.getRawValue();
37870         if(v.length > 0){
37871             start = start === undefined ? 0 : start;
37872             end = end === undefined ? v.length : end;
37873             var d = this.el.dom;
37874             if(d.setSelectionRange){
37875                 d.setSelectionRange(start, end);
37876             }else if(d.createTextRange){
37877                 var range = d.createTextRange();
37878                 range.moveStart("character", start);
37879                 range.moveEnd("character", v.length-end);
37880                 range.select();
37881             }
37882         }
37883     },
37884
37885     /**
37886      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37887      * This only takes effect if grow = true, and fires the autosize event.
37888      */
37889     autoSize : function(){
37890         if(!this.grow || !this.rendered){
37891             return;
37892         }
37893         if(!this.metrics){
37894             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37895         }
37896         var el = this.el;
37897         var v = el.dom.value;
37898         var d = document.createElement('div');
37899         d.appendChild(document.createTextNode(v));
37900         v = d.innerHTML;
37901         d = null;
37902         v += "&#160;";
37903         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37904         this.el.setWidth(w);
37905         this.fireEvent("autosize", this, w);
37906     },
37907     
37908     // private
37909     SafariOnKeyDown : function(event)
37910     {
37911         // this is a workaround for a password hang bug on chrome/ webkit.
37912         
37913         var isSelectAll = false;
37914         
37915         if(this.el.dom.selectionEnd > 0){
37916             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37917         }
37918         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37919             event.preventDefault();
37920             this.setValue('');
37921             return;
37922         }
37923         
37924         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
37925             
37926             event.preventDefault();
37927             // this is very hacky as keydown always get's upper case.
37928             
37929             var cc = String.fromCharCode(event.getCharCode());
37930             
37931             
37932             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37933             
37934         }
37935         
37936         
37937     }
37938 });/*
37939  * Based on:
37940  * Ext JS Library 1.1.1
37941  * Copyright(c) 2006-2007, Ext JS, LLC.
37942  *
37943  * Originally Released Under LGPL - original licence link has changed is not relivant.
37944  *
37945  * Fork - LGPL
37946  * <script type="text/javascript">
37947  */
37948  
37949 /**
37950  * @class Roo.form.Hidden
37951  * @extends Roo.form.TextField
37952  * Simple Hidden element used on forms 
37953  * 
37954  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37955  * 
37956  * @constructor
37957  * Creates a new Hidden form element.
37958  * @param {Object} config Configuration options
37959  */
37960
37961
37962
37963 // easy hidden field...
37964 Roo.form.Hidden = function(config){
37965     Roo.form.Hidden.superclass.constructor.call(this, config);
37966 };
37967   
37968 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37969     fieldLabel:      '',
37970     inputType:      'hidden',
37971     width:          50,
37972     allowBlank:     true,
37973     labelSeparator: '',
37974     hidden:         true,
37975     itemCls :       'x-form-item-display-none'
37976
37977
37978 });
37979
37980
37981 /*
37982  * Based on:
37983  * Ext JS Library 1.1.1
37984  * Copyright(c) 2006-2007, Ext JS, LLC.
37985  *
37986  * Originally Released Under LGPL - original licence link has changed is not relivant.
37987  *
37988  * Fork - LGPL
37989  * <script type="text/javascript">
37990  */
37991  
37992 /**
37993  * @class Roo.form.TriggerField
37994  * @extends Roo.form.TextField
37995  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37996  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37997  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37998  * for which you can provide a custom implementation.  For example:
37999  * <pre><code>
38000 var trigger = new Roo.form.TriggerField();
38001 trigger.onTriggerClick = myTriggerFn;
38002 trigger.applyTo('my-field');
38003 </code></pre>
38004  *
38005  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
38006  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
38007  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
38008  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
38009  * @constructor
38010  * Create a new TriggerField.
38011  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
38012  * to the base TextField)
38013  */
38014 Roo.form.TriggerField = function(config){
38015     this.mimicing = false;
38016     Roo.form.TriggerField.superclass.constructor.call(this, config);
38017 };
38018
38019 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
38020     /**
38021      * @cfg {String} triggerClass A CSS class to apply to the trigger
38022      */
38023     /**
38024      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38025      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
38026      */
38027     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
38028     /**
38029      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
38030      */
38031     hideTrigger:false,
38032
38033     /** @cfg {Boolean} grow @hide */
38034     /** @cfg {Number} growMin @hide */
38035     /** @cfg {Number} growMax @hide */
38036
38037     /**
38038      * @hide 
38039      * @method
38040      */
38041     autoSize: Roo.emptyFn,
38042     // private
38043     monitorTab : true,
38044     // private
38045     deferHeight : true,
38046
38047     
38048     actionMode : 'wrap',
38049     // private
38050     onResize : function(w, h){
38051         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
38052         if(typeof w == 'number'){
38053             var x = w - this.trigger.getWidth();
38054             this.el.setWidth(this.adjustWidth('input', x));
38055             this.trigger.setStyle('left', x+'px');
38056         }
38057     },
38058
38059     // private
38060     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38061
38062     // private
38063     getResizeEl : function(){
38064         return this.wrap;
38065     },
38066
38067     // private
38068     getPositionEl : function(){
38069         return this.wrap;
38070     },
38071
38072     // private
38073     alignErrorIcon : function(){
38074         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
38075     },
38076
38077     // private
38078     onRender : function(ct, position){
38079         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
38080         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
38081         this.trigger = this.wrap.createChild(this.triggerConfig ||
38082                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
38083         if(this.hideTrigger){
38084             this.trigger.setDisplayed(false);
38085         }
38086         this.initTrigger();
38087         if(!this.width){
38088             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
38089         }
38090     },
38091
38092     // private
38093     initTrigger : function(){
38094         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
38095         this.trigger.addClassOnOver('x-form-trigger-over');
38096         this.trigger.addClassOnClick('x-form-trigger-click');
38097     },
38098
38099     // private
38100     onDestroy : function(){
38101         if(this.trigger){
38102             this.trigger.removeAllListeners();
38103             this.trigger.remove();
38104         }
38105         if(this.wrap){
38106             this.wrap.remove();
38107         }
38108         Roo.form.TriggerField.superclass.onDestroy.call(this);
38109     },
38110
38111     // private
38112     onFocus : function(){
38113         Roo.form.TriggerField.superclass.onFocus.call(this);
38114         if(!this.mimicing){
38115             this.wrap.addClass('x-trigger-wrap-focus');
38116             this.mimicing = true;
38117             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
38118             if(this.monitorTab){
38119                 this.el.on("keydown", this.checkTab, this);
38120             }
38121         }
38122     },
38123
38124     // private
38125     checkTab : function(e){
38126         if(e.getKey() == e.TAB){
38127             this.triggerBlur();
38128         }
38129     },
38130
38131     // private
38132     onBlur : function(){
38133         // do nothing
38134     },
38135
38136     // private
38137     mimicBlur : function(e, t){
38138         if(!this.wrap.contains(t) && this.validateBlur()){
38139             this.triggerBlur();
38140         }
38141     },
38142
38143     // private
38144     triggerBlur : function(){
38145         this.mimicing = false;
38146         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
38147         if(this.monitorTab){
38148             this.el.un("keydown", this.checkTab, this);
38149         }
38150         this.wrap.removeClass('x-trigger-wrap-focus');
38151         Roo.form.TriggerField.superclass.onBlur.call(this);
38152     },
38153
38154     // private
38155     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
38156     validateBlur : function(e, t){
38157         return true;
38158     },
38159
38160     // private
38161     onDisable : function(){
38162         Roo.form.TriggerField.superclass.onDisable.call(this);
38163         if(this.wrap){
38164             this.wrap.addClass('x-item-disabled');
38165         }
38166     },
38167
38168     // private
38169     onEnable : function(){
38170         Roo.form.TriggerField.superclass.onEnable.call(this);
38171         if(this.wrap){
38172             this.wrap.removeClass('x-item-disabled');
38173         }
38174     },
38175
38176     // private
38177     onShow : function(){
38178         var ae = this.getActionEl();
38179         
38180         if(ae){
38181             ae.dom.style.display = '';
38182             ae.dom.style.visibility = 'visible';
38183         }
38184     },
38185
38186     // private
38187     
38188     onHide : function(){
38189         var ae = this.getActionEl();
38190         ae.dom.style.display = 'none';
38191     },
38192
38193     /**
38194      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
38195      * by an implementing function.
38196      * @method
38197      * @param {EventObject} e
38198      */
38199     onTriggerClick : Roo.emptyFn
38200 });
38201
38202 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
38203 // to be extended by an implementing class.  For an example of implementing this class, see the custom
38204 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
38205 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
38206     initComponent : function(){
38207         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
38208
38209         this.triggerConfig = {
38210             tag:'span', cls:'x-form-twin-triggers', cn:[
38211             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
38212             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
38213         ]};
38214     },
38215
38216     getTrigger : function(index){
38217         return this.triggers[index];
38218     },
38219
38220     initTrigger : function(){
38221         var ts = this.trigger.select('.x-form-trigger', true);
38222         this.wrap.setStyle('overflow', 'hidden');
38223         var triggerField = this;
38224         ts.each(function(t, all, index){
38225             t.hide = function(){
38226                 var w = triggerField.wrap.getWidth();
38227                 this.dom.style.display = 'none';
38228                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38229             };
38230             t.show = function(){
38231                 var w = triggerField.wrap.getWidth();
38232                 this.dom.style.display = '';
38233                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38234             };
38235             var triggerIndex = 'Trigger'+(index+1);
38236
38237             if(this['hide'+triggerIndex]){
38238                 t.dom.style.display = 'none';
38239             }
38240             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38241             t.addClassOnOver('x-form-trigger-over');
38242             t.addClassOnClick('x-form-trigger-click');
38243         }, this);
38244         this.triggers = ts.elements;
38245     },
38246
38247     onTrigger1Click : Roo.emptyFn,
38248     onTrigger2Click : Roo.emptyFn
38249 });/*
38250  * Based on:
38251  * Ext JS Library 1.1.1
38252  * Copyright(c) 2006-2007, Ext JS, LLC.
38253  *
38254  * Originally Released Under LGPL - original licence link has changed is not relivant.
38255  *
38256  * Fork - LGPL
38257  * <script type="text/javascript">
38258  */
38259  
38260 /**
38261  * @class Roo.form.TextArea
38262  * @extends Roo.form.TextField
38263  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38264  * support for auto-sizing.
38265  * @constructor
38266  * Creates a new TextArea
38267  * @param {Object} config Configuration options
38268  */
38269 Roo.form.TextArea = function(config){
38270     Roo.form.TextArea.superclass.constructor.call(this, config);
38271     // these are provided exchanges for backwards compat
38272     // minHeight/maxHeight were replaced by growMin/growMax to be
38273     // compatible with TextField growing config values
38274     if(this.minHeight !== undefined){
38275         this.growMin = this.minHeight;
38276     }
38277     if(this.maxHeight !== undefined){
38278         this.growMax = this.maxHeight;
38279     }
38280 };
38281
38282 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38283     /**
38284      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38285      */
38286     growMin : 60,
38287     /**
38288      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38289      */
38290     growMax: 1000,
38291     /**
38292      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38293      * in the field (equivalent to setting overflow: hidden, defaults to false)
38294      */
38295     preventScrollbars: false,
38296     /**
38297      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38298      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38299      */
38300
38301     // private
38302     onRender : function(ct, position){
38303         if(!this.el){
38304             this.defaultAutoCreate = {
38305                 tag: "textarea",
38306                 style:"width:300px;height:60px;",
38307                 autocomplete: "new-password"
38308             };
38309         }
38310         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38311         if(this.grow){
38312             this.textSizeEl = Roo.DomHelper.append(document.body, {
38313                 tag: "pre", cls: "x-form-grow-sizer"
38314             });
38315             if(this.preventScrollbars){
38316                 this.el.setStyle("overflow", "hidden");
38317             }
38318             this.el.setHeight(this.growMin);
38319         }
38320     },
38321
38322     onDestroy : function(){
38323         if(this.textSizeEl){
38324             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38325         }
38326         Roo.form.TextArea.superclass.onDestroy.call(this);
38327     },
38328
38329     // private
38330     onKeyUp : function(e){
38331         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38332             this.autoSize();
38333         }
38334     },
38335
38336     /**
38337      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38338      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38339      */
38340     autoSize : function(){
38341         if(!this.grow || !this.textSizeEl){
38342             return;
38343         }
38344         var el = this.el;
38345         var v = el.dom.value;
38346         var ts = this.textSizeEl;
38347
38348         ts.innerHTML = '';
38349         ts.appendChild(document.createTextNode(v));
38350         v = ts.innerHTML;
38351
38352         Roo.fly(ts).setWidth(this.el.getWidth());
38353         if(v.length < 1){
38354             v = "&#160;&#160;";
38355         }else{
38356             if(Roo.isIE){
38357                 v = v.replace(/\n/g, '<p>&#160;</p>');
38358             }
38359             v += "&#160;\n&#160;";
38360         }
38361         ts.innerHTML = v;
38362         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38363         if(h != this.lastHeight){
38364             this.lastHeight = h;
38365             this.el.setHeight(h);
38366             this.fireEvent("autosize", this, h);
38367         }
38368     }
38369 });/*
38370  * Based on:
38371  * Ext JS Library 1.1.1
38372  * Copyright(c) 2006-2007, Ext JS, LLC.
38373  *
38374  * Originally Released Under LGPL - original licence link has changed is not relivant.
38375  *
38376  * Fork - LGPL
38377  * <script type="text/javascript">
38378  */
38379  
38380
38381 /**
38382  * @class Roo.form.NumberField
38383  * @extends Roo.form.TextField
38384  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38385  * @constructor
38386  * Creates a new NumberField
38387  * @param {Object} config Configuration options
38388  */
38389 Roo.form.NumberField = function(config){
38390     Roo.form.NumberField.superclass.constructor.call(this, config);
38391 };
38392
38393 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38394     /**
38395      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38396      */
38397     fieldClass: "x-form-field x-form-num-field",
38398     /**
38399      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38400      */
38401     allowDecimals : true,
38402     /**
38403      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38404      */
38405     decimalSeparator : ".",
38406     /**
38407      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38408      */
38409     decimalPrecision : 2,
38410     /**
38411      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38412      */
38413     allowNegative : true,
38414     /**
38415      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38416      */
38417     minValue : Number.NEGATIVE_INFINITY,
38418     /**
38419      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38420      */
38421     maxValue : Number.MAX_VALUE,
38422     /**
38423      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38424      */
38425     minText : "The minimum value for this field is {0}",
38426     /**
38427      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38428      */
38429     maxText : "The maximum value for this field is {0}",
38430     /**
38431      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38432      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38433      */
38434     nanText : "{0} is not a valid number",
38435
38436     // private
38437     initEvents : function(){
38438         Roo.form.NumberField.superclass.initEvents.call(this);
38439         var allowed = "0123456789";
38440         if(this.allowDecimals){
38441             allowed += this.decimalSeparator;
38442         }
38443         if(this.allowNegative){
38444             allowed += "-";
38445         }
38446         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38447         var keyPress = function(e){
38448             var k = e.getKey();
38449             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38450                 return;
38451             }
38452             var c = e.getCharCode();
38453             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38454                 e.stopEvent();
38455             }
38456         };
38457         this.el.on("keypress", keyPress, this);
38458     },
38459
38460     // private
38461     validateValue : function(value){
38462         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38463             return false;
38464         }
38465         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38466              return true;
38467         }
38468         var num = this.parseValue(value);
38469         if(isNaN(num)){
38470             this.markInvalid(String.format(this.nanText, value));
38471             return false;
38472         }
38473         if(num < this.minValue){
38474             this.markInvalid(String.format(this.minText, this.minValue));
38475             return false;
38476         }
38477         if(num > this.maxValue){
38478             this.markInvalid(String.format(this.maxText, this.maxValue));
38479             return false;
38480         }
38481         return true;
38482     },
38483
38484     getValue : function(){
38485         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38486     },
38487
38488     // private
38489     parseValue : function(value){
38490         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38491         return isNaN(value) ? '' : value;
38492     },
38493
38494     // private
38495     fixPrecision : function(value){
38496         var nan = isNaN(value);
38497         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38498             return nan ? '' : value;
38499         }
38500         return parseFloat(value).toFixed(this.decimalPrecision);
38501     },
38502
38503     setValue : function(v){
38504         v = this.fixPrecision(v);
38505         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38506     },
38507
38508     // private
38509     decimalPrecisionFcn : function(v){
38510         return Math.floor(v);
38511     },
38512
38513     beforeBlur : function(){
38514         var v = this.parseValue(this.getRawValue());
38515         if(v){
38516             this.setValue(v);
38517         }
38518     }
38519 });/*
38520  * Based on:
38521  * Ext JS Library 1.1.1
38522  * Copyright(c) 2006-2007, Ext JS, LLC.
38523  *
38524  * Originally Released Under LGPL - original licence link has changed is not relivant.
38525  *
38526  * Fork - LGPL
38527  * <script type="text/javascript">
38528  */
38529  
38530 /**
38531  * @class Roo.form.DateField
38532  * @extends Roo.form.TriggerField
38533  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38534 * @constructor
38535 * Create a new DateField
38536 * @param {Object} config
38537  */
38538 Roo.form.DateField = function(config){
38539     Roo.form.DateField.superclass.constructor.call(this, config);
38540     
38541       this.addEvents({
38542          
38543         /**
38544          * @event select
38545          * Fires when a date is selected
38546              * @param {Roo.form.DateField} combo This combo box
38547              * @param {Date} date The date selected
38548              */
38549         'select' : true
38550          
38551     });
38552     
38553     
38554     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38555     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38556     this.ddMatch = null;
38557     if(this.disabledDates){
38558         var dd = this.disabledDates;
38559         var re = "(?:";
38560         for(var i = 0; i < dd.length; i++){
38561             re += dd[i];
38562             if(i != dd.length-1) re += "|";
38563         }
38564         this.ddMatch = new RegExp(re + ")");
38565     }
38566 };
38567
38568 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38569     /**
38570      * @cfg {String} format
38571      * The default date format string which can be overriden for localization support.  The format must be
38572      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38573      */
38574     format : "m/d/y",
38575     /**
38576      * @cfg {String} altFormats
38577      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38578      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38579      */
38580     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38581     /**
38582      * @cfg {Array} disabledDays
38583      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38584      */
38585     disabledDays : null,
38586     /**
38587      * @cfg {String} disabledDaysText
38588      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38589      */
38590     disabledDaysText : "Disabled",
38591     /**
38592      * @cfg {Array} disabledDates
38593      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38594      * expression so they are very powerful. Some examples:
38595      * <ul>
38596      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38597      * <li>["03/08", "09/16"] would disable those days for every year</li>
38598      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38599      * <li>["03/../2006"] would disable every day in March 2006</li>
38600      * <li>["^03"] would disable every day in every March</li>
38601      * </ul>
38602      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38603      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38604      */
38605     disabledDates : null,
38606     /**
38607      * @cfg {String} disabledDatesText
38608      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38609      */
38610     disabledDatesText : "Disabled",
38611     /**
38612      * @cfg {Date/String} minValue
38613      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38614      * valid format (defaults to null).
38615      */
38616     minValue : null,
38617     /**
38618      * @cfg {Date/String} maxValue
38619      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38620      * valid format (defaults to null).
38621      */
38622     maxValue : null,
38623     /**
38624      * @cfg {String} minText
38625      * The error text to display when the date in the cell is before minValue (defaults to
38626      * 'The date in this field must be after {minValue}').
38627      */
38628     minText : "The date in this field must be equal to or after {0}",
38629     /**
38630      * @cfg {String} maxText
38631      * The error text to display when the date in the cell is after maxValue (defaults to
38632      * 'The date in this field must be before {maxValue}').
38633      */
38634     maxText : "The date in this field must be equal to or before {0}",
38635     /**
38636      * @cfg {String} invalidText
38637      * The error text to display when the date in the field is invalid (defaults to
38638      * '{value} is not a valid date - it must be in the format {format}').
38639      */
38640     invalidText : "{0} is not a valid date - it must be in the format {1}",
38641     /**
38642      * @cfg {String} triggerClass
38643      * An additional CSS class used to style the trigger button.  The trigger will always get the
38644      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38645      * which displays a calendar icon).
38646      */
38647     triggerClass : 'x-form-date-trigger',
38648     
38649
38650     /**
38651      * @cfg {Boolean} useIso
38652      * if enabled, then the date field will use a hidden field to store the 
38653      * real value as iso formated date. default (false)
38654      */ 
38655     useIso : false,
38656     /**
38657      * @cfg {String/Object} autoCreate
38658      * A DomHelper element spec, or true for a default element spec (defaults to
38659      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38660      */ 
38661     // private
38662     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38663     
38664     // private
38665     hiddenField: false,
38666     
38667     onRender : function(ct, position)
38668     {
38669         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38670         if (this.useIso) {
38671             //this.el.dom.removeAttribute('name'); 
38672             Roo.log("Changing name?");
38673             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38674             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38675                     'before', true);
38676             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38677             // prevent input submission
38678             this.hiddenName = this.name;
38679         }
38680             
38681             
38682     },
38683     
38684     // private
38685     validateValue : function(value)
38686     {
38687         value = this.formatDate(value);
38688         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38689             Roo.log('super failed');
38690             return false;
38691         }
38692         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38693              return true;
38694         }
38695         var svalue = value;
38696         value = this.parseDate(value);
38697         if(!value){
38698             Roo.log('parse date failed' + svalue);
38699             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38700             return false;
38701         }
38702         var time = value.getTime();
38703         if(this.minValue && time < this.minValue.getTime()){
38704             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38705             return false;
38706         }
38707         if(this.maxValue && time > this.maxValue.getTime()){
38708             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38709             return false;
38710         }
38711         if(this.disabledDays){
38712             var day = value.getDay();
38713             for(var i = 0; i < this.disabledDays.length; i++) {
38714                 if(day === this.disabledDays[i]){
38715                     this.markInvalid(this.disabledDaysText);
38716                     return false;
38717                 }
38718             }
38719         }
38720         var fvalue = this.formatDate(value);
38721         if(this.ddMatch && this.ddMatch.test(fvalue)){
38722             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38723             return false;
38724         }
38725         return true;
38726     },
38727
38728     // private
38729     // Provides logic to override the default TriggerField.validateBlur which just returns true
38730     validateBlur : function(){
38731         return !this.menu || !this.menu.isVisible();
38732     },
38733     
38734     getName: function()
38735     {
38736         // returns hidden if it's set..
38737         if (!this.rendered) {return ''};
38738         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38739         
38740     },
38741
38742     /**
38743      * Returns the current date value of the date field.
38744      * @return {Date} The date value
38745      */
38746     getValue : function(){
38747         
38748         return  this.hiddenField ?
38749                 this.hiddenField.value :
38750                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38751     },
38752
38753     /**
38754      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38755      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38756      * (the default format used is "m/d/y").
38757      * <br />Usage:
38758      * <pre><code>
38759 //All of these calls set the same date value (May 4, 2006)
38760
38761 //Pass a date object:
38762 var dt = new Date('5/4/06');
38763 dateField.setValue(dt);
38764
38765 //Pass a date string (default format):
38766 dateField.setValue('5/4/06');
38767
38768 //Pass a date string (custom format):
38769 dateField.format = 'Y-m-d';
38770 dateField.setValue('2006-5-4');
38771 </code></pre>
38772      * @param {String/Date} date The date or valid date string
38773      */
38774     setValue : function(date){
38775         if (this.hiddenField) {
38776             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38777         }
38778         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38779         // make sure the value field is always stored as a date..
38780         this.value = this.parseDate(date);
38781         
38782         
38783     },
38784
38785     // private
38786     parseDate : function(value){
38787         if(!value || value instanceof Date){
38788             return value;
38789         }
38790         var v = Date.parseDate(value, this.format);
38791          if (!v && this.useIso) {
38792             v = Date.parseDate(value, 'Y-m-d');
38793         }
38794         if(!v && this.altFormats){
38795             if(!this.altFormatsArray){
38796                 this.altFormatsArray = this.altFormats.split("|");
38797             }
38798             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38799                 v = Date.parseDate(value, this.altFormatsArray[i]);
38800             }
38801         }
38802         return v;
38803     },
38804
38805     // private
38806     formatDate : function(date, fmt){
38807         return (!date || !(date instanceof Date)) ?
38808                date : date.dateFormat(fmt || this.format);
38809     },
38810
38811     // private
38812     menuListeners : {
38813         select: function(m, d){
38814             
38815             this.setValue(d);
38816             this.fireEvent('select', this, d);
38817         },
38818         show : function(){ // retain focus styling
38819             this.onFocus();
38820         },
38821         hide : function(){
38822             this.focus.defer(10, this);
38823             var ml = this.menuListeners;
38824             this.menu.un("select", ml.select,  this);
38825             this.menu.un("show", ml.show,  this);
38826             this.menu.un("hide", ml.hide,  this);
38827         }
38828     },
38829
38830     // private
38831     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38832     onTriggerClick : function(){
38833         if(this.disabled){
38834             return;
38835         }
38836         if(this.menu == null){
38837             this.menu = new Roo.menu.DateMenu();
38838         }
38839         Roo.apply(this.menu.picker,  {
38840             showClear: this.allowBlank,
38841             minDate : this.minValue,
38842             maxDate : this.maxValue,
38843             disabledDatesRE : this.ddMatch,
38844             disabledDatesText : this.disabledDatesText,
38845             disabledDays : this.disabledDays,
38846             disabledDaysText : this.disabledDaysText,
38847             format : this.useIso ? 'Y-m-d' : this.format,
38848             minText : String.format(this.minText, this.formatDate(this.minValue)),
38849             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38850         });
38851         this.menu.on(Roo.apply({}, this.menuListeners, {
38852             scope:this
38853         }));
38854         this.menu.picker.setValue(this.getValue() || new Date());
38855         this.menu.show(this.el, "tl-bl?");
38856     },
38857
38858     beforeBlur : function(){
38859         var v = this.parseDate(this.getRawValue());
38860         if(v){
38861             this.setValue(v);
38862         }
38863     },
38864
38865     /*@
38866      * overide
38867      * 
38868      */
38869     isDirty : function() {
38870         if(this.disabled) {
38871             return false;
38872         }
38873         
38874         if(typeof(this.startValue) === 'undefined'){
38875             return false;
38876         }
38877         
38878         return String(this.getValue()) !== String(this.startValue);
38879         
38880     }
38881 });/*
38882  * Based on:
38883  * Ext JS Library 1.1.1
38884  * Copyright(c) 2006-2007, Ext JS, LLC.
38885  *
38886  * Originally Released Under LGPL - original licence link has changed is not relivant.
38887  *
38888  * Fork - LGPL
38889  * <script type="text/javascript">
38890  */
38891  
38892 /**
38893  * @class Roo.form.MonthField
38894  * @extends Roo.form.TriggerField
38895  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38896 * @constructor
38897 * Create a new MonthField
38898 * @param {Object} config
38899  */
38900 Roo.form.MonthField = function(config){
38901     
38902     Roo.form.MonthField.superclass.constructor.call(this, config);
38903     
38904       this.addEvents({
38905          
38906         /**
38907          * @event select
38908          * Fires when a date is selected
38909              * @param {Roo.form.MonthFieeld} combo This combo box
38910              * @param {Date} date The date selected
38911              */
38912         'select' : true
38913          
38914     });
38915     
38916     
38917     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38918     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38919     this.ddMatch = null;
38920     if(this.disabledDates){
38921         var dd = this.disabledDates;
38922         var re = "(?:";
38923         for(var i = 0; i < dd.length; i++){
38924             re += dd[i];
38925             if(i != dd.length-1) re += "|";
38926         }
38927         this.ddMatch = new RegExp(re + ")");
38928     }
38929 };
38930
38931 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38932     /**
38933      * @cfg {String} format
38934      * The default date format string which can be overriden for localization support.  The format must be
38935      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38936      */
38937     format : "M Y",
38938     /**
38939      * @cfg {String} altFormats
38940      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38941      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38942      */
38943     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38944     /**
38945      * @cfg {Array} disabledDays
38946      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38947      */
38948     disabledDays : [0,1,2,3,4,5,6],
38949     /**
38950      * @cfg {String} disabledDaysText
38951      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38952      */
38953     disabledDaysText : "Disabled",
38954     /**
38955      * @cfg {Array} disabledDates
38956      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38957      * expression so they are very powerful. Some examples:
38958      * <ul>
38959      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38960      * <li>["03/08", "09/16"] would disable those days for every year</li>
38961      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38962      * <li>["03/../2006"] would disable every day in March 2006</li>
38963      * <li>["^03"] would disable every day in every March</li>
38964      * </ul>
38965      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38966      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38967      */
38968     disabledDates : null,
38969     /**
38970      * @cfg {String} disabledDatesText
38971      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38972      */
38973     disabledDatesText : "Disabled",
38974     /**
38975      * @cfg {Date/String} minValue
38976      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38977      * valid format (defaults to null).
38978      */
38979     minValue : null,
38980     /**
38981      * @cfg {Date/String} maxValue
38982      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38983      * valid format (defaults to null).
38984      */
38985     maxValue : null,
38986     /**
38987      * @cfg {String} minText
38988      * The error text to display when the date in the cell is before minValue (defaults to
38989      * 'The date in this field must be after {minValue}').
38990      */
38991     minText : "The date in this field must be equal to or after {0}",
38992     /**
38993      * @cfg {String} maxTextf
38994      * The error text to display when the date in the cell is after maxValue (defaults to
38995      * 'The date in this field must be before {maxValue}').
38996      */
38997     maxText : "The date in this field must be equal to or before {0}",
38998     /**
38999      * @cfg {String} invalidText
39000      * The error text to display when the date in the field is invalid (defaults to
39001      * '{value} is not a valid date - it must be in the format {format}').
39002      */
39003     invalidText : "{0} is not a valid date - it must be in the format {1}",
39004     /**
39005      * @cfg {String} triggerClass
39006      * An additional CSS class used to style the trigger button.  The trigger will always get the
39007      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
39008      * which displays a calendar icon).
39009      */
39010     triggerClass : 'x-form-date-trigger',
39011     
39012
39013     /**
39014      * @cfg {Boolean} useIso
39015      * if enabled, then the date field will use a hidden field to store the 
39016      * real value as iso formated date. default (true)
39017      */ 
39018     useIso : true,
39019     /**
39020      * @cfg {String/Object} autoCreate
39021      * A DomHelper element spec, or true for a default element spec (defaults to
39022      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
39023      */ 
39024     // private
39025     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
39026     
39027     // private
39028     hiddenField: false,
39029     
39030     hideMonthPicker : false,
39031     
39032     onRender : function(ct, position)
39033     {
39034         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
39035         if (this.useIso) {
39036             this.el.dom.removeAttribute('name'); 
39037             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
39038                     'before', true);
39039             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
39040             // prevent input submission
39041             this.hiddenName = this.name;
39042         }
39043             
39044             
39045     },
39046     
39047     // private
39048     validateValue : function(value)
39049     {
39050         value = this.formatDate(value);
39051         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
39052             return false;
39053         }
39054         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
39055              return true;
39056         }
39057         var svalue = value;
39058         value = this.parseDate(value);
39059         if(!value){
39060             this.markInvalid(String.format(this.invalidText, svalue, this.format));
39061             return false;
39062         }
39063         var time = value.getTime();
39064         if(this.minValue && time < this.minValue.getTime()){
39065             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
39066             return false;
39067         }
39068         if(this.maxValue && time > this.maxValue.getTime()){
39069             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
39070             return false;
39071         }
39072         /*if(this.disabledDays){
39073             var day = value.getDay();
39074             for(var i = 0; i < this.disabledDays.length; i++) {
39075                 if(day === this.disabledDays[i]){
39076                     this.markInvalid(this.disabledDaysText);
39077                     return false;
39078                 }
39079             }
39080         }
39081         */
39082         var fvalue = this.formatDate(value);
39083         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
39084             this.markInvalid(String.format(this.disabledDatesText, fvalue));
39085             return false;
39086         }
39087         */
39088         return true;
39089     },
39090
39091     // private
39092     // Provides logic to override the default TriggerField.validateBlur which just returns true
39093     validateBlur : function(){
39094         return !this.menu || !this.menu.isVisible();
39095     },
39096
39097     /**
39098      * Returns the current date value of the date field.
39099      * @return {Date} The date value
39100      */
39101     getValue : function(){
39102         
39103         
39104         
39105         return  this.hiddenField ?
39106                 this.hiddenField.value :
39107                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
39108     },
39109
39110     /**
39111      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
39112      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
39113      * (the default format used is "m/d/y").
39114      * <br />Usage:
39115      * <pre><code>
39116 //All of these calls set the same date value (May 4, 2006)
39117
39118 //Pass a date object:
39119 var dt = new Date('5/4/06');
39120 monthField.setValue(dt);
39121
39122 //Pass a date string (default format):
39123 monthField.setValue('5/4/06');
39124
39125 //Pass a date string (custom format):
39126 monthField.format = 'Y-m-d';
39127 monthField.setValue('2006-5-4');
39128 </code></pre>
39129      * @param {String/Date} date The date or valid date string
39130      */
39131     setValue : function(date){
39132         Roo.log('month setValue' + date);
39133         // can only be first of month..
39134         
39135         var val = this.parseDate(date);
39136         
39137         if (this.hiddenField) {
39138             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
39139         }
39140         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
39141         this.value = this.parseDate(date);
39142     },
39143
39144     // private
39145     parseDate : function(value){
39146         if(!value || value instanceof Date){
39147             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
39148             return value;
39149         }
39150         var v = Date.parseDate(value, this.format);
39151         if (!v && this.useIso) {
39152             v = Date.parseDate(value, 'Y-m-d');
39153         }
39154         if (v) {
39155             // 
39156             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
39157         }
39158         
39159         
39160         if(!v && this.altFormats){
39161             if(!this.altFormatsArray){
39162                 this.altFormatsArray = this.altFormats.split("|");
39163             }
39164             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
39165                 v = Date.parseDate(value, this.altFormatsArray[i]);
39166             }
39167         }
39168         return v;
39169     },
39170
39171     // private
39172     formatDate : function(date, fmt){
39173         return (!date || !(date instanceof Date)) ?
39174                date : date.dateFormat(fmt || this.format);
39175     },
39176
39177     // private
39178     menuListeners : {
39179         select: function(m, d){
39180             this.setValue(d);
39181             this.fireEvent('select', this, d);
39182         },
39183         show : function(){ // retain focus styling
39184             this.onFocus();
39185         },
39186         hide : function(){
39187             this.focus.defer(10, this);
39188             var ml = this.menuListeners;
39189             this.menu.un("select", ml.select,  this);
39190             this.menu.un("show", ml.show,  this);
39191             this.menu.un("hide", ml.hide,  this);
39192         }
39193     },
39194     // private
39195     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
39196     onTriggerClick : function(){
39197         if(this.disabled){
39198             return;
39199         }
39200         if(this.menu == null){
39201             this.menu = new Roo.menu.DateMenu();
39202            
39203         }
39204         
39205         Roo.apply(this.menu.picker,  {
39206             
39207             showClear: this.allowBlank,
39208             minDate : this.minValue,
39209             maxDate : this.maxValue,
39210             disabledDatesRE : this.ddMatch,
39211             disabledDatesText : this.disabledDatesText,
39212             
39213             format : this.useIso ? 'Y-m-d' : this.format,
39214             minText : String.format(this.minText, this.formatDate(this.minValue)),
39215             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39216             
39217         });
39218          this.menu.on(Roo.apply({}, this.menuListeners, {
39219             scope:this
39220         }));
39221        
39222         
39223         var m = this.menu;
39224         var p = m.picker;
39225         
39226         // hide month picker get's called when we called by 'before hide';
39227         
39228         var ignorehide = true;
39229         p.hideMonthPicker  = function(disableAnim){
39230             if (ignorehide) {
39231                 return;
39232             }
39233              if(this.monthPicker){
39234                 Roo.log("hideMonthPicker called");
39235                 if(disableAnim === true){
39236                     this.monthPicker.hide();
39237                 }else{
39238                     this.monthPicker.slideOut('t', {duration:.2});
39239                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39240                     p.fireEvent("select", this, this.value);
39241                     m.hide();
39242                 }
39243             }
39244         }
39245         
39246         Roo.log('picker set value');
39247         Roo.log(this.getValue());
39248         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39249         m.show(this.el, 'tl-bl?');
39250         ignorehide  = false;
39251         // this will trigger hideMonthPicker..
39252         
39253         
39254         // hidden the day picker
39255         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39256         
39257         
39258         
39259       
39260         
39261         p.showMonthPicker.defer(100, p);
39262     
39263         
39264        
39265     },
39266
39267     beforeBlur : function(){
39268         var v = this.parseDate(this.getRawValue());
39269         if(v){
39270             this.setValue(v);
39271         }
39272     }
39273
39274     /** @cfg {Boolean} grow @hide */
39275     /** @cfg {Number} growMin @hide */
39276     /** @cfg {Number} growMax @hide */
39277     /**
39278      * @hide
39279      * @method autoSize
39280      */
39281 });/*
39282  * Based on:
39283  * Ext JS Library 1.1.1
39284  * Copyright(c) 2006-2007, Ext JS, LLC.
39285  *
39286  * Originally Released Under LGPL - original licence link has changed is not relivant.
39287  *
39288  * Fork - LGPL
39289  * <script type="text/javascript">
39290  */
39291  
39292
39293 /**
39294  * @class Roo.form.ComboBox
39295  * @extends Roo.form.TriggerField
39296  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39297  * @constructor
39298  * Create a new ComboBox.
39299  * @param {Object} config Configuration options
39300  */
39301 Roo.form.ComboBox = function(config){
39302     Roo.form.ComboBox.superclass.constructor.call(this, config);
39303     this.addEvents({
39304         /**
39305          * @event expand
39306          * Fires when the dropdown list is expanded
39307              * @param {Roo.form.ComboBox} combo This combo box
39308              */
39309         'expand' : true,
39310         /**
39311          * @event collapse
39312          * Fires when the dropdown list is collapsed
39313              * @param {Roo.form.ComboBox} combo This combo box
39314              */
39315         'collapse' : true,
39316         /**
39317          * @event beforeselect
39318          * Fires before a list item is selected. Return false to cancel the selection.
39319              * @param {Roo.form.ComboBox} combo This combo box
39320              * @param {Roo.data.Record} record The data record returned from the underlying store
39321              * @param {Number} index The index of the selected item in the dropdown list
39322              */
39323         'beforeselect' : true,
39324         /**
39325          * @event select
39326          * Fires when a list item is selected
39327              * @param {Roo.form.ComboBox} combo This combo box
39328              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39329              * @param {Number} index The index of the selected item in the dropdown list
39330              */
39331         'select' : true,
39332         /**
39333          * @event beforequery
39334          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39335          * The event object passed has these properties:
39336              * @param {Roo.form.ComboBox} combo This combo box
39337              * @param {String} query The query
39338              * @param {Boolean} forceAll true to force "all" query
39339              * @param {Boolean} cancel true to cancel the query
39340              * @param {Object} e The query event object
39341              */
39342         'beforequery': true,
39343          /**
39344          * @event add
39345          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39346              * @param {Roo.form.ComboBox} combo This combo box
39347              */
39348         'add' : true,
39349         /**
39350          * @event edit
39351          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39352              * @param {Roo.form.ComboBox} combo This combo box
39353              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39354              */
39355         'edit' : true
39356         
39357         
39358     });
39359     if(this.transform){
39360         this.allowDomMove = false;
39361         var s = Roo.getDom(this.transform);
39362         if(!this.hiddenName){
39363             this.hiddenName = s.name;
39364         }
39365         if(!this.store){
39366             this.mode = 'local';
39367             var d = [], opts = s.options;
39368             for(var i = 0, len = opts.length;i < len; i++){
39369                 var o = opts[i];
39370                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39371                 if(o.selected) {
39372                     this.value = value;
39373                 }
39374                 d.push([value, o.text]);
39375             }
39376             this.store = new Roo.data.SimpleStore({
39377                 'id': 0,
39378                 fields: ['value', 'text'],
39379                 data : d
39380             });
39381             this.valueField = 'value';
39382             this.displayField = 'text';
39383         }
39384         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39385         if(!this.lazyRender){
39386             this.target = true;
39387             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39388             s.parentNode.removeChild(s); // remove it
39389             this.render(this.el.parentNode);
39390         }else{
39391             s.parentNode.removeChild(s); // remove it
39392         }
39393
39394     }
39395     if (this.store) {
39396         this.store = Roo.factory(this.store, Roo.data);
39397     }
39398     
39399     this.selectedIndex = -1;
39400     if(this.mode == 'local'){
39401         if(config.queryDelay === undefined){
39402             this.queryDelay = 10;
39403         }
39404         if(config.minChars === undefined){
39405             this.minChars = 0;
39406         }
39407     }
39408 };
39409
39410 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39411     /**
39412      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39413      */
39414     /**
39415      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39416      * rendering into an Roo.Editor, defaults to false)
39417      */
39418     /**
39419      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39420      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39421      */
39422     /**
39423      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39424      */
39425     /**
39426      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39427      * the dropdown list (defaults to undefined, with no header element)
39428      */
39429
39430      /**
39431      * @cfg {String/Roo.Template} tpl The template to use to render the output
39432      */
39433      
39434     // private
39435     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39436     /**
39437      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39438      */
39439     listWidth: undefined,
39440     /**
39441      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39442      * mode = 'remote' or 'text' if mode = 'local')
39443      */
39444     displayField: undefined,
39445     /**
39446      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39447      * mode = 'remote' or 'value' if mode = 'local'). 
39448      * Note: use of a valueField requires the user make a selection
39449      * in order for a value to be mapped.
39450      */
39451     valueField: undefined,
39452     
39453     
39454     /**
39455      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39456      * field's data value (defaults to the underlying DOM element's name)
39457      */
39458     hiddenName: undefined,
39459     /**
39460      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39461      */
39462     listClass: '',
39463     /**
39464      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39465      */
39466     selectedClass: 'x-combo-selected',
39467     /**
39468      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39469      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39470      * which displays a downward arrow icon).
39471      */
39472     triggerClass : 'x-form-arrow-trigger',
39473     /**
39474      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39475      */
39476     shadow:'sides',
39477     /**
39478      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39479      * anchor positions (defaults to 'tl-bl')
39480      */
39481     listAlign: 'tl-bl?',
39482     /**
39483      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39484      */
39485     maxHeight: 300,
39486     /**
39487      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39488      * query specified by the allQuery config option (defaults to 'query')
39489      */
39490     triggerAction: 'query',
39491     /**
39492      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39493      * (defaults to 4, does not apply if editable = false)
39494      */
39495     minChars : 4,
39496     /**
39497      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39498      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39499      */
39500     typeAhead: false,
39501     /**
39502      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39503      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39504      */
39505     queryDelay: 500,
39506     /**
39507      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39508      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39509      */
39510     pageSize: 0,
39511     /**
39512      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39513      * when editable = true (defaults to false)
39514      */
39515     selectOnFocus:false,
39516     /**
39517      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39518      */
39519     queryParam: 'query',
39520     /**
39521      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39522      * when mode = 'remote' (defaults to 'Loading...')
39523      */
39524     loadingText: 'Loading...',
39525     /**
39526      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39527      */
39528     resizable: false,
39529     /**
39530      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39531      */
39532     handleHeight : 8,
39533     /**
39534      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39535      * traditional select (defaults to true)
39536      */
39537     editable: true,
39538     /**
39539      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39540      */
39541     allQuery: '',
39542     /**
39543      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39544      */
39545     mode: 'remote',
39546     /**
39547      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39548      * listWidth has a higher value)
39549      */
39550     minListWidth : 70,
39551     /**
39552      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39553      * allow the user to set arbitrary text into the field (defaults to false)
39554      */
39555     forceSelection:false,
39556     /**
39557      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39558      * if typeAhead = true (defaults to 250)
39559      */
39560     typeAheadDelay : 250,
39561     /**
39562      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39563      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39564      */
39565     valueNotFoundText : undefined,
39566     /**
39567      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39568      */
39569     blockFocus : false,
39570     
39571     /**
39572      * @cfg {Boolean} disableClear Disable showing of clear button.
39573      */
39574     disableClear : false,
39575     /**
39576      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39577      */
39578     alwaysQuery : false,
39579     
39580     //private
39581     addicon : false,
39582     editicon: false,
39583     
39584     // element that contains real text value.. (when hidden is used..)
39585      
39586     // private
39587     onRender : function(ct, position){
39588         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39589         if(this.hiddenName){
39590             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39591                     'before', true);
39592             this.hiddenField.value =
39593                 this.hiddenValue !== undefined ? this.hiddenValue :
39594                 this.value !== undefined ? this.value : '';
39595
39596             // prevent input submission
39597             this.el.dom.removeAttribute('name');
39598              
39599              
39600         }
39601         if(Roo.isGecko){
39602             this.el.dom.setAttribute('autocomplete', 'off');
39603         }
39604
39605         var cls = 'x-combo-list';
39606
39607         this.list = new Roo.Layer({
39608             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39609         });
39610
39611         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39612         this.list.setWidth(lw);
39613         this.list.swallowEvent('mousewheel');
39614         this.assetHeight = 0;
39615
39616         if(this.title){
39617             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39618             this.assetHeight += this.header.getHeight();
39619         }
39620
39621         this.innerList = this.list.createChild({cls:cls+'-inner'});
39622         this.innerList.on('mouseover', this.onViewOver, this);
39623         this.innerList.on('mousemove', this.onViewMove, this);
39624         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39625         
39626         if(this.allowBlank && !this.pageSize && !this.disableClear){
39627             this.footer = this.list.createChild({cls:cls+'-ft'});
39628             this.pageTb = new Roo.Toolbar(this.footer);
39629            
39630         }
39631         if(this.pageSize){
39632             this.footer = this.list.createChild({cls:cls+'-ft'});
39633             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39634                     {pageSize: this.pageSize});
39635             
39636         }
39637         
39638         if (this.pageTb && this.allowBlank && !this.disableClear) {
39639             var _this = this;
39640             this.pageTb.add(new Roo.Toolbar.Fill(), {
39641                 cls: 'x-btn-icon x-btn-clear',
39642                 text: '&#160;',
39643                 handler: function()
39644                 {
39645                     _this.collapse();
39646                     _this.clearValue();
39647                     _this.onSelect(false, -1);
39648                 }
39649             });
39650         }
39651         if (this.footer) {
39652             this.assetHeight += this.footer.getHeight();
39653         }
39654         
39655
39656         if(!this.tpl){
39657             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39658         }
39659
39660         this.view = new Roo.View(this.innerList, this.tpl, {
39661             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39662         });
39663
39664         this.view.on('click', this.onViewClick, this);
39665
39666         this.store.on('beforeload', this.onBeforeLoad, this);
39667         this.store.on('load', this.onLoad, this);
39668         this.store.on('loadexception', this.onLoadException, this);
39669
39670         if(this.resizable){
39671             this.resizer = new Roo.Resizable(this.list,  {
39672                pinned:true, handles:'se'
39673             });
39674             this.resizer.on('resize', function(r, w, h){
39675                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39676                 this.listWidth = w;
39677                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39678                 this.restrictHeight();
39679             }, this);
39680             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39681         }
39682         if(!this.editable){
39683             this.editable = true;
39684             this.setEditable(false);
39685         }  
39686         
39687         
39688         if (typeof(this.events.add.listeners) != 'undefined') {
39689             
39690             this.addicon = this.wrap.createChild(
39691                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39692        
39693             this.addicon.on('click', function(e) {
39694                 this.fireEvent('add', this);
39695             }, this);
39696         }
39697         if (typeof(this.events.edit.listeners) != 'undefined') {
39698             
39699             this.editicon = this.wrap.createChild(
39700                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39701             if (this.addicon) {
39702                 this.editicon.setStyle('margin-left', '40px');
39703             }
39704             this.editicon.on('click', function(e) {
39705                 
39706                 // we fire even  if inothing is selected..
39707                 this.fireEvent('edit', this, this.lastData );
39708                 
39709             }, this);
39710         }
39711         
39712         
39713         
39714     },
39715
39716     // private
39717     initEvents : function(){
39718         Roo.form.ComboBox.superclass.initEvents.call(this);
39719
39720         this.keyNav = new Roo.KeyNav(this.el, {
39721             "up" : function(e){
39722                 this.inKeyMode = true;
39723                 this.selectPrev();
39724             },
39725
39726             "down" : function(e){
39727                 if(!this.isExpanded()){
39728                     this.onTriggerClick();
39729                 }else{
39730                     this.inKeyMode = true;
39731                     this.selectNext();
39732                 }
39733             },
39734
39735             "enter" : function(e){
39736                 this.onViewClick();
39737                 //return true;
39738             },
39739
39740             "esc" : function(e){
39741                 this.collapse();
39742             },
39743
39744             "tab" : function(e){
39745                 this.onViewClick(false);
39746                 this.fireEvent("specialkey", this, e);
39747                 return true;
39748             },
39749
39750             scope : this,
39751
39752             doRelay : function(foo, bar, hname){
39753                 if(hname == 'down' || this.scope.isExpanded()){
39754                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39755                 }
39756                 return true;
39757             },
39758
39759             forceKeyDown: true
39760         });
39761         this.queryDelay = Math.max(this.queryDelay || 10,
39762                 this.mode == 'local' ? 10 : 250);
39763         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39764         if(this.typeAhead){
39765             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39766         }
39767         if(this.editable !== false){
39768             this.el.on("keyup", this.onKeyUp, this);
39769         }
39770         if(this.forceSelection){
39771             this.on('blur', this.doForce, this);
39772         }
39773     },
39774
39775     onDestroy : function(){
39776         if(this.view){
39777             this.view.setStore(null);
39778             this.view.el.removeAllListeners();
39779             this.view.el.remove();
39780             this.view.purgeListeners();
39781         }
39782         if(this.list){
39783             this.list.destroy();
39784         }
39785         if(this.store){
39786             this.store.un('beforeload', this.onBeforeLoad, this);
39787             this.store.un('load', this.onLoad, this);
39788             this.store.un('loadexception', this.onLoadException, this);
39789         }
39790         Roo.form.ComboBox.superclass.onDestroy.call(this);
39791     },
39792
39793     // private
39794     fireKey : function(e){
39795         if(e.isNavKeyPress() && !this.list.isVisible()){
39796             this.fireEvent("specialkey", this, e);
39797         }
39798     },
39799
39800     // private
39801     onResize: function(w, h){
39802         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39803         
39804         if(typeof w != 'number'){
39805             // we do not handle it!?!?
39806             return;
39807         }
39808         var tw = this.trigger.getWidth();
39809         tw += this.addicon ? this.addicon.getWidth() : 0;
39810         tw += this.editicon ? this.editicon.getWidth() : 0;
39811         var x = w - tw;
39812         this.el.setWidth( this.adjustWidth('input', x));
39813             
39814         this.trigger.setStyle('left', x+'px');
39815         
39816         if(this.list && this.listWidth === undefined){
39817             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39818             this.list.setWidth(lw);
39819             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39820         }
39821         
39822     
39823         
39824     },
39825
39826     /**
39827      * Allow or prevent the user from directly editing the field text.  If false is passed,
39828      * the user will only be able to select from the items defined in the dropdown list.  This method
39829      * is the runtime equivalent of setting the 'editable' config option at config time.
39830      * @param {Boolean} value True to allow the user to directly edit the field text
39831      */
39832     setEditable : function(value){
39833         if(value == this.editable){
39834             return;
39835         }
39836         this.editable = value;
39837         if(!value){
39838             this.el.dom.setAttribute('readOnly', true);
39839             this.el.on('mousedown', this.onTriggerClick,  this);
39840             this.el.addClass('x-combo-noedit');
39841         }else{
39842             this.el.dom.setAttribute('readOnly', false);
39843             this.el.un('mousedown', this.onTriggerClick,  this);
39844             this.el.removeClass('x-combo-noedit');
39845         }
39846     },
39847
39848     // private
39849     onBeforeLoad : function(){
39850         if(!this.hasFocus){
39851             return;
39852         }
39853         this.innerList.update(this.loadingText ?
39854                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39855         this.restrictHeight();
39856         this.selectedIndex = -1;
39857     },
39858
39859     // private
39860     onLoad : function(){
39861         if(!this.hasFocus){
39862             return;
39863         }
39864         if(this.store.getCount() > 0){
39865             this.expand();
39866             this.restrictHeight();
39867             if(this.lastQuery == this.allQuery){
39868                 if(this.editable){
39869                     this.el.dom.select();
39870                 }
39871                 if(!this.selectByValue(this.value, true)){
39872                     this.select(0, true);
39873                 }
39874             }else{
39875                 this.selectNext();
39876                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39877                     this.taTask.delay(this.typeAheadDelay);
39878                 }
39879             }
39880         }else{
39881             this.onEmptyResults();
39882         }
39883         //this.el.focus();
39884     },
39885     // private
39886     onLoadException : function()
39887     {
39888         this.collapse();
39889         Roo.log(this.store.reader.jsonData);
39890         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39891             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39892         }
39893         
39894         
39895     },
39896     // private
39897     onTypeAhead : function(){
39898         if(this.store.getCount() > 0){
39899             var r = this.store.getAt(0);
39900             var newValue = r.data[this.displayField];
39901             var len = newValue.length;
39902             var selStart = this.getRawValue().length;
39903             if(selStart != len){
39904                 this.setRawValue(newValue);
39905                 this.selectText(selStart, newValue.length);
39906             }
39907         }
39908     },
39909
39910     // private
39911     onSelect : function(record, index){
39912         if(this.fireEvent('beforeselect', this, record, index) !== false){
39913             this.setFromData(index > -1 ? record.data : false);
39914             this.collapse();
39915             this.fireEvent('select', this, record, index);
39916         }
39917     },
39918
39919     /**
39920      * Returns the currently selected field value or empty string if no value is set.
39921      * @return {String} value The selected value
39922      */
39923     getValue : function(){
39924         if(this.valueField){
39925             return typeof this.value != 'undefined' ? this.value : '';
39926         }
39927         return Roo.form.ComboBox.superclass.getValue.call(this);
39928     },
39929
39930     /**
39931      * Clears any text/value currently set in the field
39932      */
39933     clearValue : function(){
39934         if(this.hiddenField){
39935             this.hiddenField.value = '';
39936         }
39937         this.value = '';
39938         this.setRawValue('');
39939         this.lastSelectionText = '';
39940         
39941     },
39942
39943     /**
39944      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39945      * will be displayed in the field.  If the value does not match the data value of an existing item,
39946      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39947      * Otherwise the field will be blank (although the value will still be set).
39948      * @param {String} value The value to match
39949      */
39950     setValue : function(v){
39951         var text = v;
39952         if(this.valueField){
39953             var r = this.findRecord(this.valueField, v);
39954             if(r){
39955                 text = r.data[this.displayField];
39956             }else if(this.valueNotFoundText !== undefined){
39957                 text = this.valueNotFoundText;
39958             }
39959         }
39960         this.lastSelectionText = text;
39961         if(this.hiddenField){
39962             this.hiddenField.value = v;
39963         }
39964         Roo.form.ComboBox.superclass.setValue.call(this, text);
39965         this.value = v;
39966     },
39967     /**
39968      * @property {Object} the last set data for the element
39969      */
39970     
39971     lastData : false,
39972     /**
39973      * Sets the value of the field based on a object which is related to the record format for the store.
39974      * @param {Object} value the value to set as. or false on reset?
39975      */
39976     setFromData : function(o){
39977         var dv = ''; // display value
39978         var vv = ''; // value value..
39979         this.lastData = o;
39980         if (this.displayField) {
39981             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39982         } else {
39983             // this is an error condition!!!
39984             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39985         }
39986         
39987         if(this.valueField){
39988             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39989         }
39990         if(this.hiddenField){
39991             this.hiddenField.value = vv;
39992             
39993             this.lastSelectionText = dv;
39994             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39995             this.value = vv;
39996             return;
39997         }
39998         // no hidden field.. - we store the value in 'value', but still display
39999         // display field!!!!
40000         this.lastSelectionText = dv;
40001         Roo.form.ComboBox.superclass.setValue.call(this, dv);
40002         this.value = vv;
40003         
40004         
40005     },
40006     // private
40007     reset : function(){
40008         // overridden so that last data is reset..
40009         this.setValue(this.resetValue);
40010         this.clearInvalid();
40011         this.lastData = false;
40012         if (this.view) {
40013             this.view.clearSelections();
40014         }
40015     },
40016     // private
40017     findRecord : function(prop, value){
40018         var record;
40019         if(this.store.getCount() > 0){
40020             this.store.each(function(r){
40021                 if(r.data[prop] == value){
40022                     record = r;
40023                     return false;
40024                 }
40025                 return true;
40026             });
40027         }
40028         return record;
40029     },
40030     
40031     getName: function()
40032     {
40033         // returns hidden if it's set..
40034         if (!this.rendered) {return ''};
40035         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40036         
40037     },
40038     // private
40039     onViewMove : function(e, t){
40040         this.inKeyMode = false;
40041     },
40042
40043     // private
40044     onViewOver : function(e, t){
40045         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
40046             return;
40047         }
40048         var item = this.view.findItemFromChild(t);
40049         if(item){
40050             var index = this.view.indexOf(item);
40051             this.select(index, false);
40052         }
40053     },
40054
40055     // private
40056     onViewClick : function(doFocus)
40057     {
40058         var index = this.view.getSelectedIndexes()[0];
40059         var r = this.store.getAt(index);
40060         if(r){
40061             this.onSelect(r, index);
40062         }
40063         if(doFocus !== false && !this.blockFocus){
40064             this.el.focus();
40065         }
40066     },
40067
40068     // private
40069     restrictHeight : function(){
40070         this.innerList.dom.style.height = '';
40071         var inner = this.innerList.dom;
40072         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
40073         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
40074         this.list.beginUpdate();
40075         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
40076         this.list.alignTo(this.el, this.listAlign);
40077         this.list.endUpdate();
40078     },
40079
40080     // private
40081     onEmptyResults : function(){
40082         this.collapse();
40083     },
40084
40085     /**
40086      * Returns true if the dropdown list is expanded, else false.
40087      */
40088     isExpanded : function(){
40089         return this.list.isVisible();
40090     },
40091
40092     /**
40093      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
40094      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40095      * @param {String} value The data value of the item to select
40096      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40097      * selected item if it is not currently in view (defaults to true)
40098      * @return {Boolean} True if the value matched an item in the list, else false
40099      */
40100     selectByValue : function(v, scrollIntoView){
40101         if(v !== undefined && v !== null){
40102             var r = this.findRecord(this.valueField || this.displayField, v);
40103             if(r){
40104                 this.select(this.store.indexOf(r), scrollIntoView);
40105                 return true;
40106             }
40107         }
40108         return false;
40109     },
40110
40111     /**
40112      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
40113      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40114      * @param {Number} index The zero-based index of the list item to select
40115      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40116      * selected item if it is not currently in view (defaults to true)
40117      */
40118     select : function(index, scrollIntoView){
40119         this.selectedIndex = index;
40120         this.view.select(index);
40121         if(scrollIntoView !== false){
40122             var el = this.view.getNode(index);
40123             if(el){
40124                 this.innerList.scrollChildIntoView(el, false);
40125             }
40126         }
40127     },
40128
40129     // private
40130     selectNext : function(){
40131         var ct = this.store.getCount();
40132         if(ct > 0){
40133             if(this.selectedIndex == -1){
40134                 this.select(0);
40135             }else if(this.selectedIndex < ct-1){
40136                 this.select(this.selectedIndex+1);
40137             }
40138         }
40139     },
40140
40141     // private
40142     selectPrev : function(){
40143         var ct = this.store.getCount();
40144         if(ct > 0){
40145             if(this.selectedIndex == -1){
40146                 this.select(0);
40147             }else if(this.selectedIndex != 0){
40148                 this.select(this.selectedIndex-1);
40149             }
40150         }
40151     },
40152
40153     // private
40154     onKeyUp : function(e){
40155         if(this.editable !== false && !e.isSpecialKey()){
40156             this.lastKey = e.getKey();
40157             this.dqTask.delay(this.queryDelay);
40158         }
40159     },
40160
40161     // private
40162     validateBlur : function(){
40163         return !this.list || !this.list.isVisible();   
40164     },
40165
40166     // private
40167     initQuery : function(){
40168         this.doQuery(this.getRawValue());
40169     },
40170
40171     // private
40172     doForce : function(){
40173         if(this.el.dom.value.length > 0){
40174             this.el.dom.value =
40175                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
40176              
40177         }
40178     },
40179
40180     /**
40181      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
40182      * query allowing the query action to be canceled if needed.
40183      * @param {String} query The SQL query to execute
40184      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
40185      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
40186      * saved in the current store (defaults to false)
40187      */
40188     doQuery : function(q, forceAll){
40189         if(q === undefined || q === null){
40190             q = '';
40191         }
40192         var qe = {
40193             query: q,
40194             forceAll: forceAll,
40195             combo: this,
40196             cancel:false
40197         };
40198         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
40199             return false;
40200         }
40201         q = qe.query;
40202         forceAll = qe.forceAll;
40203         if(forceAll === true || (q.length >= this.minChars)){
40204             if(this.lastQuery != q || this.alwaysQuery){
40205                 this.lastQuery = q;
40206                 if(this.mode == 'local'){
40207                     this.selectedIndex = -1;
40208                     if(forceAll){
40209                         this.store.clearFilter();
40210                     }else{
40211                         this.store.filter(this.displayField, q);
40212                     }
40213                     this.onLoad();
40214                 }else{
40215                     this.store.baseParams[this.queryParam] = q;
40216                     this.store.load({
40217                         params: this.getParams(q)
40218                     });
40219                     this.expand();
40220                 }
40221             }else{
40222                 this.selectedIndex = -1;
40223                 this.onLoad();   
40224             }
40225         }
40226     },
40227
40228     // private
40229     getParams : function(q){
40230         var p = {};
40231         //p[this.queryParam] = q;
40232         if(this.pageSize){
40233             p.start = 0;
40234             p.limit = this.pageSize;
40235         }
40236         return p;
40237     },
40238
40239     /**
40240      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40241      */
40242     collapse : function(){
40243         if(!this.isExpanded()){
40244             return;
40245         }
40246         this.list.hide();
40247         Roo.get(document).un('mousedown', this.collapseIf, this);
40248         Roo.get(document).un('mousewheel', this.collapseIf, this);
40249         if (!this.editable) {
40250             Roo.get(document).un('keydown', this.listKeyPress, this);
40251         }
40252         this.fireEvent('collapse', this);
40253     },
40254
40255     // private
40256     collapseIf : function(e){
40257         if(!e.within(this.wrap) && !e.within(this.list)){
40258             this.collapse();
40259         }
40260     },
40261
40262     /**
40263      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40264      */
40265     expand : function(){
40266         if(this.isExpanded() || !this.hasFocus){
40267             return;
40268         }
40269         this.list.alignTo(this.el, this.listAlign);
40270         this.list.show();
40271         Roo.get(document).on('mousedown', this.collapseIf, this);
40272         Roo.get(document).on('mousewheel', this.collapseIf, this);
40273         if (!this.editable) {
40274             Roo.get(document).on('keydown', this.listKeyPress, this);
40275         }
40276         
40277         this.fireEvent('expand', this);
40278     },
40279
40280     // private
40281     // Implements the default empty TriggerField.onTriggerClick function
40282     onTriggerClick : function(){
40283         if(this.disabled){
40284             return;
40285         }
40286         if(this.isExpanded()){
40287             this.collapse();
40288             if (!this.blockFocus) {
40289                 this.el.focus();
40290             }
40291             
40292         }else {
40293             this.hasFocus = true;
40294             if(this.triggerAction == 'all') {
40295                 this.doQuery(this.allQuery, true);
40296             } else {
40297                 this.doQuery(this.getRawValue());
40298             }
40299             if (!this.blockFocus) {
40300                 this.el.focus();
40301             }
40302         }
40303     },
40304     listKeyPress : function(e)
40305     {
40306         //Roo.log('listkeypress');
40307         // scroll to first matching element based on key pres..
40308         if (e.isSpecialKey()) {
40309             return false;
40310         }
40311         var k = String.fromCharCode(e.getKey()).toUpperCase();
40312         //Roo.log(k);
40313         var match  = false;
40314         var csel = this.view.getSelectedNodes();
40315         var cselitem = false;
40316         if (csel.length) {
40317             var ix = this.view.indexOf(csel[0]);
40318             cselitem  = this.store.getAt(ix);
40319             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40320                 cselitem = false;
40321             }
40322             
40323         }
40324         
40325         this.store.each(function(v) { 
40326             if (cselitem) {
40327                 // start at existing selection.
40328                 if (cselitem.id == v.id) {
40329                     cselitem = false;
40330                 }
40331                 return;
40332             }
40333                 
40334             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40335                 match = this.store.indexOf(v);
40336                 return false;
40337             }
40338         }, this);
40339         
40340         if (match === false) {
40341             return true; // no more action?
40342         }
40343         // scroll to?
40344         this.view.select(match);
40345         var sn = Roo.get(this.view.getSelectedNodes()[0])
40346         sn.scrollIntoView(sn.dom.parentNode, false);
40347     }
40348
40349     /** 
40350     * @cfg {Boolean} grow 
40351     * @hide 
40352     */
40353     /** 
40354     * @cfg {Number} growMin 
40355     * @hide 
40356     */
40357     /** 
40358     * @cfg {Number} growMax 
40359     * @hide 
40360     */
40361     /**
40362      * @hide
40363      * @method autoSize
40364      */
40365 });/*
40366  * Copyright(c) 2010-2012, Roo J Solutions Limited
40367  *
40368  * Licence LGPL
40369  *
40370  */
40371
40372 /**
40373  * @class Roo.form.ComboBoxArray
40374  * @extends Roo.form.TextField
40375  * A facebook style adder... for lists of email / people / countries  etc...
40376  * pick multiple items from a combo box, and shows each one.
40377  *
40378  *  Fred [x]  Brian [x]  [Pick another |v]
40379  *
40380  *
40381  *  For this to work: it needs various extra information
40382  *    - normal combo problay has
40383  *      name, hiddenName
40384  *    + displayField, valueField
40385  *
40386  *    For our purpose...
40387  *
40388  *
40389  *   If we change from 'extends' to wrapping...
40390  *   
40391  *  
40392  *
40393  
40394  
40395  * @constructor
40396  * Create a new ComboBoxArray.
40397  * @param {Object} config Configuration options
40398  */
40399  
40400
40401 Roo.form.ComboBoxArray = function(config)
40402 {
40403     this.addEvents({
40404         /**
40405          * @event beforeremove
40406          * Fires before remove the value from the list
40407              * @param {Roo.form.ComboBoxArray} _self This combo box array
40408              * @param {Roo.form.ComboBoxArray.Item} item removed item
40409              */
40410         'beforeremove' : true,
40411         /**
40412          * @event remove
40413          * Fires when remove the value from the list
40414              * @param {Roo.form.ComboBoxArray} _self This combo box array
40415              * @param {Roo.form.ComboBoxArray.Item} item removed item
40416              */
40417         'remove' : true
40418         
40419         
40420     });
40421     
40422     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40423     
40424     this.items = new Roo.util.MixedCollection(false);
40425     
40426     // construct the child combo...
40427     
40428     
40429     
40430     
40431    
40432     
40433 }
40434
40435  
40436 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40437
40438     /**
40439      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40440      */
40441     
40442     lastData : false,
40443     
40444     // behavies liek a hiddne field
40445     inputType:      'hidden',
40446     /**
40447      * @cfg {Number} width The width of the box that displays the selected element
40448      */ 
40449     width:          300,
40450
40451     
40452     
40453     /**
40454      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40455      */
40456     name : false,
40457     /**
40458      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40459      */
40460     hiddenName : false,
40461     
40462     
40463     // private the array of items that are displayed..
40464     items  : false,
40465     // private - the hidden field el.
40466     hiddenEl : false,
40467     // private - the filed el..
40468     el : false,
40469     
40470     //validateValue : function() { return true; }, // all values are ok!
40471     //onAddClick: function() { },
40472     
40473     onRender : function(ct, position) 
40474     {
40475         
40476         // create the standard hidden element
40477         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40478         
40479         
40480         // give fake names to child combo;
40481         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40482         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40483         
40484         this.combo = Roo.factory(this.combo, Roo.form);
40485         this.combo.onRender(ct, position);
40486         if (typeof(this.combo.width) != 'undefined') {
40487             this.combo.onResize(this.combo.width,0);
40488         }
40489         
40490         this.combo.initEvents();
40491         
40492         // assigned so form know we need to do this..
40493         this.store          = this.combo.store;
40494         this.valueField     = this.combo.valueField;
40495         this.displayField   = this.combo.displayField ;
40496         
40497         
40498         this.combo.wrap.addClass('x-cbarray-grp');
40499         
40500         var cbwrap = this.combo.wrap.createChild(
40501             {tag: 'div', cls: 'x-cbarray-cb'},
40502             this.combo.el.dom
40503         );
40504         
40505              
40506         this.hiddenEl = this.combo.wrap.createChild({
40507             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40508         });
40509         this.el = this.combo.wrap.createChild({
40510             tag: 'input',  type:'hidden' , name: this.name, value : ''
40511         });
40512          //   this.el.dom.removeAttribute("name");
40513         
40514         
40515         this.outerWrap = this.combo.wrap;
40516         this.wrap = cbwrap;
40517         
40518         this.outerWrap.setWidth(this.width);
40519         this.outerWrap.dom.removeChild(this.el.dom);
40520         
40521         this.wrap.dom.appendChild(this.el.dom);
40522         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40523         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40524         
40525         this.combo.trigger.setStyle('position','relative');
40526         this.combo.trigger.setStyle('left', '0px');
40527         this.combo.trigger.setStyle('top', '2px');
40528         
40529         this.combo.el.setStyle('vertical-align', 'text-bottom');
40530         
40531         //this.trigger.setStyle('vertical-align', 'top');
40532         
40533         // this should use the code from combo really... on('add' ....)
40534         if (this.adder) {
40535             
40536         
40537             this.adder = this.outerWrap.createChild(
40538                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40539             var _t = this;
40540             this.adder.on('click', function(e) {
40541                 _t.fireEvent('adderclick', this, e);
40542             }, _t);
40543         }
40544         //var _t = this;
40545         //this.adder.on('click', this.onAddClick, _t);
40546         
40547         
40548         this.combo.on('select', function(cb, rec, ix) {
40549             this.addItem(rec.data);
40550             
40551             cb.setValue('');
40552             cb.el.dom.value = '';
40553             //cb.lastData = rec.data;
40554             // add to list
40555             
40556         }, this);
40557         
40558         
40559     },
40560     
40561     
40562     getName: function()
40563     {
40564         // returns hidden if it's set..
40565         if (!this.rendered) {return ''};
40566         return  this.hiddenName ? this.hiddenName : this.name;
40567         
40568     },
40569     
40570     
40571     onResize: function(w, h){
40572         
40573         return;
40574         // not sure if this is needed..
40575         //this.combo.onResize(w,h);
40576         
40577         if(typeof w != 'number'){
40578             // we do not handle it!?!?
40579             return;
40580         }
40581         var tw = this.combo.trigger.getWidth();
40582         tw += this.addicon ? this.addicon.getWidth() : 0;
40583         tw += this.editicon ? this.editicon.getWidth() : 0;
40584         var x = w - tw;
40585         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40586             
40587         this.combo.trigger.setStyle('left', '0px');
40588         
40589         if(this.list && this.listWidth === undefined){
40590             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40591             this.list.setWidth(lw);
40592             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40593         }
40594         
40595     
40596         
40597     },
40598     
40599     addItem: function(rec)
40600     {
40601         var valueField = this.combo.valueField;
40602         var displayField = this.combo.displayField;
40603         if (this.items.indexOfKey(rec[valueField]) > -1) {
40604             //console.log("GOT " + rec.data.id);
40605             return;
40606         }
40607         
40608         var x = new Roo.form.ComboBoxArray.Item({
40609             //id : rec[this.idField],
40610             data : rec,
40611             displayField : displayField ,
40612             tipField : displayField ,
40613             cb : this
40614         });
40615         // use the 
40616         this.items.add(rec[valueField],x);
40617         // add it before the element..
40618         this.updateHiddenEl();
40619         x.render(this.outerWrap, this.wrap.dom);
40620         // add the image handler..
40621     },
40622     
40623     updateHiddenEl : function()
40624     {
40625         this.validate();
40626         if (!this.hiddenEl) {
40627             return;
40628         }
40629         var ar = [];
40630         var idField = this.combo.valueField;
40631         
40632         this.items.each(function(f) {
40633             ar.push(f.data[idField]);
40634            
40635         });
40636         this.hiddenEl.dom.value = ar.join(',');
40637         this.validate();
40638     },
40639     
40640     reset : function()
40641     {
40642         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40643         this.items.each(function(f) {
40644            f.remove(); 
40645         });
40646         this.el.dom.value = '';
40647         if (this.hiddenEl) {
40648             this.hiddenEl.dom.value = '';
40649         }
40650         
40651     },
40652     getValue: function()
40653     {
40654         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40655     },
40656     setValue: function(v) // not a valid action - must use addItems..
40657     {
40658          
40659         this.reset();
40660         
40661         
40662         
40663         if (this.store.isLocal && (typeof(v) == 'string')) {
40664             // then we can use the store to find the values..
40665             // comma seperated at present.. this needs to allow JSON based encoding..
40666             this.hiddenEl.value  = v;
40667             var v_ar = [];
40668             Roo.each(v.split(','), function(k) {
40669                 Roo.log("CHECK " + this.valueField + ',' + k);
40670                 var li = this.store.query(this.valueField, k);
40671                 if (!li.length) {
40672                     return;
40673                 }
40674                 var add = {};
40675                 add[this.valueField] = k;
40676                 add[this.displayField] = li.item(0).data[this.displayField];
40677                 
40678                 this.addItem(add);
40679             }, this) 
40680              
40681         }
40682         if (typeof(v) == 'object' ) {
40683             // then let's assume it's an array of objects..
40684             Roo.each(v, function(l) {
40685                 this.addItem(l);
40686             }, this);
40687              
40688         }
40689         
40690         
40691     },
40692     setFromData: function(v)
40693     {
40694         // this recieves an object, if setValues is called.
40695         this.reset();
40696         this.el.dom.value = v[this.displayField];
40697         this.hiddenEl.dom.value = v[this.valueField];
40698         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40699             return;
40700         }
40701         var kv = v[this.valueField];
40702         var dv = v[this.displayField];
40703         kv = typeof(kv) != 'string' ? '' : kv;
40704         dv = typeof(dv) != 'string' ? '' : dv;
40705         
40706         
40707         var keys = kv.split(',');
40708         var display = dv.split(',');
40709         for (var i = 0 ; i < keys.length; i++) {
40710             
40711             add = {};
40712             add[this.valueField] = keys[i];
40713             add[this.displayField] = display[i];
40714             this.addItem(add);
40715         }
40716       
40717         
40718     },
40719     
40720     /**
40721      * Validates the combox array value
40722      * @return {Boolean} True if the value is valid, else false
40723      */
40724     validate : function(){
40725         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40726             this.clearInvalid();
40727             return true;
40728         }
40729         return false;
40730     },
40731     
40732     validateValue : function(value){
40733         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40734         
40735     },
40736     
40737     /*@
40738      * overide
40739      * 
40740      */
40741     isDirty : function() {
40742         if(this.disabled) {
40743             return false;
40744         }
40745         
40746         try {
40747             var d = Roo.decode(String(this.originalValue));
40748         } catch (e) {
40749             return String(this.getValue()) !== String(this.originalValue);
40750         }
40751         
40752         var originalValue = [];
40753         
40754         for (var i = 0; i < d.length; i++){
40755             originalValue.push(d[i][this.valueField]);
40756         }
40757         
40758         return String(this.getValue()) !== String(originalValue.join(','));
40759         
40760     }
40761     
40762 });
40763
40764
40765
40766 /**
40767  * @class Roo.form.ComboBoxArray.Item
40768  * @extends Roo.BoxComponent
40769  * A selected item in the list
40770  *  Fred [x]  Brian [x]  [Pick another |v]
40771  * 
40772  * @constructor
40773  * Create a new item.
40774  * @param {Object} config Configuration options
40775  */
40776  
40777 Roo.form.ComboBoxArray.Item = function(config) {
40778     config.id = Roo.id();
40779     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40780 }
40781
40782 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40783     data : {},
40784     cb: false,
40785     displayField : false,
40786     tipField : false,
40787     
40788     
40789     defaultAutoCreate : {
40790         tag: 'div',
40791         cls: 'x-cbarray-item',
40792         cn : [ 
40793             { tag: 'div' },
40794             {
40795                 tag: 'img',
40796                 width:16,
40797                 height : 16,
40798                 src : Roo.BLANK_IMAGE_URL ,
40799                 align: 'center'
40800             }
40801         ]
40802         
40803     },
40804     
40805  
40806     onRender : function(ct, position)
40807     {
40808         Roo.form.Field.superclass.onRender.call(this, ct, position);
40809         
40810         if(!this.el){
40811             var cfg = this.getAutoCreate();
40812             this.el = ct.createChild(cfg, position);
40813         }
40814         
40815         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40816         
40817         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40818             this.cb.renderer(this.data) :
40819             String.format('{0}',this.data[this.displayField]);
40820         
40821             
40822         this.el.child('div').dom.setAttribute('qtip',
40823                         String.format('{0}',this.data[this.tipField])
40824         );
40825         
40826         this.el.child('img').on('click', this.remove, this);
40827         
40828     },
40829    
40830     remove : function()
40831     {
40832         if(this.cb.disabled){
40833             return;
40834         }
40835         
40836         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
40837             this.cb.items.remove(this);
40838             this.el.child('img').un('click', this.remove, this);
40839             this.el.remove();
40840             this.cb.updateHiddenEl();
40841
40842             this.cb.fireEvent('remove', this.cb, this);
40843         }
40844         
40845     }
40846 });/*
40847  * Based on:
40848  * Ext JS Library 1.1.1
40849  * Copyright(c) 2006-2007, Ext JS, LLC.
40850  *
40851  * Originally Released Under LGPL - original licence link has changed is not relivant.
40852  *
40853  * Fork - LGPL
40854  * <script type="text/javascript">
40855  */
40856 /**
40857  * @class Roo.form.Checkbox
40858  * @extends Roo.form.Field
40859  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40860  * @constructor
40861  * Creates a new Checkbox
40862  * @param {Object} config Configuration options
40863  */
40864 Roo.form.Checkbox = function(config){
40865     Roo.form.Checkbox.superclass.constructor.call(this, config);
40866     this.addEvents({
40867         /**
40868          * @event check
40869          * Fires when the checkbox is checked or unchecked.
40870              * @param {Roo.form.Checkbox} this This checkbox
40871              * @param {Boolean} checked The new checked value
40872              */
40873         check : true
40874     });
40875 };
40876
40877 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40878     /**
40879      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40880      */
40881     focusClass : undefined,
40882     /**
40883      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40884      */
40885     fieldClass: "x-form-field",
40886     /**
40887      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40888      */
40889     checked: false,
40890     /**
40891      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40892      * {tag: "input", type: "checkbox", autocomplete: "off"})
40893      */
40894     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40895     /**
40896      * @cfg {String} boxLabel The text that appears beside the checkbox
40897      */
40898     boxLabel : "",
40899     /**
40900      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40901      */  
40902     inputValue : '1',
40903     /**
40904      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40905      */
40906      valueOff: '0', // value when not checked..
40907
40908     actionMode : 'viewEl', 
40909     //
40910     // private
40911     itemCls : 'x-menu-check-item x-form-item',
40912     groupClass : 'x-menu-group-item',
40913     inputType : 'hidden',
40914     
40915     
40916     inSetChecked: false, // check that we are not calling self...
40917     
40918     inputElement: false, // real input element?
40919     basedOn: false, // ????
40920     
40921     isFormField: true, // not sure where this is needed!!!!
40922
40923     onResize : function(){
40924         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40925         if(!this.boxLabel){
40926             this.el.alignTo(this.wrap, 'c-c');
40927         }
40928     },
40929
40930     initEvents : function(){
40931         Roo.form.Checkbox.superclass.initEvents.call(this);
40932         this.el.on("click", this.onClick,  this);
40933         this.el.on("change", this.onClick,  this);
40934     },
40935
40936
40937     getResizeEl : function(){
40938         return this.wrap;
40939     },
40940
40941     getPositionEl : function(){
40942         return this.wrap;
40943     },
40944
40945     // private
40946     onRender : function(ct, position){
40947         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40948         /*
40949         if(this.inputValue !== undefined){
40950             this.el.dom.value = this.inputValue;
40951         }
40952         */
40953         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40954         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40955         var viewEl = this.wrap.createChild({ 
40956             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40957         this.viewEl = viewEl;   
40958         this.wrap.on('click', this.onClick,  this); 
40959         
40960         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40961         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40962         
40963         
40964         
40965         if(this.boxLabel){
40966             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40967         //    viewEl.on('click', this.onClick,  this); 
40968         }
40969         //if(this.checked){
40970             this.setChecked(this.checked);
40971         //}else{
40972             //this.checked = this.el.dom;
40973         //}
40974
40975     },
40976
40977     // private
40978     initValue : Roo.emptyFn,
40979
40980     /**
40981      * Returns the checked state of the checkbox.
40982      * @return {Boolean} True if checked, else false
40983      */
40984     getValue : function(){
40985         if(this.el){
40986             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40987         }
40988         return this.valueOff;
40989         
40990     },
40991
40992         // private
40993     onClick : function(){ 
40994         if (this.disabled) {
40995             return;
40996         }
40997         this.setChecked(!this.checked);
40998
40999         //if(this.el.dom.checked != this.checked){
41000         //    this.setValue(this.el.dom.checked);
41001        // }
41002     },
41003
41004     /**
41005      * Sets the checked state of the checkbox.
41006      * On is always based on a string comparison between inputValue and the param.
41007      * @param {Boolean/String} value - the value to set 
41008      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
41009      */
41010     setValue : function(v,suppressEvent){
41011         
41012         
41013         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
41014         //if(this.el && this.el.dom){
41015         //    this.el.dom.checked = this.checked;
41016         //    this.el.dom.defaultChecked = this.checked;
41017         //}
41018         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
41019         //this.fireEvent("check", this, this.checked);
41020     },
41021     // private..
41022     setChecked : function(state,suppressEvent)
41023     {
41024         if (this.inSetChecked) {
41025             this.checked = state;
41026             return;
41027         }
41028         
41029     
41030         if(this.wrap){
41031             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
41032         }
41033         this.checked = state;
41034         if(suppressEvent !== true){
41035             this.fireEvent('check', this, state);
41036         }
41037         this.inSetChecked = true;
41038         this.el.dom.value = state ? this.inputValue : this.valueOff;
41039         this.inSetChecked = false;
41040         
41041     },
41042     // handle setting of hidden value by some other method!!?!?
41043     setFromHidden: function()
41044     {
41045         if(!this.el){
41046             return;
41047         }
41048         //console.log("SET FROM HIDDEN");
41049         //alert('setFrom hidden');
41050         this.setValue(this.el.dom.value);
41051     },
41052     
41053     onDestroy : function()
41054     {
41055         if(this.viewEl){
41056             Roo.get(this.viewEl).remove();
41057         }
41058          
41059         Roo.form.Checkbox.superclass.onDestroy.call(this);
41060     }
41061
41062 });/*
41063  * Based on:
41064  * Ext JS Library 1.1.1
41065  * Copyright(c) 2006-2007, Ext JS, LLC.
41066  *
41067  * Originally Released Under LGPL - original licence link has changed is not relivant.
41068  *
41069  * Fork - LGPL
41070  * <script type="text/javascript">
41071  */
41072  
41073 /**
41074  * @class Roo.form.Radio
41075  * @extends Roo.form.Checkbox
41076  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
41077  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
41078  * @constructor
41079  * Creates a new Radio
41080  * @param {Object} config Configuration options
41081  */
41082 Roo.form.Radio = function(){
41083     Roo.form.Radio.superclass.constructor.apply(this, arguments);
41084 };
41085 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
41086     inputType: 'radio',
41087
41088     /**
41089      * If this radio is part of a group, it will return the selected value
41090      * @return {String}
41091      */
41092     getGroupValue : function(){
41093         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
41094     },
41095     
41096     
41097     onRender : function(ct, position){
41098         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
41099         
41100         if(this.inputValue !== undefined){
41101             this.el.dom.value = this.inputValue;
41102         }
41103          
41104         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
41105         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
41106         //var viewEl = this.wrap.createChild({ 
41107         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
41108         //this.viewEl = viewEl;   
41109         //this.wrap.on('click', this.onClick,  this); 
41110         
41111         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
41112         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
41113         
41114         
41115         
41116         if(this.boxLabel){
41117             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
41118         //    viewEl.on('click', this.onClick,  this); 
41119         }
41120          if(this.checked){
41121             this.el.dom.checked =   'checked' ;
41122         }
41123          
41124     } 
41125     
41126     
41127 });//<script type="text/javascript">
41128
41129 /*
41130  * Based  Ext JS Library 1.1.1
41131  * Copyright(c) 2006-2007, Ext JS, LLC.
41132  * LGPL
41133  *
41134  */
41135  
41136 /**
41137  * @class Roo.HtmlEditorCore
41138  * @extends Roo.Component
41139  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
41140  *
41141  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
41142  */
41143
41144 Roo.HtmlEditorCore = function(config){
41145     
41146     
41147     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
41148     
41149     
41150     this.addEvents({
41151         /**
41152          * @event initialize
41153          * Fires when the editor is fully initialized (including the iframe)
41154          * @param {Roo.HtmlEditorCore} this
41155          */
41156         initialize: true,
41157         /**
41158          * @event activate
41159          * Fires when the editor is first receives the focus. Any insertion must wait
41160          * until after this event.
41161          * @param {Roo.HtmlEditorCore} this
41162          */
41163         activate: true,
41164          /**
41165          * @event beforesync
41166          * Fires before the textarea is updated with content from the editor iframe. Return false
41167          * to cancel the sync.
41168          * @param {Roo.HtmlEditorCore} this
41169          * @param {String} html
41170          */
41171         beforesync: true,
41172          /**
41173          * @event beforepush
41174          * Fires before the iframe editor is updated with content from the textarea. Return false
41175          * to cancel the push.
41176          * @param {Roo.HtmlEditorCore} this
41177          * @param {String} html
41178          */
41179         beforepush: true,
41180          /**
41181          * @event sync
41182          * Fires when the textarea is updated with content from the editor iframe.
41183          * @param {Roo.HtmlEditorCore} this
41184          * @param {String} html
41185          */
41186         sync: true,
41187          /**
41188          * @event push
41189          * Fires when the iframe editor is updated with content from the textarea.
41190          * @param {Roo.HtmlEditorCore} this
41191          * @param {String} html
41192          */
41193         push: true,
41194         
41195         /**
41196          * @event editorevent
41197          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41198          * @param {Roo.HtmlEditorCore} this
41199          */
41200         editorevent: true
41201         
41202     });
41203     
41204     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
41205     
41206     // defaults : white / black...
41207     this.applyBlacklists();
41208     
41209     
41210     
41211 };
41212
41213
41214 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41215
41216
41217      /**
41218      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41219      */
41220     
41221     owner : false,
41222     
41223      /**
41224      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41225      *                        Roo.resizable.
41226      */
41227     resizable : false,
41228      /**
41229      * @cfg {Number} height (in pixels)
41230      */   
41231     height: 300,
41232    /**
41233      * @cfg {Number} width (in pixels)
41234      */   
41235     width: 500,
41236     
41237     /**
41238      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41239      * 
41240      */
41241     stylesheets: false,
41242     
41243     // id of frame..
41244     frameId: false,
41245     
41246     // private properties
41247     validationEvent : false,
41248     deferHeight: true,
41249     initialized : false,
41250     activated : false,
41251     sourceEditMode : false,
41252     onFocus : Roo.emptyFn,
41253     iframePad:3,
41254     hideMode:'offsets',
41255     
41256     clearUp: true,
41257     
41258     // blacklist + whitelisted elements..
41259     black: false,
41260     white: false,
41261      
41262     
41263
41264     /**
41265      * Protected method that will not generally be called directly. It
41266      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41267      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41268      */
41269     getDocMarkup : function(){
41270         // body styles..
41271         var st = '';
41272         
41273         // inherit styels from page...?? 
41274         if (this.stylesheets === false) {
41275             
41276             Roo.get(document.head).select('style').each(function(node) {
41277                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41278             });
41279             
41280             Roo.get(document.head).select('link').each(function(node) { 
41281                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41282             });
41283             
41284         } else if (!this.stylesheets.length) {
41285                 // simple..
41286                 st = '<style type="text/css">' +
41287                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41288                    '</style>';
41289         } else { 
41290             
41291         }
41292         
41293         st +=  '<style type="text/css">' +
41294             'IMG { cursor: pointer } ' +
41295         '</style>';
41296
41297         
41298         return '<html><head>' + st  +
41299             //<style type="text/css">' +
41300             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41301             //'</style>' +
41302             ' </head><body class="roo-htmleditor-body"></body></html>';
41303     },
41304
41305     // private
41306     onRender : function(ct, position)
41307     {
41308         var _t = this;
41309         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41310         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41311         
41312         
41313         this.el.dom.style.border = '0 none';
41314         this.el.dom.setAttribute('tabIndex', -1);
41315         this.el.addClass('x-hidden hide');
41316         
41317         
41318         
41319         if(Roo.isIE){ // fix IE 1px bogus margin
41320             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41321         }
41322        
41323         
41324         this.frameId = Roo.id();
41325         
41326          
41327         
41328         var iframe = this.owner.wrap.createChild({
41329             tag: 'iframe',
41330             cls: 'form-control', // bootstrap..
41331             id: this.frameId,
41332             name: this.frameId,
41333             frameBorder : 'no',
41334             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41335         }, this.el
41336         );
41337         
41338         
41339         this.iframe = iframe.dom;
41340
41341          this.assignDocWin();
41342         
41343         this.doc.designMode = 'on';
41344        
41345         this.doc.open();
41346         this.doc.write(this.getDocMarkup());
41347         this.doc.close();
41348
41349         
41350         var task = { // must defer to wait for browser to be ready
41351             run : function(){
41352                 //console.log("run task?" + this.doc.readyState);
41353                 this.assignDocWin();
41354                 if(this.doc.body || this.doc.readyState == 'complete'){
41355                     try {
41356                         this.doc.designMode="on";
41357                     } catch (e) {
41358                         return;
41359                     }
41360                     Roo.TaskMgr.stop(task);
41361                     this.initEditor.defer(10, this);
41362                 }
41363             },
41364             interval : 10,
41365             duration: 10000,
41366             scope: this
41367         };
41368         Roo.TaskMgr.start(task);
41369
41370     },
41371
41372     // private
41373     onResize : function(w, h)
41374     {
41375          Roo.log('resize: ' +w + ',' + h );
41376         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41377         if(!this.iframe){
41378             return;
41379         }
41380         if(typeof w == 'number'){
41381             
41382             this.iframe.style.width = w + 'px';
41383         }
41384         if(typeof h == 'number'){
41385             
41386             this.iframe.style.height = h + 'px';
41387             if(this.doc){
41388                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41389             }
41390         }
41391         
41392     },
41393
41394     /**
41395      * Toggles the editor between standard and source edit mode.
41396      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41397      */
41398     toggleSourceEdit : function(sourceEditMode){
41399         
41400         this.sourceEditMode = sourceEditMode === true;
41401         
41402         if(this.sourceEditMode){
41403  
41404             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41405             
41406         }else{
41407             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41408             //this.iframe.className = '';
41409             this.deferFocus();
41410         }
41411         //this.setSize(this.owner.wrap.getSize());
41412         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41413     },
41414
41415     
41416   
41417
41418     /**
41419      * Protected method that will not generally be called directly. If you need/want
41420      * custom HTML cleanup, this is the method you should override.
41421      * @param {String} html The HTML to be cleaned
41422      * return {String} The cleaned HTML
41423      */
41424     cleanHtml : function(html){
41425         html = String(html);
41426         if(html.length > 5){
41427             if(Roo.isSafari){ // strip safari nonsense
41428                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41429             }
41430         }
41431         if(html == '&nbsp;'){
41432             html = '';
41433         }
41434         return html;
41435     },
41436
41437     /**
41438      * HTML Editor -> Textarea
41439      * Protected method that will not generally be called directly. Syncs the contents
41440      * of the editor iframe with the textarea.
41441      */
41442     syncValue : function(){
41443         if(this.initialized){
41444             var bd = (this.doc.body || this.doc.documentElement);
41445             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41446             var html = bd.innerHTML;
41447             if(Roo.isSafari){
41448                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41449                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41450                 if(m && m[1]){
41451                     html = '<div style="'+m[0]+'">' + html + '</div>';
41452                 }
41453             }
41454             html = this.cleanHtml(html);
41455             // fix up the special chars.. normaly like back quotes in word...
41456             // however we do not want to do this with chinese..
41457             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41458                 var cc = b.charCodeAt();
41459                 if (
41460                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41461                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41462                     (cc >= 0xf900 && cc < 0xfb00 )
41463                 ) {
41464                         return b;
41465                 }
41466                 return "&#"+cc+";" 
41467             });
41468             if(this.owner.fireEvent('beforesync', this, html) !== false){
41469                 this.el.dom.value = html;
41470                 this.owner.fireEvent('sync', this, html);
41471             }
41472         }
41473     },
41474
41475     /**
41476      * Protected method that will not generally be called directly. Pushes the value of the textarea
41477      * into the iframe editor.
41478      */
41479     pushValue : function(){
41480         if(this.initialized){
41481             var v = this.el.dom.value.trim();
41482             
41483 //            if(v.length < 1){
41484 //                v = '&#160;';
41485 //            }
41486             
41487             if(this.owner.fireEvent('beforepush', this, v) !== false){
41488                 var d = (this.doc.body || this.doc.documentElement);
41489                 d.innerHTML = v;
41490                 this.cleanUpPaste();
41491                 this.el.dom.value = d.innerHTML;
41492                 this.owner.fireEvent('push', this, v);
41493             }
41494         }
41495     },
41496
41497     // private
41498     deferFocus : function(){
41499         this.focus.defer(10, this);
41500     },
41501
41502     // doc'ed in Field
41503     focus : function(){
41504         if(this.win && !this.sourceEditMode){
41505             this.win.focus();
41506         }else{
41507             this.el.focus();
41508         }
41509     },
41510     
41511     assignDocWin: function()
41512     {
41513         var iframe = this.iframe;
41514         
41515          if(Roo.isIE){
41516             this.doc = iframe.contentWindow.document;
41517             this.win = iframe.contentWindow;
41518         } else {
41519 //            if (!Roo.get(this.frameId)) {
41520 //                return;
41521 //            }
41522 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41523 //            this.win = Roo.get(this.frameId).dom.contentWindow;
41524             
41525             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
41526                 return;
41527             }
41528             
41529             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41530             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
41531         }
41532     },
41533     
41534     // private
41535     initEditor : function(){
41536         //console.log("INIT EDITOR");
41537         this.assignDocWin();
41538         
41539         
41540         
41541         this.doc.designMode="on";
41542         this.doc.open();
41543         this.doc.write(this.getDocMarkup());
41544         this.doc.close();
41545         
41546         var dbody = (this.doc.body || this.doc.documentElement);
41547         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41548         // this copies styles from the containing element into thsi one..
41549         // not sure why we need all of this..
41550         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41551         
41552         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41553         //ss['background-attachment'] = 'fixed'; // w3c
41554         dbody.bgProperties = 'fixed'; // ie
41555         //Roo.DomHelper.applyStyles(dbody, ss);
41556         Roo.EventManager.on(this.doc, {
41557             //'mousedown': this.onEditorEvent,
41558             'mouseup': this.onEditorEvent,
41559             'dblclick': this.onEditorEvent,
41560             'click': this.onEditorEvent,
41561             'keyup': this.onEditorEvent,
41562             buffer:100,
41563             scope: this
41564         });
41565         if(Roo.isGecko){
41566             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41567         }
41568         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41569             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41570         }
41571         this.initialized = true;
41572
41573         this.owner.fireEvent('initialize', this);
41574         this.pushValue();
41575     },
41576
41577     // private
41578     onDestroy : function(){
41579         
41580         
41581         
41582         if(this.rendered){
41583             
41584             //for (var i =0; i < this.toolbars.length;i++) {
41585             //    // fixme - ask toolbars for heights?
41586             //    this.toolbars[i].onDestroy();
41587            // }
41588             
41589             //this.wrap.dom.innerHTML = '';
41590             //this.wrap.remove();
41591         }
41592     },
41593
41594     // private
41595     onFirstFocus : function(){
41596         
41597         this.assignDocWin();
41598         
41599         
41600         this.activated = true;
41601          
41602     
41603         if(Roo.isGecko){ // prevent silly gecko errors
41604             this.win.focus();
41605             var s = this.win.getSelection();
41606             if(!s.focusNode || s.focusNode.nodeType != 3){
41607                 var r = s.getRangeAt(0);
41608                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41609                 r.collapse(true);
41610                 this.deferFocus();
41611             }
41612             try{
41613                 this.execCmd('useCSS', true);
41614                 this.execCmd('styleWithCSS', false);
41615             }catch(e){}
41616         }
41617         this.owner.fireEvent('activate', this);
41618     },
41619
41620     // private
41621     adjustFont: function(btn){
41622         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41623         //if(Roo.isSafari){ // safari
41624         //    adjust *= 2;
41625        // }
41626         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41627         if(Roo.isSafari){ // safari
41628             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41629             v =  (v < 10) ? 10 : v;
41630             v =  (v > 48) ? 48 : v;
41631             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41632             
41633         }
41634         
41635         
41636         v = Math.max(1, v+adjust);
41637         
41638         this.execCmd('FontSize', v  );
41639     },
41640
41641     onEditorEvent : function(e)
41642     {
41643         this.owner.fireEvent('editorevent', this, e);
41644       //  this.updateToolbar();
41645         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41646     },
41647
41648     insertTag : function(tg)
41649     {
41650         // could be a bit smarter... -> wrap the current selected tRoo..
41651         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41652             
41653             range = this.createRange(this.getSelection());
41654             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41655             wrappingNode.appendChild(range.extractContents());
41656             range.insertNode(wrappingNode);
41657
41658             return;
41659             
41660             
41661             
41662         }
41663         this.execCmd("formatblock",   tg);
41664         
41665     },
41666     
41667     insertText : function(txt)
41668     {
41669         
41670         
41671         var range = this.createRange();
41672         range.deleteContents();
41673                //alert(Sender.getAttribute('label'));
41674                
41675         range.insertNode(this.doc.createTextNode(txt));
41676     } ,
41677     
41678      
41679
41680     /**
41681      * Executes a Midas editor command on the editor document and performs necessary focus and
41682      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41683      * @param {String} cmd The Midas command
41684      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41685      */
41686     relayCmd : function(cmd, value){
41687         this.win.focus();
41688         this.execCmd(cmd, value);
41689         this.owner.fireEvent('editorevent', this);
41690         //this.updateToolbar();
41691         this.owner.deferFocus();
41692     },
41693
41694     /**
41695      * Executes a Midas editor command directly on the editor document.
41696      * For visual commands, you should use {@link #relayCmd} instead.
41697      * <b>This should only be called after the editor is initialized.</b>
41698      * @param {String} cmd The Midas command
41699      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41700      */
41701     execCmd : function(cmd, value){
41702         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41703         this.syncValue();
41704     },
41705  
41706  
41707    
41708     /**
41709      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41710      * to insert tRoo.
41711      * @param {String} text | dom node.. 
41712      */
41713     insertAtCursor : function(text)
41714     {
41715         
41716         
41717         
41718         if(!this.activated){
41719             return;
41720         }
41721         /*
41722         if(Roo.isIE){
41723             this.win.focus();
41724             var r = this.doc.selection.createRange();
41725             if(r){
41726                 r.collapse(true);
41727                 r.pasteHTML(text);
41728                 this.syncValue();
41729                 this.deferFocus();
41730             
41731             }
41732             return;
41733         }
41734         */
41735         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41736             this.win.focus();
41737             
41738             
41739             // from jquery ui (MIT licenced)
41740             var range, node;
41741             var win = this.win;
41742             
41743             if (win.getSelection && win.getSelection().getRangeAt) {
41744                 range = win.getSelection().getRangeAt(0);
41745                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41746                 range.insertNode(node);
41747             } else if (win.document.selection && win.document.selection.createRange) {
41748                 // no firefox support
41749                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41750                 win.document.selection.createRange().pasteHTML(txt);
41751             } else {
41752                 // no firefox support
41753                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41754                 this.execCmd('InsertHTML', txt);
41755             } 
41756             
41757             this.syncValue();
41758             
41759             this.deferFocus();
41760         }
41761     },
41762  // private
41763     mozKeyPress : function(e){
41764         if(e.ctrlKey){
41765             var c = e.getCharCode(), cmd;
41766           
41767             if(c > 0){
41768                 c = String.fromCharCode(c).toLowerCase();
41769                 switch(c){
41770                     case 'b':
41771                         cmd = 'bold';
41772                         break;
41773                     case 'i':
41774                         cmd = 'italic';
41775                         break;
41776                     
41777                     case 'u':
41778                         cmd = 'underline';
41779                         break;
41780                     
41781                     case 'v':
41782                         this.cleanUpPaste.defer(100, this);
41783                         return;
41784                         
41785                 }
41786                 if(cmd){
41787                     this.win.focus();
41788                     this.execCmd(cmd);
41789                     this.deferFocus();
41790                     e.preventDefault();
41791                 }
41792                 
41793             }
41794         }
41795     },
41796
41797     // private
41798     fixKeys : function(){ // load time branching for fastest keydown performance
41799         if(Roo.isIE){
41800             return function(e){
41801                 var k = e.getKey(), r;
41802                 if(k == e.TAB){
41803                     e.stopEvent();
41804                     r = this.doc.selection.createRange();
41805                     if(r){
41806                         r.collapse(true);
41807                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41808                         this.deferFocus();
41809                     }
41810                     return;
41811                 }
41812                 
41813                 if(k == e.ENTER){
41814                     r = this.doc.selection.createRange();
41815                     if(r){
41816                         var target = r.parentElement();
41817                         if(!target || target.tagName.toLowerCase() != 'li'){
41818                             e.stopEvent();
41819                             r.pasteHTML('<br />');
41820                             r.collapse(false);
41821                             r.select();
41822                         }
41823                     }
41824                 }
41825                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41826                     this.cleanUpPaste.defer(100, this);
41827                     return;
41828                 }
41829                 
41830                 
41831             };
41832         }else if(Roo.isOpera){
41833             return function(e){
41834                 var k = e.getKey();
41835                 if(k == e.TAB){
41836                     e.stopEvent();
41837                     this.win.focus();
41838                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41839                     this.deferFocus();
41840                 }
41841                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41842                     this.cleanUpPaste.defer(100, this);
41843                     return;
41844                 }
41845                 
41846             };
41847         }else if(Roo.isSafari){
41848             return function(e){
41849                 var k = e.getKey();
41850                 
41851                 if(k == e.TAB){
41852                     e.stopEvent();
41853                     this.execCmd('InsertText','\t');
41854                     this.deferFocus();
41855                     return;
41856                 }
41857                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41858                     this.cleanUpPaste.defer(100, this);
41859                     return;
41860                 }
41861                 
41862              };
41863         }
41864     }(),
41865     
41866     getAllAncestors: function()
41867     {
41868         var p = this.getSelectedNode();
41869         var a = [];
41870         if (!p) {
41871             a.push(p); // push blank onto stack..
41872             p = this.getParentElement();
41873         }
41874         
41875         
41876         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41877             a.push(p);
41878             p = p.parentNode;
41879         }
41880         a.push(this.doc.body);
41881         return a;
41882     },
41883     lastSel : false,
41884     lastSelNode : false,
41885     
41886     
41887     getSelection : function() 
41888     {
41889         this.assignDocWin();
41890         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41891     },
41892     
41893     getSelectedNode: function() 
41894     {
41895         // this may only work on Gecko!!!
41896         
41897         // should we cache this!!!!
41898         
41899         
41900         
41901          
41902         var range = this.createRange(this.getSelection()).cloneRange();
41903         
41904         if (Roo.isIE) {
41905             var parent = range.parentElement();
41906             while (true) {
41907                 var testRange = range.duplicate();
41908                 testRange.moveToElementText(parent);
41909                 if (testRange.inRange(range)) {
41910                     break;
41911                 }
41912                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41913                     break;
41914                 }
41915                 parent = parent.parentElement;
41916             }
41917             return parent;
41918         }
41919         
41920         // is ancestor a text element.
41921         var ac =  range.commonAncestorContainer;
41922         if (ac.nodeType == 3) {
41923             ac = ac.parentNode;
41924         }
41925         
41926         var ar = ac.childNodes;
41927          
41928         var nodes = [];
41929         var other_nodes = [];
41930         var has_other_nodes = false;
41931         for (var i=0;i<ar.length;i++) {
41932             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41933                 continue;
41934             }
41935             // fullly contained node.
41936             
41937             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41938                 nodes.push(ar[i]);
41939                 continue;
41940             }
41941             
41942             // probably selected..
41943             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41944                 other_nodes.push(ar[i]);
41945                 continue;
41946             }
41947             // outer..
41948             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41949                 continue;
41950             }
41951             
41952             
41953             has_other_nodes = true;
41954         }
41955         if (!nodes.length && other_nodes.length) {
41956             nodes= other_nodes;
41957         }
41958         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41959             return false;
41960         }
41961         
41962         return nodes[0];
41963     },
41964     createRange: function(sel)
41965     {
41966         // this has strange effects when using with 
41967         // top toolbar - not sure if it's a great idea.
41968         //this.editor.contentWindow.focus();
41969         if (typeof sel != "undefined") {
41970             try {
41971                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41972             } catch(e) {
41973                 return this.doc.createRange();
41974             }
41975         } else {
41976             return this.doc.createRange();
41977         }
41978     },
41979     getParentElement: function()
41980     {
41981         
41982         this.assignDocWin();
41983         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41984         
41985         var range = this.createRange(sel);
41986          
41987         try {
41988             var p = range.commonAncestorContainer;
41989             while (p.nodeType == 3) { // text node
41990                 p = p.parentNode;
41991             }
41992             return p;
41993         } catch (e) {
41994             return null;
41995         }
41996     
41997     },
41998     /***
41999      *
42000      * Range intersection.. the hard stuff...
42001      *  '-1' = before
42002      *  '0' = hits..
42003      *  '1' = after.
42004      *         [ -- selected range --- ]
42005      *   [fail]                        [fail]
42006      *
42007      *    basically..
42008      *      if end is before start or  hits it. fail.
42009      *      if start is after end or hits it fail.
42010      *
42011      *   if either hits (but other is outside. - then it's not 
42012      *   
42013      *    
42014      **/
42015     
42016     
42017     // @see http://www.thismuchiknow.co.uk/?p=64.
42018     rangeIntersectsNode : function(range, node)
42019     {
42020         var nodeRange = node.ownerDocument.createRange();
42021         try {
42022             nodeRange.selectNode(node);
42023         } catch (e) {
42024             nodeRange.selectNodeContents(node);
42025         }
42026     
42027         var rangeStartRange = range.cloneRange();
42028         rangeStartRange.collapse(true);
42029     
42030         var rangeEndRange = range.cloneRange();
42031         rangeEndRange.collapse(false);
42032     
42033         var nodeStartRange = nodeRange.cloneRange();
42034         nodeStartRange.collapse(true);
42035     
42036         var nodeEndRange = nodeRange.cloneRange();
42037         nodeEndRange.collapse(false);
42038     
42039         return rangeStartRange.compareBoundaryPoints(
42040                  Range.START_TO_START, nodeEndRange) == -1 &&
42041                rangeEndRange.compareBoundaryPoints(
42042                  Range.START_TO_START, nodeStartRange) == 1;
42043         
42044          
42045     },
42046     rangeCompareNode : function(range, node)
42047     {
42048         var nodeRange = node.ownerDocument.createRange();
42049         try {
42050             nodeRange.selectNode(node);
42051         } catch (e) {
42052             nodeRange.selectNodeContents(node);
42053         }
42054         
42055         
42056         range.collapse(true);
42057     
42058         nodeRange.collapse(true);
42059      
42060         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
42061         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
42062          
42063         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
42064         
42065         var nodeIsBefore   =  ss == 1;
42066         var nodeIsAfter    = ee == -1;
42067         
42068         if (nodeIsBefore && nodeIsAfter)
42069             return 0; // outer
42070         if (!nodeIsBefore && nodeIsAfter)
42071             return 1; //right trailed.
42072         
42073         if (nodeIsBefore && !nodeIsAfter)
42074             return 2;  // left trailed.
42075         // fully contined.
42076         return 3;
42077     },
42078
42079     // private? - in a new class?
42080     cleanUpPaste :  function()
42081     {
42082         // cleans up the whole document..
42083         Roo.log('cleanuppaste');
42084         
42085         this.cleanUpChildren(this.doc.body);
42086         var clean = this.cleanWordChars(this.doc.body.innerHTML);
42087         if (clean != this.doc.body.innerHTML) {
42088             this.doc.body.innerHTML = clean;
42089         }
42090         
42091     },
42092     
42093     cleanWordChars : function(input) {// change the chars to hex code
42094         var he = Roo.HtmlEditorCore;
42095         
42096         var output = input;
42097         Roo.each(he.swapCodes, function(sw) { 
42098             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
42099             
42100             output = output.replace(swapper, sw[1]);
42101         });
42102         
42103         return output;
42104     },
42105     
42106     
42107     cleanUpChildren : function (n)
42108     {
42109         if (!n.childNodes.length) {
42110             return;
42111         }
42112         for (var i = n.childNodes.length-1; i > -1 ; i--) {
42113            this.cleanUpChild(n.childNodes[i]);
42114         }
42115     },
42116     
42117     
42118         
42119     
42120     cleanUpChild : function (node)
42121     {
42122         var ed = this;
42123         //console.log(node);
42124         if (node.nodeName == "#text") {
42125             // clean up silly Windows -- stuff?
42126             return; 
42127         }
42128         if (node.nodeName == "#comment") {
42129             node.parentNode.removeChild(node);
42130             // clean up silly Windows -- stuff?
42131             return; 
42132         }
42133         var lcname = node.tagName.toLowerCase();
42134         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
42135         // whitelist of tags..
42136         
42137         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
42138             // remove node.
42139             node.parentNode.removeChild(node);
42140             return;
42141             
42142         }
42143         
42144         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
42145         
42146         // remove <a name=....> as rendering on yahoo mailer is borked with this.
42147         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
42148         
42149         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
42150         //    remove_keep_children = true;
42151         //}
42152         
42153         if (remove_keep_children) {
42154             this.cleanUpChildren(node);
42155             // inserts everything just before this node...
42156             while (node.childNodes.length) {
42157                 var cn = node.childNodes[0];
42158                 node.removeChild(cn);
42159                 node.parentNode.insertBefore(cn, node);
42160             }
42161             node.parentNode.removeChild(node);
42162             return;
42163         }
42164         
42165         if (!node.attributes || !node.attributes.length) {
42166             this.cleanUpChildren(node);
42167             return;
42168         }
42169         
42170         function cleanAttr(n,v)
42171         {
42172             
42173             if (v.match(/^\./) || v.match(/^\//)) {
42174                 return;
42175             }
42176             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
42177                 return;
42178             }
42179             if (v.match(/^#/)) {
42180                 return;
42181             }
42182 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
42183             node.removeAttribute(n);
42184             
42185         }
42186         
42187         var cwhite = this.cwhite;
42188         var cblack = this.cblack;
42189             
42190         function cleanStyle(n,v)
42191         {
42192             if (v.match(/expression/)) { //XSS?? should we even bother..
42193                 node.removeAttribute(n);
42194                 return;
42195             }
42196             
42197             var parts = v.split(/;/);
42198             var clean = [];
42199             
42200             Roo.each(parts, function(p) {
42201                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42202                 if (!p.length) {
42203                     return true;
42204                 }
42205                 var l = p.split(':').shift().replace(/\s+/g,'');
42206                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42207                 
42208                 if ( cwhite.length && cblack.indexOf(l) > -1) {
42209 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42210                     //node.removeAttribute(n);
42211                     return true;
42212                 }
42213                 //Roo.log()
42214                 // only allow 'c whitelisted system attributes'
42215                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42216 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42217                     //node.removeAttribute(n);
42218                     return true;
42219                 }
42220                 
42221                 
42222                  
42223                 
42224                 clean.push(p);
42225                 return true;
42226             });
42227             if (clean.length) { 
42228                 node.setAttribute(n, clean.join(';'));
42229             } else {
42230                 node.removeAttribute(n);
42231             }
42232             
42233         }
42234         
42235         
42236         for (var i = node.attributes.length-1; i > -1 ; i--) {
42237             var a = node.attributes[i];
42238             //console.log(a);
42239             
42240             if (a.name.toLowerCase().substr(0,2)=='on')  {
42241                 node.removeAttribute(a.name);
42242                 continue;
42243             }
42244             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42245                 node.removeAttribute(a.name);
42246                 continue;
42247             }
42248             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42249                 cleanAttr(a.name,a.value); // fixme..
42250                 continue;
42251             }
42252             if (a.name == 'style') {
42253                 cleanStyle(a.name,a.value);
42254                 continue;
42255             }
42256             /// clean up MS crap..
42257             // tecnically this should be a list of valid class'es..
42258             
42259             
42260             if (a.name == 'class') {
42261                 if (a.value.match(/^Mso/)) {
42262                     node.className = '';
42263                 }
42264                 
42265                 if (a.value.match(/body/)) {
42266                     node.className = '';
42267                 }
42268                 continue;
42269             }
42270             
42271             // style cleanup!?
42272             // class cleanup?
42273             
42274         }
42275         
42276         
42277         this.cleanUpChildren(node);
42278         
42279         
42280     },
42281     
42282     /**
42283      * Clean up MS wordisms...
42284      */
42285     cleanWord : function(node)
42286     {
42287         
42288         
42289         if (!node) {
42290             this.cleanWord(this.doc.body);
42291             return;
42292         }
42293         if (node.nodeName == "#text") {
42294             // clean up silly Windows -- stuff?
42295             return; 
42296         }
42297         if (node.nodeName == "#comment") {
42298             node.parentNode.removeChild(node);
42299             // clean up silly Windows -- stuff?
42300             return; 
42301         }
42302         
42303         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42304             node.parentNode.removeChild(node);
42305             return;
42306         }
42307         
42308         // remove - but keep children..
42309         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42310             while (node.childNodes.length) {
42311                 var cn = node.childNodes[0];
42312                 node.removeChild(cn);
42313                 node.parentNode.insertBefore(cn, node);
42314             }
42315             node.parentNode.removeChild(node);
42316             this.iterateChildren(node, this.cleanWord);
42317             return;
42318         }
42319         // clean styles
42320         if (node.className.length) {
42321             
42322             var cn = node.className.split(/\W+/);
42323             var cna = [];
42324             Roo.each(cn, function(cls) {
42325                 if (cls.match(/Mso[a-zA-Z]+/)) {
42326                     return;
42327                 }
42328                 cna.push(cls);
42329             });
42330             node.className = cna.length ? cna.join(' ') : '';
42331             if (!cna.length) {
42332                 node.removeAttribute("class");
42333             }
42334         }
42335         
42336         if (node.hasAttribute("lang")) {
42337             node.removeAttribute("lang");
42338         }
42339         
42340         if (node.hasAttribute("style")) {
42341             
42342             var styles = node.getAttribute("style").split(";");
42343             var nstyle = [];
42344             Roo.each(styles, function(s) {
42345                 if (!s.match(/:/)) {
42346                     return;
42347                 }
42348                 var kv = s.split(":");
42349                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42350                     return;
42351                 }
42352                 // what ever is left... we allow.
42353                 nstyle.push(s);
42354             });
42355             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42356             if (!nstyle.length) {
42357                 node.removeAttribute('style');
42358             }
42359         }
42360         this.iterateChildren(node, this.cleanWord);
42361         
42362         
42363         
42364     },
42365     /**
42366      * iterateChildren of a Node, calling fn each time, using this as the scole..
42367      * @param {DomNode} node node to iterate children of.
42368      * @param {Function} fn method of this class to call on each item.
42369      */
42370     iterateChildren : function(node, fn)
42371     {
42372         if (!node.childNodes.length) {
42373                 return;
42374         }
42375         for (var i = node.childNodes.length-1; i > -1 ; i--) {
42376            fn.call(this, node.childNodes[i])
42377         }
42378     },
42379     
42380     
42381     /**
42382      * cleanTableWidths.
42383      *
42384      * Quite often pasting from word etc.. results in tables with column and widths.
42385      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
42386      *
42387      */
42388     cleanTableWidths : function(node)
42389     {
42390          
42391          
42392         if (!node) {
42393             this.cleanTableWidths(this.doc.body);
42394             return;
42395         }
42396         
42397         // ignore list...
42398         if (node.nodeName == "#text" || node.nodeName == "#comment") {
42399             return; 
42400         }
42401         Roo.log(node.tagName);
42402         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
42403             this.iterateChildren(node, this.cleanTableWidths);
42404             return;
42405         }
42406         if (node.hasAttribute('width')) {
42407             node.removeAttribute('width');
42408         }
42409         
42410          
42411         if (node.hasAttribute("style")) {
42412             // pretty basic...
42413             
42414             var styles = node.getAttribute("style").split(";");
42415             var nstyle = [];
42416             Roo.each(styles, function(s) {
42417                 if (!s.match(/:/)) {
42418                     return;
42419                 }
42420                 var kv = s.split(":");
42421                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
42422                     return;
42423                 }
42424                 // what ever is left... we allow.
42425                 nstyle.push(s);
42426             });
42427             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42428             if (!nstyle.length) {
42429                 node.removeAttribute('style');
42430             }
42431         }
42432         
42433         this.iterateChildren(node, this.cleanTableWidths);
42434         
42435         
42436     },
42437     
42438     
42439     
42440     
42441     domToHTML : function(currentElement, depth, nopadtext) {
42442         
42443         depth = depth || 0;
42444         nopadtext = nopadtext || false;
42445     
42446         if (!currentElement) {
42447             return this.domToHTML(this.doc.body);
42448         }
42449         
42450         //Roo.log(currentElement);
42451         var j;
42452         var allText = false;
42453         var nodeName = currentElement.nodeName;
42454         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42455         
42456         if  (nodeName == '#text') {
42457             
42458             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
42459         }
42460         
42461         
42462         var ret = '';
42463         if (nodeName != 'BODY') {
42464              
42465             var i = 0;
42466             // Prints the node tagName, such as <A>, <IMG>, etc
42467             if (tagName) {
42468                 var attr = [];
42469                 for(i = 0; i < currentElement.attributes.length;i++) {
42470                     // quoting?
42471                     var aname = currentElement.attributes.item(i).name;
42472                     if (!currentElement.attributes.item(i).value.length) {
42473                         continue;
42474                     }
42475                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42476                 }
42477                 
42478                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42479             } 
42480             else {
42481                 
42482                 // eack
42483             }
42484         } else {
42485             tagName = false;
42486         }
42487         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42488             return ret;
42489         }
42490         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42491             nopadtext = true;
42492         }
42493         
42494         
42495         // Traverse the tree
42496         i = 0;
42497         var currentElementChild = currentElement.childNodes.item(i);
42498         var allText = true;
42499         var innerHTML  = '';
42500         lastnode = '';
42501         while (currentElementChild) {
42502             // Formatting code (indent the tree so it looks nice on the screen)
42503             var nopad = nopadtext;
42504             if (lastnode == 'SPAN') {
42505                 nopad  = true;
42506             }
42507             // text
42508             if  (currentElementChild.nodeName == '#text') {
42509                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42510                 toadd = nopadtext ? toadd : toadd.trim();
42511                 if (!nopad && toadd.length > 80) {
42512                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42513                 }
42514                 innerHTML  += toadd;
42515                 
42516                 i++;
42517                 currentElementChild = currentElement.childNodes.item(i);
42518                 lastNode = '';
42519                 continue;
42520             }
42521             allText = false;
42522             
42523             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42524                 
42525             // Recursively traverse the tree structure of the child node
42526             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42527             lastnode = currentElementChild.nodeName;
42528             i++;
42529             currentElementChild=currentElement.childNodes.item(i);
42530         }
42531         
42532         ret += innerHTML;
42533         
42534         if (!allText) {
42535                 // The remaining code is mostly for formatting the tree
42536             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42537         }
42538         
42539         
42540         if (tagName) {
42541             ret+= "</"+tagName+">";
42542         }
42543         return ret;
42544         
42545     },
42546         
42547     applyBlacklists : function()
42548     {
42549         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
42550         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
42551         
42552         this.white = [];
42553         this.black = [];
42554         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
42555             if (b.indexOf(tag) > -1) {
42556                 return;
42557             }
42558             this.white.push(tag);
42559             
42560         }, this);
42561         
42562         Roo.each(w, function(tag) {
42563             if (b.indexOf(tag) > -1) {
42564                 return;
42565             }
42566             if (this.white.indexOf(tag) > -1) {
42567                 return;
42568             }
42569             this.white.push(tag);
42570             
42571         }, this);
42572         
42573         
42574         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
42575             if (w.indexOf(tag) > -1) {
42576                 return;
42577             }
42578             this.black.push(tag);
42579             
42580         }, this);
42581         
42582         Roo.each(b, function(tag) {
42583             if (w.indexOf(tag) > -1) {
42584                 return;
42585             }
42586             if (this.black.indexOf(tag) > -1) {
42587                 return;
42588             }
42589             this.black.push(tag);
42590             
42591         }, this);
42592         
42593         
42594         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
42595         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
42596         
42597         this.cwhite = [];
42598         this.cblack = [];
42599         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
42600             if (b.indexOf(tag) > -1) {
42601                 return;
42602             }
42603             this.cwhite.push(tag);
42604             
42605         }, this);
42606         
42607         Roo.each(w, function(tag) {
42608             if (b.indexOf(tag) > -1) {
42609                 return;
42610             }
42611             if (this.cwhite.indexOf(tag) > -1) {
42612                 return;
42613             }
42614             this.cwhite.push(tag);
42615             
42616         }, this);
42617         
42618         
42619         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
42620             if (w.indexOf(tag) > -1) {
42621                 return;
42622             }
42623             this.cblack.push(tag);
42624             
42625         }, this);
42626         
42627         Roo.each(b, function(tag) {
42628             if (w.indexOf(tag) > -1) {
42629                 return;
42630             }
42631             if (this.cblack.indexOf(tag) > -1) {
42632                 return;
42633             }
42634             this.cblack.push(tag);
42635             
42636         }, this);
42637     },
42638     
42639     setStylesheets : function(stylesheets)
42640     {
42641         if(typeof(stylesheets) == 'string'){
42642             Roo.get(this.iframe.contentDocument.head).createChild({
42643                 tag : 'link',
42644                 rel : 'stylesheet',
42645                 type : 'text/css',
42646                 href : stylesheets
42647             });
42648             
42649             return;
42650         }
42651         var _this = this;
42652      
42653         Roo.each(stylesheets, function(s) {
42654             if(!s.length){
42655                 return;
42656             }
42657             
42658             Roo.get(_this.iframe.contentDocument.head).createChild({
42659                 tag : 'link',
42660                 rel : 'stylesheet',
42661                 type : 'text/css',
42662                 href : s
42663             });
42664         });
42665
42666         
42667     },
42668     
42669     removeStylesheets : function()
42670     {
42671         var _this = this;
42672         
42673         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
42674             s.remove();
42675         });
42676     }
42677     
42678     // hide stuff that is not compatible
42679     /**
42680      * @event blur
42681      * @hide
42682      */
42683     /**
42684      * @event change
42685      * @hide
42686      */
42687     /**
42688      * @event focus
42689      * @hide
42690      */
42691     /**
42692      * @event specialkey
42693      * @hide
42694      */
42695     /**
42696      * @cfg {String} fieldClass @hide
42697      */
42698     /**
42699      * @cfg {String} focusClass @hide
42700      */
42701     /**
42702      * @cfg {String} autoCreate @hide
42703      */
42704     /**
42705      * @cfg {String} inputType @hide
42706      */
42707     /**
42708      * @cfg {String} invalidClass @hide
42709      */
42710     /**
42711      * @cfg {String} invalidText @hide
42712      */
42713     /**
42714      * @cfg {String} msgFx @hide
42715      */
42716     /**
42717      * @cfg {String} validateOnBlur @hide
42718      */
42719 });
42720
42721 Roo.HtmlEditorCore.white = [
42722         'area', 'br', 'img', 'input', 'hr', 'wbr',
42723         
42724        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42725        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42726        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42727        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42728        'table',   'ul',         'xmp', 
42729        
42730        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42731       'thead',   'tr', 
42732      
42733       'dir', 'menu', 'ol', 'ul', 'dl',
42734        
42735       'embed',  'object'
42736 ];
42737
42738
42739 Roo.HtmlEditorCore.black = [
42740     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42741         'applet', // 
42742         'base',   'basefont', 'bgsound', 'blink',  'body', 
42743         'frame',  'frameset', 'head',    'html',   'ilayer', 
42744         'iframe', 'layer',  'link',     'meta',    'object',   
42745         'script', 'style' ,'title',  'xml' // clean later..
42746 ];
42747 Roo.HtmlEditorCore.clean = [
42748     'script', 'style', 'title', 'xml'
42749 ];
42750 Roo.HtmlEditorCore.remove = [
42751     'font'
42752 ];
42753 // attributes..
42754
42755 Roo.HtmlEditorCore.ablack = [
42756     'on'
42757 ];
42758     
42759 Roo.HtmlEditorCore.aclean = [ 
42760     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42761 ];
42762
42763 // protocols..
42764 Roo.HtmlEditorCore.pwhite= [
42765         'http',  'https',  'mailto'
42766 ];
42767
42768 // white listed style attributes.
42769 Roo.HtmlEditorCore.cwhite= [
42770       //  'text-align', /// default is to allow most things..
42771       
42772          
42773 //        'font-size'//??
42774 ];
42775
42776 // black listed style attributes.
42777 Roo.HtmlEditorCore.cblack= [
42778       //  'font-size' -- this can be set by the project 
42779 ];
42780
42781
42782 Roo.HtmlEditorCore.swapCodes   =[ 
42783     [    8211, "--" ], 
42784     [    8212, "--" ], 
42785     [    8216,  "'" ],  
42786     [    8217, "'" ],  
42787     [    8220, '"' ],  
42788     [    8221, '"' ],  
42789     [    8226, "*" ],  
42790     [    8230, "..." ]
42791 ]; 
42792
42793     //<script type="text/javascript">
42794
42795 /*
42796  * Ext JS Library 1.1.1
42797  * Copyright(c) 2006-2007, Ext JS, LLC.
42798  * Licence LGPL
42799  * 
42800  */
42801  
42802  
42803 Roo.form.HtmlEditor = function(config){
42804     
42805     
42806     
42807     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42808     
42809     if (!this.toolbars) {
42810         this.toolbars = [];
42811     }
42812     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42813     
42814     
42815 };
42816
42817 /**
42818  * @class Roo.form.HtmlEditor
42819  * @extends Roo.form.Field
42820  * Provides a lightweight HTML Editor component.
42821  *
42822  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42823  * 
42824  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42825  * supported by this editor.</b><br/><br/>
42826  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42827  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42828  */
42829 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42830     /**
42831      * @cfg {Boolean} clearUp
42832      */
42833     clearUp : true,
42834       /**
42835      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42836      */
42837     toolbars : false,
42838    
42839      /**
42840      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42841      *                        Roo.resizable.
42842      */
42843     resizable : false,
42844      /**
42845      * @cfg {Number} height (in pixels)
42846      */   
42847     height: 300,
42848    /**
42849      * @cfg {Number} width (in pixels)
42850      */   
42851     width: 500,
42852     
42853     /**
42854      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42855      * 
42856      */
42857     stylesheets: false,
42858     
42859     
42860      /**
42861      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
42862      * 
42863      */
42864     cblack: false,
42865     /**
42866      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
42867      * 
42868      */
42869     cwhite: false,
42870     
42871      /**
42872      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
42873      * 
42874      */
42875     black: false,
42876     /**
42877      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
42878      * 
42879      */
42880     white: false,
42881     
42882     // id of frame..
42883     frameId: false,
42884     
42885     // private properties
42886     validationEvent : false,
42887     deferHeight: true,
42888     initialized : false,
42889     activated : false,
42890     
42891     onFocus : Roo.emptyFn,
42892     iframePad:3,
42893     hideMode:'offsets',
42894     
42895     actionMode : 'container', // defaults to hiding it...
42896     
42897     defaultAutoCreate : { // modified by initCompnoent..
42898         tag: "textarea",
42899         style:"width:500px;height:300px;",
42900         autocomplete: "new-password"
42901     },
42902
42903     // private
42904     initComponent : function(){
42905         this.addEvents({
42906             /**
42907              * @event initialize
42908              * Fires when the editor is fully initialized (including the iframe)
42909              * @param {HtmlEditor} this
42910              */
42911             initialize: true,
42912             /**
42913              * @event activate
42914              * Fires when the editor is first receives the focus. Any insertion must wait
42915              * until after this event.
42916              * @param {HtmlEditor} this
42917              */
42918             activate: true,
42919              /**
42920              * @event beforesync
42921              * Fires before the textarea is updated with content from the editor iframe. Return false
42922              * to cancel the sync.
42923              * @param {HtmlEditor} this
42924              * @param {String} html
42925              */
42926             beforesync: true,
42927              /**
42928              * @event beforepush
42929              * Fires before the iframe editor is updated with content from the textarea. Return false
42930              * to cancel the push.
42931              * @param {HtmlEditor} this
42932              * @param {String} html
42933              */
42934             beforepush: true,
42935              /**
42936              * @event sync
42937              * Fires when the textarea is updated with content from the editor iframe.
42938              * @param {HtmlEditor} this
42939              * @param {String} html
42940              */
42941             sync: true,
42942              /**
42943              * @event push
42944              * Fires when the iframe editor is updated with content from the textarea.
42945              * @param {HtmlEditor} this
42946              * @param {String} html
42947              */
42948             push: true,
42949              /**
42950              * @event editmodechange
42951              * Fires when the editor switches edit modes
42952              * @param {HtmlEditor} this
42953              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42954              */
42955             editmodechange: true,
42956             /**
42957              * @event editorevent
42958              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42959              * @param {HtmlEditor} this
42960              */
42961             editorevent: true,
42962             /**
42963              * @event firstfocus
42964              * Fires when on first focus - needed by toolbars..
42965              * @param {HtmlEditor} this
42966              */
42967             firstfocus: true,
42968             /**
42969              * @event autosave
42970              * Auto save the htmlEditor value as a file into Events
42971              * @param {HtmlEditor} this
42972              */
42973             autosave: true,
42974             /**
42975              * @event savedpreview
42976              * preview the saved version of htmlEditor
42977              * @param {HtmlEditor} this
42978              */
42979             savedpreview: true,
42980             
42981             /**
42982             * @event stylesheetsclick
42983             * Fires when press the Sytlesheets button
42984             * @param {Roo.HtmlEditorCore} this
42985             */
42986             stylesheetsclick: true
42987         });
42988         this.defaultAutoCreate =  {
42989             tag: "textarea",
42990             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42991             autocomplete: "new-password"
42992         };
42993     },
42994
42995     /**
42996      * Protected method that will not generally be called directly. It
42997      * is called when the editor creates its toolbar. Override this method if you need to
42998      * add custom toolbar buttons.
42999      * @param {HtmlEditor} editor
43000      */
43001     createToolbar : function(editor){
43002         Roo.log("create toolbars");
43003         if (!editor.toolbars || !editor.toolbars.length) {
43004             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
43005         }
43006         
43007         for (var i =0 ; i < editor.toolbars.length;i++) {
43008             editor.toolbars[i] = Roo.factory(
43009                     typeof(editor.toolbars[i]) == 'string' ?
43010                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
43011                 Roo.form.HtmlEditor);
43012             editor.toolbars[i].init(editor);
43013         }
43014          
43015         
43016     },
43017
43018      
43019     // private
43020     onRender : function(ct, position)
43021     {
43022         var _t = this;
43023         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
43024         
43025         this.wrap = this.el.wrap({
43026             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
43027         });
43028         
43029         this.editorcore.onRender(ct, position);
43030          
43031         if (this.resizable) {
43032             this.resizeEl = new Roo.Resizable(this.wrap, {
43033                 pinned : true,
43034                 wrap: true,
43035                 dynamic : true,
43036                 minHeight : this.height,
43037                 height: this.height,
43038                 handles : this.resizable,
43039                 width: this.width,
43040                 listeners : {
43041                     resize : function(r, w, h) {
43042                         _t.onResize(w,h); // -something
43043                     }
43044                 }
43045             });
43046             
43047         }
43048         this.createToolbar(this);
43049        
43050         
43051         if(!this.width){
43052             this.setSize(this.wrap.getSize());
43053         }
43054         if (this.resizeEl) {
43055             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
43056             // should trigger onReize..
43057         }
43058         
43059         this.keyNav = new Roo.KeyNav(this.el, {
43060             
43061             "tab" : function(e){
43062                 e.preventDefault();
43063                 
43064                 var value = this.getValue();
43065                 
43066                 var start = this.el.dom.selectionStart;
43067                 var end = this.el.dom.selectionEnd;
43068                 
43069                 if(!e.shiftKey){
43070                     
43071                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
43072                     this.el.dom.setSelectionRange(end + 1, end + 1);
43073                     return;
43074                 }
43075                 
43076                 var f = value.substring(0, start).split("\t");
43077                 
43078                 if(f.pop().length != 0){
43079                     return;
43080                 }
43081                 
43082                 this.setValue(f.join("\t") + value.substring(end));
43083                 this.el.dom.setSelectionRange(start - 1, start - 1);
43084                 
43085             },
43086             
43087             "home" : function(e){
43088                 e.preventDefault();
43089                 
43090                 var curr = this.el.dom.selectionStart;
43091                 var lines = this.getValue().split("\n");
43092                 
43093                 if(!lines.length){
43094                     return;
43095                 }
43096                 
43097                 if(e.ctrlKey){
43098                     this.el.dom.setSelectionRange(0, 0);
43099                     return;
43100                 }
43101                 
43102                 var pos = 0;
43103                 
43104                 for (var i = 0; i < lines.length;i++) {
43105                     pos += lines[i].length;
43106                     
43107                     if(i != 0){
43108                         pos += 1;
43109                     }
43110                     
43111                     if(pos < curr){
43112                         continue;
43113                     }
43114                     
43115                     pos -= lines[i].length;
43116                     
43117                     break;
43118                 }
43119                 
43120                 if(!e.shiftKey){
43121                     this.el.dom.setSelectionRange(pos, pos);
43122                     return;
43123                 }
43124                 
43125                 this.el.dom.selectionStart = pos;
43126                 this.el.dom.selectionEnd = curr;
43127             },
43128             
43129             "end" : function(e){
43130                 e.preventDefault();
43131                 
43132                 var curr = this.el.dom.selectionStart;
43133                 var lines = this.getValue().split("\n");
43134                 
43135                 if(!lines.length){
43136                     return;
43137                 }
43138                 
43139                 if(e.ctrlKey){
43140                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
43141                     return;
43142                 }
43143                 
43144                 var pos = 0;
43145                 
43146                 for (var i = 0; i < lines.length;i++) {
43147                     
43148                     pos += lines[i].length;
43149                     
43150                     if(i != 0){
43151                         pos += 1;
43152                     }
43153                     
43154                     if(pos < curr){
43155                         continue;
43156                     }
43157                     
43158                     break;
43159                 }
43160                 
43161                 if(!e.shiftKey){
43162                     this.el.dom.setSelectionRange(pos, pos);
43163                     return;
43164                 }
43165                 
43166                 this.el.dom.selectionStart = curr;
43167                 this.el.dom.selectionEnd = pos;
43168             },
43169
43170             scope : this,
43171
43172             doRelay : function(foo, bar, hname){
43173                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43174             },
43175
43176             forceKeyDown: true
43177         });
43178         
43179 //        if(this.autosave && this.w){
43180 //            this.autoSaveFn = setInterval(this.autosave, 1000);
43181 //        }
43182     },
43183
43184     // private
43185     onResize : function(w, h)
43186     {
43187         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
43188         var ew = false;
43189         var eh = false;
43190         
43191         if(this.el ){
43192             if(typeof w == 'number'){
43193                 var aw = w - this.wrap.getFrameWidth('lr');
43194                 this.el.setWidth(this.adjustWidth('textarea', aw));
43195                 ew = aw;
43196             }
43197             if(typeof h == 'number'){
43198                 var tbh = 0;
43199                 for (var i =0; i < this.toolbars.length;i++) {
43200                     // fixme - ask toolbars for heights?
43201                     tbh += this.toolbars[i].tb.el.getHeight();
43202                     if (this.toolbars[i].footer) {
43203                         tbh += this.toolbars[i].footer.el.getHeight();
43204                     }
43205                 }
43206                 
43207                 
43208                 
43209                 
43210                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
43211                 ah -= 5; // knock a few pixes off for look..
43212 //                Roo.log(ah);
43213                 this.el.setHeight(this.adjustWidth('textarea', ah));
43214                 var eh = ah;
43215             }
43216         }
43217         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
43218         this.editorcore.onResize(ew,eh);
43219         
43220     },
43221
43222     /**
43223      * Toggles the editor between standard and source edit mode.
43224      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43225      */
43226     toggleSourceEdit : function(sourceEditMode)
43227     {
43228         this.editorcore.toggleSourceEdit(sourceEditMode);
43229         
43230         if(this.editorcore.sourceEditMode){
43231             Roo.log('editor - showing textarea');
43232             
43233 //            Roo.log('in');
43234 //            Roo.log(this.syncValue());
43235             this.editorcore.syncValue();
43236             this.el.removeClass('x-hidden');
43237             this.el.dom.removeAttribute('tabIndex');
43238             this.el.focus();
43239             
43240             for (var i = 0; i < this.toolbars.length; i++) {
43241                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43242                     this.toolbars[i].tb.hide();
43243                     this.toolbars[i].footer.hide();
43244                 }
43245             }
43246             
43247         }else{
43248             Roo.log('editor - hiding textarea');
43249 //            Roo.log('out')
43250 //            Roo.log(this.pushValue()); 
43251             this.editorcore.pushValue();
43252             
43253             this.el.addClass('x-hidden');
43254             this.el.dom.setAttribute('tabIndex', -1);
43255             
43256             for (var i = 0; i < this.toolbars.length; i++) {
43257                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43258                     this.toolbars[i].tb.show();
43259                     this.toolbars[i].footer.show();
43260                 }
43261             }
43262             
43263             //this.deferFocus();
43264         }
43265         
43266         this.setSize(this.wrap.getSize());
43267         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
43268         
43269         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
43270     },
43271  
43272     // private (for BoxComponent)
43273     adjustSize : Roo.BoxComponent.prototype.adjustSize,
43274
43275     // private (for BoxComponent)
43276     getResizeEl : function(){
43277         return this.wrap;
43278     },
43279
43280     // private (for BoxComponent)
43281     getPositionEl : function(){
43282         return this.wrap;
43283     },
43284
43285     // private
43286     initEvents : function(){
43287         this.originalValue = this.getValue();
43288     },
43289
43290     /**
43291      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43292      * @method
43293      */
43294     markInvalid : Roo.emptyFn,
43295     /**
43296      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43297      * @method
43298      */
43299     clearInvalid : Roo.emptyFn,
43300
43301     setValue : function(v){
43302         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
43303         this.editorcore.pushValue();
43304     },
43305
43306      
43307     // private
43308     deferFocus : function(){
43309         this.focus.defer(10, this);
43310     },
43311
43312     // doc'ed in Field
43313     focus : function(){
43314         this.editorcore.focus();
43315         
43316     },
43317       
43318
43319     // private
43320     onDestroy : function(){
43321         
43322         
43323         
43324         if(this.rendered){
43325             
43326             for (var i =0; i < this.toolbars.length;i++) {
43327                 // fixme - ask toolbars for heights?
43328                 this.toolbars[i].onDestroy();
43329             }
43330             
43331             this.wrap.dom.innerHTML = '';
43332             this.wrap.remove();
43333         }
43334     },
43335
43336     // private
43337     onFirstFocus : function(){
43338         //Roo.log("onFirstFocus");
43339         this.editorcore.onFirstFocus();
43340          for (var i =0; i < this.toolbars.length;i++) {
43341             this.toolbars[i].onFirstFocus();
43342         }
43343         
43344     },
43345     
43346     // private
43347     syncValue : function()
43348     {
43349         this.editorcore.syncValue();
43350     },
43351     
43352     pushValue : function()
43353     {
43354         this.editorcore.pushValue();
43355     },
43356     
43357     setStylesheets : function(stylesheets)
43358     {
43359         this.editorcore.setStylesheets(stylesheets);
43360     },
43361     
43362     removeStylesheets : function()
43363     {
43364         this.editorcore.removeStylesheets();
43365     }
43366      
43367     
43368     // hide stuff that is not compatible
43369     /**
43370      * @event blur
43371      * @hide
43372      */
43373     /**
43374      * @event change
43375      * @hide
43376      */
43377     /**
43378      * @event focus
43379      * @hide
43380      */
43381     /**
43382      * @event specialkey
43383      * @hide
43384      */
43385     /**
43386      * @cfg {String} fieldClass @hide
43387      */
43388     /**
43389      * @cfg {String} focusClass @hide
43390      */
43391     /**
43392      * @cfg {String} autoCreate @hide
43393      */
43394     /**
43395      * @cfg {String} inputType @hide
43396      */
43397     /**
43398      * @cfg {String} invalidClass @hide
43399      */
43400     /**
43401      * @cfg {String} invalidText @hide
43402      */
43403     /**
43404      * @cfg {String} msgFx @hide
43405      */
43406     /**
43407      * @cfg {String} validateOnBlur @hide
43408      */
43409 });
43410  
43411     // <script type="text/javascript">
43412 /*
43413  * Based on
43414  * Ext JS Library 1.1.1
43415  * Copyright(c) 2006-2007, Ext JS, LLC.
43416  *  
43417  
43418  */
43419
43420 /**
43421  * @class Roo.form.HtmlEditorToolbar1
43422  * Basic Toolbar
43423  * 
43424  * Usage:
43425  *
43426  new Roo.form.HtmlEditor({
43427     ....
43428     toolbars : [
43429         new Roo.form.HtmlEditorToolbar1({
43430             disable : { fonts: 1 , format: 1, ..., ... , ...],
43431             btns : [ .... ]
43432         })
43433     }
43434      
43435  * 
43436  * @cfg {Object} disable List of elements to disable..
43437  * @cfg {Array} btns List of additional buttons.
43438  * 
43439  * 
43440  * NEEDS Extra CSS? 
43441  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
43442  */
43443  
43444 Roo.form.HtmlEditor.ToolbarStandard = function(config)
43445 {
43446     
43447     Roo.apply(this, config);
43448     
43449     // default disabled, based on 'good practice'..
43450     this.disable = this.disable || {};
43451     Roo.applyIf(this.disable, {
43452         fontSize : true,
43453         colors : true,
43454         specialElements : true
43455     });
43456     
43457     
43458     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43459     // dont call parent... till later.
43460 }
43461
43462 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
43463     
43464     tb: false,
43465     
43466     rendered: false,
43467     
43468     editor : false,
43469     editorcore : false,
43470     /**
43471      * @cfg {Object} disable  List of toolbar elements to disable
43472          
43473      */
43474     disable : false,
43475     
43476     
43477      /**
43478      * @cfg {String} createLinkText The default text for the create link prompt
43479      */
43480     createLinkText : 'Please enter the URL for the link:',
43481     /**
43482      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
43483      */
43484     defaultLinkValue : 'http:/'+'/',
43485    
43486     
43487       /**
43488      * @cfg {Array} fontFamilies An array of available font families
43489      */
43490     fontFamilies : [
43491         'Arial',
43492         'Courier New',
43493         'Tahoma',
43494         'Times New Roman',
43495         'Verdana'
43496     ],
43497     
43498     specialChars : [
43499            "&#169;",
43500           "&#174;",     
43501           "&#8482;",    
43502           "&#163;" ,    
43503          // "&#8212;",    
43504           "&#8230;",    
43505           "&#247;" ,    
43506         //  "&#225;" ,     ?? a acute?
43507            "&#8364;"    , //Euro
43508        //   "&#8220;"    ,
43509         //  "&#8221;"    ,
43510         //  "&#8226;"    ,
43511           "&#176;"  //   , // degrees
43512
43513          // "&#233;"     , // e ecute
43514          // "&#250;"     , // u ecute?
43515     ],
43516     
43517     specialElements : [
43518         {
43519             text: "Insert Table",
43520             xtype: 'MenuItem',
43521             xns : Roo.Menu,
43522             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
43523                 
43524         },
43525         {    
43526             text: "Insert Image",
43527             xtype: 'MenuItem',
43528             xns : Roo.Menu,
43529             ihtml : '<img src="about:blank"/>'
43530             
43531         }
43532         
43533          
43534     ],
43535     
43536     
43537     inputElements : [ 
43538             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
43539             "input:submit", "input:button", "select", "textarea", "label" ],
43540     formats : [
43541         ["p"] ,  
43542         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
43543         ["pre"],[ "code"], 
43544         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
43545         ['div'],['span']
43546     ],
43547     
43548     cleanStyles : [
43549         "font-size"
43550     ],
43551      /**
43552      * @cfg {String} defaultFont default font to use.
43553      */
43554     defaultFont: 'tahoma',
43555    
43556     fontSelect : false,
43557     
43558     
43559     formatCombo : false,
43560     
43561     init : function(editor)
43562     {
43563         this.editor = editor;
43564         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43565         var editorcore = this.editorcore;
43566         
43567         var _t = this;
43568         
43569         var fid = editorcore.frameId;
43570         var etb = this;
43571         function btn(id, toggle, handler){
43572             var xid = fid + '-'+ id ;
43573             return {
43574                 id : xid,
43575                 cmd : id,
43576                 cls : 'x-btn-icon x-edit-'+id,
43577                 enableToggle:toggle !== false,
43578                 scope: _t, // was editor...
43579                 handler:handler||_t.relayBtnCmd,
43580                 clickEvent:'mousedown',
43581                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43582                 tabIndex:-1
43583             };
43584         }
43585         
43586         
43587         
43588         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43589         this.tb = tb;
43590          // stop form submits
43591         tb.el.on('click', function(e){
43592             e.preventDefault(); // what does this do?
43593         });
43594
43595         if(!this.disable.font) { // && !Roo.isSafari){
43596             /* why no safari for fonts 
43597             editor.fontSelect = tb.el.createChild({
43598                 tag:'select',
43599                 tabIndex: -1,
43600                 cls:'x-font-select',
43601                 html: this.createFontOptions()
43602             });
43603             
43604             editor.fontSelect.on('change', function(){
43605                 var font = editor.fontSelect.dom.value;
43606                 editor.relayCmd('fontname', font);
43607                 editor.deferFocus();
43608             }, editor);
43609             
43610             tb.add(
43611                 editor.fontSelect.dom,
43612                 '-'
43613             );
43614             */
43615             
43616         };
43617         if(!this.disable.formats){
43618             this.formatCombo = new Roo.form.ComboBox({
43619                 store: new Roo.data.SimpleStore({
43620                     id : 'tag',
43621                     fields: ['tag'],
43622                     data : this.formats // from states.js
43623                 }),
43624                 blockFocus : true,
43625                 name : '',
43626                 //autoCreate : {tag: "div",  size: "20"},
43627                 displayField:'tag',
43628                 typeAhead: false,
43629                 mode: 'local',
43630                 editable : false,
43631                 triggerAction: 'all',
43632                 emptyText:'Add tag',
43633                 selectOnFocus:true,
43634                 width:135,
43635                 listeners : {
43636                     'select': function(c, r, i) {
43637                         editorcore.insertTag(r.get('tag'));
43638                         editor.focus();
43639                     }
43640                 }
43641
43642             });
43643             tb.addField(this.formatCombo);
43644             
43645         }
43646         
43647         if(!this.disable.format){
43648             tb.add(
43649                 btn('bold'),
43650                 btn('italic'),
43651                 btn('underline')
43652             );
43653         };
43654         if(!this.disable.fontSize){
43655             tb.add(
43656                 '-',
43657                 
43658                 
43659                 btn('increasefontsize', false, editorcore.adjustFont),
43660                 btn('decreasefontsize', false, editorcore.adjustFont)
43661             );
43662         };
43663         
43664         
43665         if(!this.disable.colors){
43666             tb.add(
43667                 '-', {
43668                     id:editorcore.frameId +'-forecolor',
43669                     cls:'x-btn-icon x-edit-forecolor',
43670                     clickEvent:'mousedown',
43671                     tooltip: this.buttonTips['forecolor'] || undefined,
43672                     tabIndex:-1,
43673                     menu : new Roo.menu.ColorMenu({
43674                         allowReselect: true,
43675                         focus: Roo.emptyFn,
43676                         value:'000000',
43677                         plain:true,
43678                         selectHandler: function(cp, color){
43679                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43680                             editor.deferFocus();
43681                         },
43682                         scope: editorcore,
43683                         clickEvent:'mousedown'
43684                     })
43685                 }, {
43686                     id:editorcore.frameId +'backcolor',
43687                     cls:'x-btn-icon x-edit-backcolor',
43688                     clickEvent:'mousedown',
43689                     tooltip: this.buttonTips['backcolor'] || undefined,
43690                     tabIndex:-1,
43691                     menu : new Roo.menu.ColorMenu({
43692                         focus: Roo.emptyFn,
43693                         value:'FFFFFF',
43694                         plain:true,
43695                         allowReselect: true,
43696                         selectHandler: function(cp, color){
43697                             if(Roo.isGecko){
43698                                 editorcore.execCmd('useCSS', false);
43699                                 editorcore.execCmd('hilitecolor', color);
43700                                 editorcore.execCmd('useCSS', true);
43701                                 editor.deferFocus();
43702                             }else{
43703                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43704                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43705                                 editor.deferFocus();
43706                             }
43707                         },
43708                         scope:editorcore,
43709                         clickEvent:'mousedown'
43710                     })
43711                 }
43712             );
43713         };
43714         // now add all the items...
43715         
43716
43717         if(!this.disable.alignments){
43718             tb.add(
43719                 '-',
43720                 btn('justifyleft'),
43721                 btn('justifycenter'),
43722                 btn('justifyright')
43723             );
43724         };
43725
43726         //if(!Roo.isSafari){
43727             if(!this.disable.links){
43728                 tb.add(
43729                     '-',
43730                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43731                 );
43732             };
43733
43734             if(!this.disable.lists){
43735                 tb.add(
43736                     '-',
43737                     btn('insertorderedlist'),
43738                     btn('insertunorderedlist')
43739                 );
43740             }
43741             if(!this.disable.sourceEdit){
43742                 tb.add(
43743                     '-',
43744                     btn('sourceedit', true, function(btn){
43745                         this.toggleSourceEdit(btn.pressed);
43746                     })
43747                 );
43748             }
43749         //}
43750         
43751         var smenu = { };
43752         // special menu.. - needs to be tidied up..
43753         if (!this.disable.special) {
43754             smenu = {
43755                 text: "&#169;",
43756                 cls: 'x-edit-none',
43757                 
43758                 menu : {
43759                     items : []
43760                 }
43761             };
43762             for (var i =0; i < this.specialChars.length; i++) {
43763                 smenu.menu.items.push({
43764                     
43765                     html: this.specialChars[i],
43766                     handler: function(a,b) {
43767                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43768                         //editor.insertAtCursor(a.html);
43769                         
43770                     },
43771                     tabIndex:-1
43772                 });
43773             }
43774             
43775             
43776             tb.add(smenu);
43777             
43778             
43779         }
43780         
43781         var cmenu = { };
43782         if (!this.disable.cleanStyles) {
43783             cmenu = {
43784                 cls: 'x-btn-icon x-btn-clear',
43785                 
43786                 menu : {
43787                     items : []
43788                 }
43789             };
43790             for (var i =0; i < this.cleanStyles.length; i++) {
43791                 cmenu.menu.items.push({
43792                     actiontype : this.cleanStyles[i],
43793                     html: 'Remove ' + this.cleanStyles[i],
43794                     handler: function(a,b) {
43795 //                        Roo.log(a);
43796 //                        Roo.log(b);
43797                         var c = Roo.get(editorcore.doc.body);
43798                         c.select('[style]').each(function(s) {
43799                             s.dom.style.removeProperty(a.actiontype);
43800                         });
43801                         editorcore.syncValue();
43802                     },
43803                     tabIndex:-1
43804                 });
43805             }
43806              cmenu.menu.items.push({
43807                 actiontype : 'tablewidths',
43808                 html: 'Remove Table Widths',
43809                 handler: function(a,b) {
43810                     editorcore.cleanTableWidths();
43811                     editorcore.syncValue();
43812                 },
43813                 tabIndex:-1
43814             });
43815             cmenu.menu.items.push({
43816                 actiontype : 'word',
43817                 html: 'Remove MS Word Formating',
43818                 handler: function(a,b) {
43819                     editorcore.cleanWord();
43820                     editorcore.syncValue();
43821                 },
43822                 tabIndex:-1
43823             });
43824             
43825             cmenu.menu.items.push({
43826                 actiontype : 'all',
43827                 html: 'Remove All Styles',
43828                 handler: function(a,b) {
43829                     
43830                     var c = Roo.get(editorcore.doc.body);
43831                     c.select('[style]').each(function(s) {
43832                         s.dom.removeAttribute('style');
43833                     });
43834                     editorcore.syncValue();
43835                 },
43836                 tabIndex:-1
43837             });
43838             
43839             cmenu.menu.items.push({
43840                 actiontype : 'all',
43841                 html: 'Remove All CSS Classes',
43842                 handler: function(a,b) {
43843                     
43844                     var c = Roo.get(editorcore.doc.body);
43845                     c.select('[class]').each(function(s) {
43846                         s.dom.className = '';
43847                     });
43848                     editorcore.syncValue();
43849                 },
43850                 tabIndex:-1
43851             });
43852             
43853              cmenu.menu.items.push({
43854                 actiontype : 'tidy',
43855                 html: 'Tidy HTML Source',
43856                 handler: function(a,b) {
43857                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43858                     editorcore.syncValue();
43859                 },
43860                 tabIndex:-1
43861             });
43862             
43863             
43864             tb.add(cmenu);
43865         }
43866          
43867         if (!this.disable.specialElements) {
43868             var semenu = {
43869                 text: "Other;",
43870                 cls: 'x-edit-none',
43871                 menu : {
43872                     items : []
43873                 }
43874             };
43875             for (var i =0; i < this.specialElements.length; i++) {
43876                 semenu.menu.items.push(
43877                     Roo.apply({ 
43878                         handler: function(a,b) {
43879                             editor.insertAtCursor(this.ihtml);
43880                         }
43881                     }, this.specialElements[i])
43882                 );
43883                     
43884             }
43885             
43886             tb.add(semenu);
43887             
43888             
43889         }
43890          
43891         
43892         if (this.btns) {
43893             for(var i =0; i< this.btns.length;i++) {
43894                 var b = Roo.factory(this.btns[i],Roo.form);
43895                 b.cls =  'x-edit-none';
43896                 
43897                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
43898                     b.cls += ' x-init-enable';
43899                 }
43900                 
43901                 b.scope = editorcore;
43902                 tb.add(b);
43903             }
43904         
43905         }
43906         
43907         
43908         
43909         // disable everything...
43910         
43911         this.tb.items.each(function(item){
43912             
43913            if(
43914                 item.id != editorcore.frameId+ '-sourceedit' && 
43915                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
43916             ){
43917                 
43918                 item.disable();
43919             }
43920         });
43921         this.rendered = true;
43922         
43923         // the all the btns;
43924         editor.on('editorevent', this.updateToolbar, this);
43925         // other toolbars need to implement this..
43926         //editor.on('editmodechange', this.updateToolbar, this);
43927     },
43928     
43929     
43930     relayBtnCmd : function(btn) {
43931         this.editorcore.relayCmd(btn.cmd);
43932     },
43933     // private used internally
43934     createLink : function(){
43935         Roo.log("create link?");
43936         var url = prompt(this.createLinkText, this.defaultLinkValue);
43937         if(url && url != 'http:/'+'/'){
43938             this.editorcore.relayCmd('createlink', url);
43939         }
43940     },
43941
43942     
43943     /**
43944      * Protected method that will not generally be called directly. It triggers
43945      * a toolbar update by reading the markup state of the current selection in the editor.
43946      */
43947     updateToolbar: function(){
43948
43949         if(!this.editorcore.activated){
43950             this.editor.onFirstFocus();
43951             return;
43952         }
43953
43954         var btns = this.tb.items.map, 
43955             doc = this.editorcore.doc,
43956             frameId = this.editorcore.frameId;
43957
43958         if(!this.disable.font && !Roo.isSafari){
43959             /*
43960             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43961             if(name != this.fontSelect.dom.value){
43962                 this.fontSelect.dom.value = name;
43963             }
43964             */
43965         }
43966         if(!this.disable.format){
43967             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43968             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43969             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43970         }
43971         if(!this.disable.alignments){
43972             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43973             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43974             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43975         }
43976         if(!Roo.isSafari && !this.disable.lists){
43977             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43978             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43979         }
43980         
43981         var ans = this.editorcore.getAllAncestors();
43982         if (this.formatCombo) {
43983             
43984             
43985             var store = this.formatCombo.store;
43986             this.formatCombo.setValue("");
43987             for (var i =0; i < ans.length;i++) {
43988                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43989                     // select it..
43990                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43991                     break;
43992                 }
43993             }
43994         }
43995         
43996         
43997         
43998         // hides menus... - so this cant be on a menu...
43999         Roo.menu.MenuMgr.hideAll();
44000
44001         //this.editorsyncValue();
44002     },
44003    
44004     
44005     createFontOptions : function(){
44006         var buf = [], fs = this.fontFamilies, ff, lc;
44007         
44008         
44009         
44010         for(var i = 0, len = fs.length; i< len; i++){
44011             ff = fs[i];
44012             lc = ff.toLowerCase();
44013             buf.push(
44014                 '<option value="',lc,'" style="font-family:',ff,';"',
44015                     (this.defaultFont == lc ? ' selected="true">' : '>'),
44016                     ff,
44017                 '</option>'
44018             );
44019         }
44020         return buf.join('');
44021     },
44022     
44023     toggleSourceEdit : function(sourceEditMode){
44024         
44025         Roo.log("toolbar toogle");
44026         if(sourceEditMode === undefined){
44027             sourceEditMode = !this.sourceEditMode;
44028         }
44029         this.sourceEditMode = sourceEditMode === true;
44030         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
44031         // just toggle the button?
44032         if(btn.pressed !== this.sourceEditMode){
44033             btn.toggle(this.sourceEditMode);
44034             return;
44035         }
44036         
44037         if(sourceEditMode){
44038             Roo.log("disabling buttons");
44039             this.tb.items.each(function(item){
44040                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
44041                     item.disable();
44042                 }
44043             });
44044           
44045         }else{
44046             Roo.log("enabling buttons");
44047             if(this.editorcore.initialized){
44048                 this.tb.items.each(function(item){
44049                     item.enable();
44050                 });
44051             }
44052             
44053         }
44054         Roo.log("calling toggole on editor");
44055         // tell the editor that it's been pressed..
44056         this.editor.toggleSourceEdit(sourceEditMode);
44057        
44058     },
44059      /**
44060      * Object collection of toolbar tooltips for the buttons in the editor. The key
44061      * is the command id associated with that button and the value is a valid QuickTips object.
44062      * For example:
44063 <pre><code>
44064 {
44065     bold : {
44066         title: 'Bold (Ctrl+B)',
44067         text: 'Make the selected text bold.',
44068         cls: 'x-html-editor-tip'
44069     },
44070     italic : {
44071         title: 'Italic (Ctrl+I)',
44072         text: 'Make the selected text italic.',
44073         cls: 'x-html-editor-tip'
44074     },
44075     ...
44076 </code></pre>
44077     * @type Object
44078      */
44079     buttonTips : {
44080         bold : {
44081             title: 'Bold (Ctrl+B)',
44082             text: 'Make the selected text bold.',
44083             cls: 'x-html-editor-tip'
44084         },
44085         italic : {
44086             title: 'Italic (Ctrl+I)',
44087             text: 'Make the selected text italic.',
44088             cls: 'x-html-editor-tip'
44089         },
44090         underline : {
44091             title: 'Underline (Ctrl+U)',
44092             text: 'Underline the selected text.',
44093             cls: 'x-html-editor-tip'
44094         },
44095         increasefontsize : {
44096             title: 'Grow Text',
44097             text: 'Increase the font size.',
44098             cls: 'x-html-editor-tip'
44099         },
44100         decreasefontsize : {
44101             title: 'Shrink Text',
44102             text: 'Decrease the font size.',
44103             cls: 'x-html-editor-tip'
44104         },
44105         backcolor : {
44106             title: 'Text Highlight Color',
44107             text: 'Change the background color of the selected text.',
44108             cls: 'x-html-editor-tip'
44109         },
44110         forecolor : {
44111             title: 'Font Color',
44112             text: 'Change the color of the selected text.',
44113             cls: 'x-html-editor-tip'
44114         },
44115         justifyleft : {
44116             title: 'Align Text Left',
44117             text: 'Align text to the left.',
44118             cls: 'x-html-editor-tip'
44119         },
44120         justifycenter : {
44121             title: 'Center Text',
44122             text: 'Center text in the editor.',
44123             cls: 'x-html-editor-tip'
44124         },
44125         justifyright : {
44126             title: 'Align Text Right',
44127             text: 'Align text to the right.',
44128             cls: 'x-html-editor-tip'
44129         },
44130         insertunorderedlist : {
44131             title: 'Bullet List',
44132             text: 'Start a bulleted list.',
44133             cls: 'x-html-editor-tip'
44134         },
44135         insertorderedlist : {
44136             title: 'Numbered List',
44137             text: 'Start a numbered list.',
44138             cls: 'x-html-editor-tip'
44139         },
44140         createlink : {
44141             title: 'Hyperlink',
44142             text: 'Make the selected text a hyperlink.',
44143             cls: 'x-html-editor-tip'
44144         },
44145         sourceedit : {
44146             title: 'Source Edit',
44147             text: 'Switch to source editing mode.',
44148             cls: 'x-html-editor-tip'
44149         }
44150     },
44151     // private
44152     onDestroy : function(){
44153         if(this.rendered){
44154             
44155             this.tb.items.each(function(item){
44156                 if(item.menu){
44157                     item.menu.removeAll();
44158                     if(item.menu.el){
44159                         item.menu.el.destroy();
44160                     }
44161                 }
44162                 item.destroy();
44163             });
44164              
44165         }
44166     },
44167     onFirstFocus: function() {
44168         this.tb.items.each(function(item){
44169            item.enable();
44170         });
44171     }
44172 });
44173
44174
44175
44176
44177 // <script type="text/javascript">
44178 /*
44179  * Based on
44180  * Ext JS Library 1.1.1
44181  * Copyright(c) 2006-2007, Ext JS, LLC.
44182  *  
44183  
44184  */
44185
44186  
44187 /**
44188  * @class Roo.form.HtmlEditor.ToolbarContext
44189  * Context Toolbar
44190  * 
44191  * Usage:
44192  *
44193  new Roo.form.HtmlEditor({
44194     ....
44195     toolbars : [
44196         { xtype: 'ToolbarStandard', styles : {} }
44197         { xtype: 'ToolbarContext', disable : {} }
44198     ]
44199 })
44200
44201      
44202  * 
44203  * @config : {Object} disable List of elements to disable.. (not done yet.)
44204  * @config : {Object} styles  Map of styles available.
44205  * 
44206  */
44207
44208 Roo.form.HtmlEditor.ToolbarContext = function(config)
44209 {
44210     
44211     Roo.apply(this, config);
44212     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
44213     // dont call parent... till later.
44214     this.styles = this.styles || {};
44215 }
44216
44217  
44218
44219 Roo.form.HtmlEditor.ToolbarContext.types = {
44220     'IMG' : {
44221         width : {
44222             title: "Width",
44223             width: 40
44224         },
44225         height:  {
44226             title: "Height",
44227             width: 40
44228         },
44229         align: {
44230             title: "Align",
44231             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
44232             width : 80
44233             
44234         },
44235         border: {
44236             title: "Border",
44237             width: 40
44238         },
44239         alt: {
44240             title: "Alt",
44241             width: 120
44242         },
44243         src : {
44244             title: "Src",
44245             width: 220
44246         }
44247         
44248     },
44249     'A' : {
44250         name : {
44251             title: "Name",
44252             width: 50
44253         },
44254         target:  {
44255             title: "Target",
44256             width: 120
44257         },
44258         href:  {
44259             title: "Href",
44260             width: 220
44261         } // border?
44262         
44263     },
44264     'TABLE' : {
44265         rows : {
44266             title: "Rows",
44267             width: 20
44268         },
44269         cols : {
44270             title: "Cols",
44271             width: 20
44272         },
44273         width : {
44274             title: "Width",
44275             width: 40
44276         },
44277         height : {
44278             title: "Height",
44279             width: 40
44280         },
44281         border : {
44282             title: "Border",
44283             width: 20
44284         }
44285     },
44286     'TD' : {
44287         width : {
44288             title: "Width",
44289             width: 40
44290         },
44291         height : {
44292             title: "Height",
44293             width: 40
44294         },   
44295         align: {
44296             title: "Align",
44297             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
44298             width: 80
44299         },
44300         valign: {
44301             title: "Valign",
44302             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
44303             width: 80
44304         },
44305         colspan: {
44306             title: "Colspan",
44307             width: 20
44308             
44309         },
44310          'font-family'  : {
44311             title : "Font",
44312             style : 'fontFamily',
44313             displayField: 'display',
44314             optname : 'font-family',
44315             width: 140
44316         }
44317     },
44318     'INPUT' : {
44319         name : {
44320             title: "name",
44321             width: 120
44322         },
44323         value : {
44324             title: "Value",
44325             width: 120
44326         },
44327         width : {
44328             title: "Width",
44329             width: 40
44330         }
44331     },
44332     'LABEL' : {
44333         'for' : {
44334             title: "For",
44335             width: 120
44336         }
44337     },
44338     'TEXTAREA' : {
44339           name : {
44340             title: "name",
44341             width: 120
44342         },
44343         rows : {
44344             title: "Rows",
44345             width: 20
44346         },
44347         cols : {
44348             title: "Cols",
44349             width: 20
44350         }
44351     },
44352     'SELECT' : {
44353         name : {
44354             title: "name",
44355             width: 120
44356         },
44357         selectoptions : {
44358             title: "Options",
44359             width: 200
44360         }
44361     },
44362     
44363     // should we really allow this??
44364     // should this just be 
44365     'BODY' : {
44366         title : {
44367             title: "Title",
44368             width: 200,
44369             disabled : true
44370         }
44371     },
44372     'SPAN' : {
44373         'font-family'  : {
44374             title : "Font",
44375             style : 'fontFamily',
44376             displayField: 'display',
44377             optname : 'font-family',
44378             width: 140
44379         }
44380     },
44381     'DIV' : {
44382         'font-family'  : {
44383             title : "Font",
44384             style : 'fontFamily',
44385             displayField: 'display',
44386             optname : 'font-family',
44387             width: 140
44388         }
44389     },
44390      'P' : {
44391         'font-family'  : {
44392             title : "Font",
44393             style : 'fontFamily',
44394             displayField: 'display',
44395             optname : 'font-family',
44396             width: 140
44397         }
44398     },
44399     
44400     '*' : {
44401         // empty..
44402     }
44403
44404 };
44405
44406 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
44407 Roo.form.HtmlEditor.ToolbarContext.stores = false;
44408
44409 Roo.form.HtmlEditor.ToolbarContext.options = {
44410         'font-family'  : [ 
44411                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
44412                 [ 'Courier New', 'Courier New'],
44413                 [ 'Tahoma', 'Tahoma'],
44414                 [ 'Times New Roman,serif', 'Times'],
44415                 [ 'Verdana','Verdana' ]
44416         ]
44417 };
44418
44419 // fixme - these need to be configurable..
44420  
44421
44422 Roo.form.HtmlEditor.ToolbarContext.types
44423
44424
44425 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
44426     
44427     tb: false,
44428     
44429     rendered: false,
44430     
44431     editor : false,
44432     editorcore : false,
44433     /**
44434      * @cfg {Object} disable  List of toolbar elements to disable
44435          
44436      */
44437     disable : false,
44438     /**
44439      * @cfg {Object} styles List of styles 
44440      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
44441      *
44442      * These must be defined in the page, so they get rendered correctly..
44443      * .headline { }
44444      * TD.underline { }
44445      * 
44446      */
44447     styles : false,
44448     
44449     options: false,
44450     
44451     toolbars : false,
44452     
44453     init : function(editor)
44454     {
44455         this.editor = editor;
44456         this.editorcore = editor.editorcore ? editor.editorcore : editor;
44457         var editorcore = this.editorcore;
44458         
44459         var fid = editorcore.frameId;
44460         var etb = this;
44461         function btn(id, toggle, handler){
44462             var xid = fid + '-'+ id ;
44463             return {
44464                 id : xid,
44465                 cmd : id,
44466                 cls : 'x-btn-icon x-edit-'+id,
44467                 enableToggle:toggle !== false,
44468                 scope: editorcore, // was editor...
44469                 handler:handler||editorcore.relayBtnCmd,
44470                 clickEvent:'mousedown',
44471                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
44472                 tabIndex:-1
44473             };
44474         }
44475         // create a new element.
44476         var wdiv = editor.wrap.createChild({
44477                 tag: 'div'
44478             }, editor.wrap.dom.firstChild.nextSibling, true);
44479         
44480         // can we do this more than once??
44481         
44482          // stop form submits
44483       
44484  
44485         // disable everything...
44486         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44487         this.toolbars = {};
44488            
44489         for (var i in  ty) {
44490           
44491             this.toolbars[i] = this.buildToolbar(ty[i],i);
44492         }
44493         this.tb = this.toolbars.BODY;
44494         this.tb.el.show();
44495         this.buildFooter();
44496         this.footer.show();
44497         editor.on('hide', function( ) { this.footer.hide() }, this);
44498         editor.on('show', function( ) { this.footer.show() }, this);
44499         
44500          
44501         this.rendered = true;
44502         
44503         // the all the btns;
44504         editor.on('editorevent', this.updateToolbar, this);
44505         // other toolbars need to implement this..
44506         //editor.on('editmodechange', this.updateToolbar, this);
44507     },
44508     
44509     
44510     
44511     /**
44512      * Protected method that will not generally be called directly. It triggers
44513      * a toolbar update by reading the markup state of the current selection in the editor.
44514      *
44515      * Note you can force an update by calling on('editorevent', scope, false)
44516      */
44517     updateToolbar: function(editor,ev,sel){
44518
44519         //Roo.log(ev);
44520         // capture mouse up - this is handy for selecting images..
44521         // perhaps should go somewhere else...
44522         if(!this.editorcore.activated){
44523              this.editor.onFirstFocus();
44524             return;
44525         }
44526         
44527         
44528         
44529         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
44530         // selectNode - might want to handle IE?
44531         if (ev &&
44532             (ev.type == 'mouseup' || ev.type == 'click' ) &&
44533             ev.target && ev.target.tagName == 'IMG') {
44534             // they have click on an image...
44535             // let's see if we can change the selection...
44536             sel = ev.target;
44537          
44538               var nodeRange = sel.ownerDocument.createRange();
44539             try {
44540                 nodeRange.selectNode(sel);
44541             } catch (e) {
44542                 nodeRange.selectNodeContents(sel);
44543             }
44544             //nodeRange.collapse(true);
44545             var s = this.editorcore.win.getSelection();
44546             s.removeAllRanges();
44547             s.addRange(nodeRange);
44548         }  
44549         
44550       
44551         var updateFooter = sel ? false : true;
44552         
44553         
44554         var ans = this.editorcore.getAllAncestors();
44555         
44556         // pick
44557         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44558         
44559         if (!sel) { 
44560             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
44561             sel = sel ? sel : this.editorcore.doc.body;
44562             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
44563             
44564         }
44565         // pick a menu that exists..
44566         var tn = sel.tagName.toUpperCase();
44567         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
44568         
44569         tn = sel.tagName.toUpperCase();
44570         
44571         var lastSel = this.tb.selectedNode
44572         
44573         this.tb.selectedNode = sel;
44574         
44575         // if current menu does not match..
44576         
44577         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
44578                 
44579             this.tb.el.hide();
44580             ///console.log("show: " + tn);
44581             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
44582             this.tb.el.show();
44583             // update name
44584             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
44585             
44586             
44587             // update attributes
44588             if (this.tb.fields) {
44589                 this.tb.fields.each(function(e) {
44590                     if (e.stylename) {
44591                         e.setValue(sel.style[e.stylename]);
44592                         return;
44593                     } 
44594                    e.setValue(sel.getAttribute(e.attrname));
44595                 });
44596             }
44597             
44598             var hasStyles = false;
44599             for(var i in this.styles) {
44600                 hasStyles = true;
44601                 break;
44602             }
44603             
44604             // update styles
44605             if (hasStyles) { 
44606                 var st = this.tb.fields.item(0);
44607                 
44608                 st.store.removeAll();
44609                
44610                 
44611                 var cn = sel.className.split(/\s+/);
44612                 
44613                 var avs = [];
44614                 if (this.styles['*']) {
44615                     
44616                     Roo.each(this.styles['*'], function(v) {
44617                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44618                     });
44619                 }
44620                 if (this.styles[tn]) { 
44621                     Roo.each(this.styles[tn], function(v) {
44622                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44623                     });
44624                 }
44625                 
44626                 st.store.loadData(avs);
44627                 st.collapse();
44628                 st.setValue(cn);
44629             }
44630             // flag our selected Node.
44631             this.tb.selectedNode = sel;
44632            
44633            
44634             Roo.menu.MenuMgr.hideAll();
44635
44636         }
44637         
44638         if (!updateFooter) {
44639             //this.footDisp.dom.innerHTML = ''; 
44640             return;
44641         }
44642         // update the footer
44643         //
44644         var html = '';
44645         
44646         this.footerEls = ans.reverse();
44647         Roo.each(this.footerEls, function(a,i) {
44648             if (!a) { return; }
44649             html += html.length ? ' &gt; '  :  '';
44650             
44651             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44652             
44653         });
44654        
44655         // 
44656         var sz = this.footDisp.up('td').getSize();
44657         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44658         this.footDisp.dom.style.marginLeft = '5px';
44659         
44660         this.footDisp.dom.style.overflow = 'hidden';
44661         
44662         this.footDisp.dom.innerHTML = html;
44663             
44664         //this.editorsyncValue();
44665     },
44666      
44667     
44668    
44669        
44670     // private
44671     onDestroy : function(){
44672         if(this.rendered){
44673             
44674             this.tb.items.each(function(item){
44675                 if(item.menu){
44676                     item.menu.removeAll();
44677                     if(item.menu.el){
44678                         item.menu.el.destroy();
44679                     }
44680                 }
44681                 item.destroy();
44682             });
44683              
44684         }
44685     },
44686     onFirstFocus: function() {
44687         // need to do this for all the toolbars..
44688         this.tb.items.each(function(item){
44689            item.enable();
44690         });
44691     },
44692     buildToolbar: function(tlist, nm)
44693     {
44694         var editor = this.editor;
44695         var editorcore = this.editorcore;
44696          // create a new element.
44697         var wdiv = editor.wrap.createChild({
44698                 tag: 'div'
44699             }, editor.wrap.dom.firstChild.nextSibling, true);
44700         
44701        
44702         var tb = new Roo.Toolbar(wdiv);
44703         // add the name..
44704         
44705         tb.add(nm+ ":&nbsp;");
44706         
44707         var styles = [];
44708         for(var i in this.styles) {
44709             styles.push(i);
44710         }
44711         
44712         // styles...
44713         if (styles && styles.length) {
44714             
44715             // this needs a multi-select checkbox...
44716             tb.addField( new Roo.form.ComboBox({
44717                 store: new Roo.data.SimpleStore({
44718                     id : 'val',
44719                     fields: ['val', 'selected'],
44720                     data : [] 
44721                 }),
44722                 name : '-roo-edit-className',
44723                 attrname : 'className',
44724                 displayField: 'val',
44725                 typeAhead: false,
44726                 mode: 'local',
44727                 editable : false,
44728                 triggerAction: 'all',
44729                 emptyText:'Select Style',
44730                 selectOnFocus:true,
44731                 width: 130,
44732                 listeners : {
44733                     'select': function(c, r, i) {
44734                         // initial support only for on class per el..
44735                         tb.selectedNode.className =  r ? r.get('val') : '';
44736                         editorcore.syncValue();
44737                     }
44738                 }
44739     
44740             }));
44741         }
44742         
44743         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44744         var tbops = tbc.options;
44745         
44746         for (var i in tlist) {
44747             
44748             var item = tlist[i];
44749             tb.add(item.title + ":&nbsp;");
44750             
44751             
44752             //optname == used so you can configure the options available..
44753             var opts = item.opts ? item.opts : false;
44754             if (item.optname) {
44755                 opts = tbops[item.optname];
44756            
44757             }
44758             
44759             if (opts) {
44760                 // opts == pulldown..
44761                 tb.addField( new Roo.form.ComboBox({
44762                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44763                         id : 'val',
44764                         fields: ['val', 'display'],
44765                         data : opts  
44766                     }),
44767                     name : '-roo-edit-' + i,
44768                     attrname : i,
44769                     stylename : item.style ? item.style : false,
44770                     displayField: item.displayField ? item.displayField : 'val',
44771                     valueField :  'val',
44772                     typeAhead: false,
44773                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44774                     editable : false,
44775                     triggerAction: 'all',
44776                     emptyText:'Select',
44777                     selectOnFocus:true,
44778                     width: item.width ? item.width  : 130,
44779                     listeners : {
44780                         'select': function(c, r, i) {
44781                             if (c.stylename) {
44782                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44783                                 return;
44784                             }
44785                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44786                         }
44787                     }
44788
44789                 }));
44790                 continue;
44791                     
44792                  
44793                 
44794                 tb.addField( new Roo.form.TextField({
44795                     name: i,
44796                     width: 100,
44797                     //allowBlank:false,
44798                     value: ''
44799                 }));
44800                 continue;
44801             }
44802             tb.addField( new Roo.form.TextField({
44803                 name: '-roo-edit-' + i,
44804                 attrname : i,
44805                 
44806                 width: item.width,
44807                 //allowBlank:true,
44808                 value: '',
44809                 listeners: {
44810                     'change' : function(f, nv, ov) {
44811                         tb.selectedNode.setAttribute(f.attrname, nv);
44812                     }
44813                 }
44814             }));
44815              
44816         }
44817         
44818         var _this = this;
44819         
44820         if(nm == 'BODY'){
44821             tb.addSeparator();
44822         
44823             tb.addButton( {
44824                 text: 'Stylesheets',
44825
44826                 listeners : {
44827                     click : function ()
44828                     {
44829                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
44830                     }
44831                 }
44832             });
44833         }
44834         
44835         tb.addFill();
44836         tb.addButton( {
44837             text: 'Remove Tag',
44838     
44839             listeners : {
44840                 click : function ()
44841                 {
44842                     // remove
44843                     // undo does not work.
44844                      
44845                     var sn = tb.selectedNode;
44846                     
44847                     var pn = sn.parentNode;
44848                     
44849                     var stn =  sn.childNodes[0];
44850                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44851                     while (sn.childNodes.length) {
44852                         var node = sn.childNodes[0];
44853                         sn.removeChild(node);
44854                         //Roo.log(node);
44855                         pn.insertBefore(node, sn);
44856                         
44857                     }
44858                     pn.removeChild(sn);
44859                     var range = editorcore.createRange();
44860         
44861                     range.setStart(stn,0);
44862                     range.setEnd(en,0); //????
44863                     //range.selectNode(sel);
44864                     
44865                     
44866                     var selection = editorcore.getSelection();
44867                     selection.removeAllRanges();
44868                     selection.addRange(range);
44869                     
44870                     
44871                     
44872                     //_this.updateToolbar(null, null, pn);
44873                     _this.updateToolbar(null, null, null);
44874                     _this.footDisp.dom.innerHTML = ''; 
44875                 }
44876             }
44877             
44878                     
44879                 
44880             
44881         });
44882         
44883         
44884         tb.el.on('click', function(e){
44885             e.preventDefault(); // what does this do?
44886         });
44887         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44888         tb.el.hide();
44889         tb.name = nm;
44890         // dont need to disable them... as they will get hidden
44891         return tb;
44892          
44893         
44894     },
44895     buildFooter : function()
44896     {
44897         
44898         var fel = this.editor.wrap.createChild();
44899         this.footer = new Roo.Toolbar(fel);
44900         // toolbar has scrolly on left / right?
44901         var footDisp= new Roo.Toolbar.Fill();
44902         var _t = this;
44903         this.footer.add(
44904             {
44905                 text : '&lt;',
44906                 xtype: 'Button',
44907                 handler : function() {
44908                     _t.footDisp.scrollTo('left',0,true)
44909                 }
44910             }
44911         );
44912         this.footer.add( footDisp );
44913         this.footer.add( 
44914             {
44915                 text : '&gt;',
44916                 xtype: 'Button',
44917                 handler : function() {
44918                     // no animation..
44919                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44920                 }
44921             }
44922         );
44923         var fel = Roo.get(footDisp.el);
44924         fel.addClass('x-editor-context');
44925         this.footDispWrap = fel; 
44926         this.footDispWrap.overflow  = 'hidden';
44927         
44928         this.footDisp = fel.createChild();
44929         this.footDispWrap.on('click', this.onContextClick, this)
44930         
44931         
44932     },
44933     onContextClick : function (ev,dom)
44934     {
44935         ev.preventDefault();
44936         var  cn = dom.className;
44937         //Roo.log(cn);
44938         if (!cn.match(/x-ed-loc-/)) {
44939             return;
44940         }
44941         var n = cn.split('-').pop();
44942         var ans = this.footerEls;
44943         var sel = ans[n];
44944         
44945          // pick
44946         var range = this.editorcore.createRange();
44947         
44948         range.selectNodeContents(sel);
44949         //range.selectNode(sel);
44950         
44951         
44952         var selection = this.editorcore.getSelection();
44953         selection.removeAllRanges();
44954         selection.addRange(range);
44955         
44956         
44957         
44958         this.updateToolbar(null, null, sel);
44959         
44960         
44961     }
44962     
44963     
44964     
44965     
44966     
44967 });
44968
44969
44970
44971
44972
44973 /*
44974  * Based on:
44975  * Ext JS Library 1.1.1
44976  * Copyright(c) 2006-2007, Ext JS, LLC.
44977  *
44978  * Originally Released Under LGPL - original licence link has changed is not relivant.
44979  *
44980  * Fork - LGPL
44981  * <script type="text/javascript">
44982  */
44983  
44984 /**
44985  * @class Roo.form.BasicForm
44986  * @extends Roo.util.Observable
44987  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44988  * @constructor
44989  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44990  * @param {Object} config Configuration options
44991  */
44992 Roo.form.BasicForm = function(el, config){
44993     this.allItems = [];
44994     this.childForms = [];
44995     Roo.apply(this, config);
44996     /*
44997      * The Roo.form.Field items in this form.
44998      * @type MixedCollection
44999      */
45000      
45001      
45002     this.items = new Roo.util.MixedCollection(false, function(o){
45003         return o.id || (o.id = Roo.id());
45004     });
45005     this.addEvents({
45006         /**
45007          * @event beforeaction
45008          * Fires before any action is performed. Return false to cancel the action.
45009          * @param {Form} this
45010          * @param {Action} action The action to be performed
45011          */
45012         beforeaction: true,
45013         /**
45014          * @event actionfailed
45015          * Fires when an action fails.
45016          * @param {Form} this
45017          * @param {Action} action The action that failed
45018          */
45019         actionfailed : true,
45020         /**
45021          * @event actioncomplete
45022          * Fires when an action is completed.
45023          * @param {Form} this
45024          * @param {Action} action The action that completed
45025          */
45026         actioncomplete : true
45027     });
45028     if(el){
45029         this.initEl(el);
45030     }
45031     Roo.form.BasicForm.superclass.constructor.call(this);
45032 };
45033
45034 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
45035     /**
45036      * @cfg {String} method
45037      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
45038      */
45039     /**
45040      * @cfg {DataReader} reader
45041      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
45042      * This is optional as there is built-in support for processing JSON.
45043      */
45044     /**
45045      * @cfg {DataReader} errorReader
45046      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
45047      * This is completely optional as there is built-in support for processing JSON.
45048      */
45049     /**
45050      * @cfg {String} url
45051      * The URL to use for form actions if one isn't supplied in the action options.
45052      */
45053     /**
45054      * @cfg {Boolean} fileUpload
45055      * Set to true if this form is a file upload.
45056      */
45057      
45058     /**
45059      * @cfg {Object} baseParams
45060      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
45061      */
45062      /**
45063      
45064     /**
45065      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
45066      */
45067     timeout: 30,
45068
45069     // private
45070     activeAction : null,
45071
45072     /**
45073      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
45074      * or setValues() data instead of when the form was first created.
45075      */
45076     trackResetOnLoad : false,
45077     
45078     
45079     /**
45080      * childForms - used for multi-tab forms
45081      * @type {Array}
45082      */
45083     childForms : false,
45084     
45085     /**
45086      * allItems - full list of fields.
45087      * @type {Array}
45088      */
45089     allItems : false,
45090     
45091     /**
45092      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
45093      * element by passing it or its id or mask the form itself by passing in true.
45094      * @type Mixed
45095      */
45096     waitMsgTarget : false,
45097
45098     // private
45099     initEl : function(el){
45100         this.el = Roo.get(el);
45101         this.id = this.el.id || Roo.id();
45102         this.el.on('submit', this.onSubmit, this);
45103         this.el.addClass('x-form');
45104     },
45105
45106     // private
45107     onSubmit : function(e){
45108         e.stopEvent();
45109     },
45110
45111     /**
45112      * Returns true if client-side validation on the form is successful.
45113      * @return Boolean
45114      */
45115     isValid : function(){
45116         var valid = true;
45117         this.items.each(function(f){
45118            if(!f.validate()){
45119                valid = false;
45120            }
45121         });
45122         return valid;
45123     },
45124
45125     /**
45126      * Returns true if any fields in this form have changed since their original load.
45127      * @return Boolean
45128      */
45129     isDirty : function(){
45130         var dirty = false;
45131         this.items.each(function(f){
45132            if(f.isDirty()){
45133                dirty = true;
45134                return false;
45135            }
45136         });
45137         return dirty;
45138     },
45139
45140     /**
45141      * Performs a predefined action (submit or load) or custom actions you define on this form.
45142      * @param {String} actionName The name of the action type
45143      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
45144      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
45145      * accept other config options):
45146      * <pre>
45147 Property          Type             Description
45148 ----------------  ---------------  ----------------------------------------------------------------------------------
45149 url               String           The url for the action (defaults to the form's url)
45150 method            String           The form method to use (defaults to the form's method, or POST if not defined)
45151 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
45152 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
45153                                    validate the form on the client (defaults to false)
45154      * </pre>
45155      * @return {BasicForm} this
45156      */
45157     doAction : function(action, options){
45158         if(typeof action == 'string'){
45159             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
45160         }
45161         if(this.fireEvent('beforeaction', this, action) !== false){
45162             this.beforeAction(action);
45163             action.run.defer(100, action);
45164         }
45165         return this;
45166     },
45167
45168     /**
45169      * Shortcut to do a submit action.
45170      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45171      * @return {BasicForm} this
45172      */
45173     submit : function(options){
45174         this.doAction('submit', options);
45175         return this;
45176     },
45177
45178     /**
45179      * Shortcut to do a load action.
45180      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45181      * @return {BasicForm} this
45182      */
45183     load : function(options){
45184         this.doAction('load', options);
45185         return this;
45186     },
45187
45188     /**
45189      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
45190      * @param {Record} record The record to edit
45191      * @return {BasicForm} this
45192      */
45193     updateRecord : function(record){
45194         record.beginEdit();
45195         var fs = record.fields;
45196         fs.each(function(f){
45197             var field = this.findField(f.name);
45198             if(field){
45199                 record.set(f.name, field.getValue());
45200             }
45201         }, this);
45202         record.endEdit();
45203         return this;
45204     },
45205
45206     /**
45207      * Loads an Roo.data.Record into this form.
45208      * @param {Record} record The record to load
45209      * @return {BasicForm} this
45210      */
45211     loadRecord : function(record){
45212         this.setValues(record.data);
45213         return this;
45214     },
45215
45216     // private
45217     beforeAction : function(action){
45218         var o = action.options;
45219         
45220        
45221         if(this.waitMsgTarget === true){
45222             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
45223         }else if(this.waitMsgTarget){
45224             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
45225             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
45226         }else {
45227             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
45228         }
45229          
45230     },
45231
45232     // private
45233     afterAction : function(action, success){
45234         this.activeAction = null;
45235         var o = action.options;
45236         
45237         if(this.waitMsgTarget === true){
45238             this.el.unmask();
45239         }else if(this.waitMsgTarget){
45240             this.waitMsgTarget.unmask();
45241         }else{
45242             Roo.MessageBox.updateProgress(1);
45243             Roo.MessageBox.hide();
45244         }
45245          
45246         if(success){
45247             if(o.reset){
45248                 this.reset();
45249             }
45250             Roo.callback(o.success, o.scope, [this, action]);
45251             this.fireEvent('actioncomplete', this, action);
45252             
45253         }else{
45254             
45255             // failure condition..
45256             // we have a scenario where updates need confirming.
45257             // eg. if a locking scenario exists..
45258             // we look for { errors : { needs_confirm : true }} in the response.
45259             if (
45260                 (typeof(action.result) != 'undefined')  &&
45261                 (typeof(action.result.errors) != 'undefined')  &&
45262                 (typeof(action.result.errors.needs_confirm) != 'undefined')
45263            ){
45264                 var _t = this;
45265                 Roo.MessageBox.confirm(
45266                     "Change requires confirmation",
45267                     action.result.errorMsg,
45268                     function(r) {
45269                         if (r != 'yes') {
45270                             return;
45271                         }
45272                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
45273                     }
45274                     
45275                 );
45276                 
45277                 
45278                 
45279                 return;
45280             }
45281             
45282             Roo.callback(o.failure, o.scope, [this, action]);
45283             // show an error message if no failed handler is set..
45284             if (!this.hasListener('actionfailed')) {
45285                 Roo.MessageBox.alert("Error",
45286                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
45287                         action.result.errorMsg :
45288                         "Saving Failed, please check your entries or try again"
45289                 );
45290             }
45291             
45292             this.fireEvent('actionfailed', this, action);
45293         }
45294         
45295     },
45296
45297     /**
45298      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
45299      * @param {String} id The value to search for
45300      * @return Field
45301      */
45302     findField : function(id){
45303         var field = this.items.get(id);
45304         if(!field){
45305             this.items.each(function(f){
45306                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
45307                     field = f;
45308                     return false;
45309                 }
45310             });
45311         }
45312         return field || null;
45313     },
45314
45315     /**
45316      * Add a secondary form to this one, 
45317      * Used to provide tabbed forms. One form is primary, with hidden values 
45318      * which mirror the elements from the other forms.
45319      * 
45320      * @param {Roo.form.Form} form to add.
45321      * 
45322      */
45323     addForm : function(form)
45324     {
45325        
45326         if (this.childForms.indexOf(form) > -1) {
45327             // already added..
45328             return;
45329         }
45330         this.childForms.push(form);
45331         var n = '';
45332         Roo.each(form.allItems, function (fe) {
45333             
45334             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
45335             if (this.findField(n)) { // already added..
45336                 return;
45337             }
45338             var add = new Roo.form.Hidden({
45339                 name : n
45340             });
45341             add.render(this.el);
45342             
45343             this.add( add );
45344         }, this);
45345         
45346     },
45347     /**
45348      * Mark fields in this form invalid in bulk.
45349      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
45350      * @return {BasicForm} this
45351      */
45352     markInvalid : function(errors){
45353         if(errors instanceof Array){
45354             for(var i = 0, len = errors.length; i < len; i++){
45355                 var fieldError = errors[i];
45356                 var f = this.findField(fieldError.id);
45357                 if(f){
45358                     f.markInvalid(fieldError.msg);
45359                 }
45360             }
45361         }else{
45362             var field, id;
45363             for(id in errors){
45364                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
45365                     field.markInvalid(errors[id]);
45366                 }
45367             }
45368         }
45369         Roo.each(this.childForms || [], function (f) {
45370             f.markInvalid(errors);
45371         });
45372         
45373         return this;
45374     },
45375
45376     /**
45377      * Set values for fields in this form in bulk.
45378      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
45379      * @return {BasicForm} this
45380      */
45381     setValues : function(values){
45382         if(values instanceof Array){ // array of objects
45383             for(var i = 0, len = values.length; i < len; i++){
45384                 var v = values[i];
45385                 var f = this.findField(v.id);
45386                 if(f){
45387                     f.setValue(v.value);
45388                     if(this.trackResetOnLoad){
45389                         f.originalValue = f.getValue();
45390                     }
45391                 }
45392             }
45393         }else{ // object hash
45394             var field, id;
45395             for(id in values){
45396                 if(typeof values[id] != 'function' && (field = this.findField(id))){
45397                     
45398                     if (field.setFromData && 
45399                         field.valueField && 
45400                         field.displayField &&
45401                         // combos' with local stores can 
45402                         // be queried via setValue()
45403                         // to set their value..
45404                         (field.store && !field.store.isLocal)
45405                         ) {
45406                         // it's a combo
45407                         var sd = { };
45408                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
45409                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
45410                         field.setFromData(sd);
45411                         
45412                     } else {
45413                         field.setValue(values[id]);
45414                     }
45415                     
45416                     
45417                     if(this.trackResetOnLoad){
45418                         field.originalValue = field.getValue();
45419                     }
45420                 }
45421             }
45422         }
45423          
45424         Roo.each(this.childForms || [], function (f) {
45425             f.setValues(values);
45426         });
45427                 
45428         return this;
45429     },
45430
45431     /**
45432      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
45433      * they are returned as an array.
45434      * @param {Boolean} asString
45435      * @return {Object}
45436      */
45437     getValues : function(asString){
45438         if (this.childForms) {
45439             // copy values from the child forms
45440             Roo.each(this.childForms, function (f) {
45441                 this.setValues(f.getValues());
45442             }, this);
45443         }
45444         
45445         
45446         
45447         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
45448         if(asString === true){
45449             return fs;
45450         }
45451         return Roo.urlDecode(fs);
45452     },
45453     
45454     /**
45455      * Returns the fields in this form as an object with key/value pairs. 
45456      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
45457      * @return {Object}
45458      */
45459     getFieldValues : function(with_hidden)
45460     {
45461         if (this.childForms) {
45462             // copy values from the child forms
45463             // should this call getFieldValues - probably not as we do not currently copy
45464             // hidden fields when we generate..
45465             Roo.each(this.childForms, function (f) {
45466                 this.setValues(f.getValues());
45467             }, this);
45468         }
45469         
45470         var ret = {};
45471         this.items.each(function(f){
45472             if (!f.getName()) {
45473                 return;
45474             }
45475             var v = f.getValue();
45476             if (f.inputType =='radio') {
45477                 if (typeof(ret[f.getName()]) == 'undefined') {
45478                     ret[f.getName()] = ''; // empty..
45479                 }
45480                 
45481                 if (!f.el.dom.checked) {
45482                     return;
45483                     
45484                 }
45485                 v = f.el.dom.value;
45486                 
45487             }
45488             
45489             // not sure if this supported any more..
45490             if ((typeof(v) == 'object') && f.getRawValue) {
45491                 v = f.getRawValue() ; // dates..
45492             }
45493             // combo boxes where name != hiddenName...
45494             if (f.name != f.getName()) {
45495                 ret[f.name] = f.getRawValue();
45496             }
45497             ret[f.getName()] = v;
45498         });
45499         
45500         return ret;
45501     },
45502
45503     /**
45504      * Clears all invalid messages in this form.
45505      * @return {BasicForm} this
45506      */
45507     clearInvalid : function(){
45508         this.items.each(function(f){
45509            f.clearInvalid();
45510         });
45511         
45512         Roo.each(this.childForms || [], function (f) {
45513             f.clearInvalid();
45514         });
45515         
45516         
45517         return this;
45518     },
45519
45520     /**
45521      * Resets this form.
45522      * @return {BasicForm} this
45523      */
45524     reset : function(){
45525         this.items.each(function(f){
45526             f.reset();
45527         });
45528         
45529         Roo.each(this.childForms || [], function (f) {
45530             f.reset();
45531         });
45532        
45533         
45534         return this;
45535     },
45536
45537     /**
45538      * Add Roo.form components to this form.
45539      * @param {Field} field1
45540      * @param {Field} field2 (optional)
45541      * @param {Field} etc (optional)
45542      * @return {BasicForm} this
45543      */
45544     add : function(){
45545         this.items.addAll(Array.prototype.slice.call(arguments, 0));
45546         return this;
45547     },
45548
45549
45550     /**
45551      * Removes a field from the items collection (does NOT remove its markup).
45552      * @param {Field} field
45553      * @return {BasicForm} this
45554      */
45555     remove : function(field){
45556         this.items.remove(field);
45557         return this;
45558     },
45559
45560     /**
45561      * Looks at the fields in this form, checks them for an id attribute,
45562      * and calls applyTo on the existing dom element with that id.
45563      * @return {BasicForm} this
45564      */
45565     render : function(){
45566         this.items.each(function(f){
45567             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
45568                 f.applyTo(f.id);
45569             }
45570         });
45571         return this;
45572     },
45573
45574     /**
45575      * Calls {@link Ext#apply} for all fields in this form with the passed object.
45576      * @param {Object} values
45577      * @return {BasicForm} this
45578      */
45579     applyToFields : function(o){
45580         this.items.each(function(f){
45581            Roo.apply(f, o);
45582         });
45583         return this;
45584     },
45585
45586     /**
45587      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
45588      * @param {Object} values
45589      * @return {BasicForm} this
45590      */
45591     applyIfToFields : function(o){
45592         this.items.each(function(f){
45593            Roo.applyIf(f, o);
45594         });
45595         return this;
45596     }
45597 });
45598
45599 // back compat
45600 Roo.BasicForm = Roo.form.BasicForm;/*
45601  * Based on:
45602  * Ext JS Library 1.1.1
45603  * Copyright(c) 2006-2007, Ext JS, LLC.
45604  *
45605  * Originally Released Under LGPL - original licence link has changed is not relivant.
45606  *
45607  * Fork - LGPL
45608  * <script type="text/javascript">
45609  */
45610
45611 /**
45612  * @class Roo.form.Form
45613  * @extends Roo.form.BasicForm
45614  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
45615  * @constructor
45616  * @param {Object} config Configuration options
45617  */
45618 Roo.form.Form = function(config){
45619     var xitems =  [];
45620     if (config.items) {
45621         xitems = config.items;
45622         delete config.items;
45623     }
45624    
45625     
45626     Roo.form.Form.superclass.constructor.call(this, null, config);
45627     this.url = this.url || this.action;
45628     if(!this.root){
45629         this.root = new Roo.form.Layout(Roo.applyIf({
45630             id: Roo.id()
45631         }, config));
45632     }
45633     this.active = this.root;
45634     /**
45635      * Array of all the buttons that have been added to this form via {@link addButton}
45636      * @type Array
45637      */
45638     this.buttons = [];
45639     this.allItems = [];
45640     this.addEvents({
45641         /**
45642          * @event clientvalidation
45643          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45644          * @param {Form} this
45645          * @param {Boolean} valid true if the form has passed client-side validation
45646          */
45647         clientvalidation: true,
45648         /**
45649          * @event rendered
45650          * Fires when the form is rendered
45651          * @param {Roo.form.Form} form
45652          */
45653         rendered : true
45654     });
45655     
45656     if (this.progressUrl) {
45657             // push a hidden field onto the list of fields..
45658             this.addxtype( {
45659                     xns: Roo.form, 
45660                     xtype : 'Hidden', 
45661                     name : 'UPLOAD_IDENTIFIER' 
45662             });
45663         }
45664         
45665     
45666     Roo.each(xitems, this.addxtype, this);
45667     
45668     
45669     
45670 };
45671
45672 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45673     /**
45674      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45675      */
45676     /**
45677      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45678      */
45679     /**
45680      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45681      */
45682     buttonAlign:'center',
45683
45684     /**
45685      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45686      */
45687     minButtonWidth:75,
45688
45689     /**
45690      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45691      * This property cascades to child containers if not set.
45692      */
45693     labelAlign:'left',
45694
45695     /**
45696      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45697      * fires a looping event with that state. This is required to bind buttons to the valid
45698      * state using the config value formBind:true on the button.
45699      */
45700     monitorValid : false,
45701
45702     /**
45703      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45704      */
45705     monitorPoll : 200,
45706     
45707     /**
45708      * @cfg {String} progressUrl - Url to return progress data 
45709      */
45710     
45711     progressUrl : false,
45712   
45713     /**
45714      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45715      * fields are added and the column is closed. If no fields are passed the column remains open
45716      * until end() is called.
45717      * @param {Object} config The config to pass to the column
45718      * @param {Field} field1 (optional)
45719      * @param {Field} field2 (optional)
45720      * @param {Field} etc (optional)
45721      * @return Column The column container object
45722      */
45723     column : function(c){
45724         var col = new Roo.form.Column(c);
45725         this.start(col);
45726         if(arguments.length > 1){ // duplicate code required because of Opera
45727             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45728             this.end();
45729         }
45730         return col;
45731     },
45732
45733     /**
45734      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45735      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45736      * until end() is called.
45737      * @param {Object} config The config to pass to the fieldset
45738      * @param {Field} field1 (optional)
45739      * @param {Field} field2 (optional)
45740      * @param {Field} etc (optional)
45741      * @return FieldSet The fieldset container object
45742      */
45743     fieldset : function(c){
45744         var fs = new Roo.form.FieldSet(c);
45745         this.start(fs);
45746         if(arguments.length > 1){ // duplicate code required because of Opera
45747             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45748             this.end();
45749         }
45750         return fs;
45751     },
45752
45753     /**
45754      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45755      * fields are added and the container is closed. If no fields are passed the container remains open
45756      * until end() is called.
45757      * @param {Object} config The config to pass to the Layout
45758      * @param {Field} field1 (optional)
45759      * @param {Field} field2 (optional)
45760      * @param {Field} etc (optional)
45761      * @return Layout The container object
45762      */
45763     container : function(c){
45764         var l = new Roo.form.Layout(c);
45765         this.start(l);
45766         if(arguments.length > 1){ // duplicate code required because of Opera
45767             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45768             this.end();
45769         }
45770         return l;
45771     },
45772
45773     /**
45774      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45775      * @param {Object} container A Roo.form.Layout or subclass of Layout
45776      * @return {Form} this
45777      */
45778     start : function(c){
45779         // cascade label info
45780         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45781         this.active.stack.push(c);
45782         c.ownerCt = this.active;
45783         this.active = c;
45784         return this;
45785     },
45786
45787     /**
45788      * Closes the current open container
45789      * @return {Form} this
45790      */
45791     end : function(){
45792         if(this.active == this.root){
45793             return this;
45794         }
45795         this.active = this.active.ownerCt;
45796         return this;
45797     },
45798
45799     /**
45800      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45801      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45802      * as the label of the field.
45803      * @param {Field} field1
45804      * @param {Field} field2 (optional)
45805      * @param {Field} etc. (optional)
45806      * @return {Form} this
45807      */
45808     add : function(){
45809         this.active.stack.push.apply(this.active.stack, arguments);
45810         this.allItems.push.apply(this.allItems,arguments);
45811         var r = [];
45812         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45813             if(a[i].isFormField){
45814                 r.push(a[i]);
45815             }
45816         }
45817         if(r.length > 0){
45818             Roo.form.Form.superclass.add.apply(this, r);
45819         }
45820         return this;
45821     },
45822     
45823
45824     
45825     
45826     
45827      /**
45828      * Find any element that has been added to a form, using it's ID or name
45829      * This can include framesets, columns etc. along with regular fields..
45830      * @param {String} id - id or name to find.
45831      
45832      * @return {Element} e - or false if nothing found.
45833      */
45834     findbyId : function(id)
45835     {
45836         var ret = false;
45837         if (!id) {
45838             return ret;
45839         }
45840         Roo.each(this.allItems, function(f){
45841             if (f.id == id || f.name == id ){
45842                 ret = f;
45843                 return false;
45844             }
45845         });
45846         return ret;
45847     },
45848
45849     
45850     
45851     /**
45852      * Render this form into the passed container. This should only be called once!
45853      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45854      * @return {Form} this
45855      */
45856     render : function(ct)
45857     {
45858         
45859         
45860         
45861         ct = Roo.get(ct);
45862         var o = this.autoCreate || {
45863             tag: 'form',
45864             method : this.method || 'POST',
45865             id : this.id || Roo.id()
45866         };
45867         this.initEl(ct.createChild(o));
45868
45869         this.root.render(this.el);
45870         
45871        
45872              
45873         this.items.each(function(f){
45874             f.render('x-form-el-'+f.id);
45875         });
45876
45877         if(this.buttons.length > 0){
45878             // tables are required to maintain order and for correct IE layout
45879             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45880                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45881                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45882             }}, null, true);
45883             var tr = tb.getElementsByTagName('tr')[0];
45884             for(var i = 0, len = this.buttons.length; i < len; i++) {
45885                 var b = this.buttons[i];
45886                 var td = document.createElement('td');
45887                 td.className = 'x-form-btn-td';
45888                 b.render(tr.appendChild(td));
45889             }
45890         }
45891         if(this.monitorValid){ // initialize after render
45892             this.startMonitoring();
45893         }
45894         this.fireEvent('rendered', this);
45895         return this;
45896     },
45897
45898     /**
45899      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45900      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45901      * object or a valid Roo.DomHelper element config
45902      * @param {Function} handler The function called when the button is clicked
45903      * @param {Object} scope (optional) The scope of the handler function
45904      * @return {Roo.Button}
45905      */
45906     addButton : function(config, handler, scope){
45907         var bc = {
45908             handler: handler,
45909             scope: scope,
45910             minWidth: this.minButtonWidth,
45911             hideParent:true
45912         };
45913         if(typeof config == "string"){
45914             bc.text = config;
45915         }else{
45916             Roo.apply(bc, config);
45917         }
45918         var btn = new Roo.Button(null, bc);
45919         this.buttons.push(btn);
45920         return btn;
45921     },
45922
45923      /**
45924      * Adds a series of form elements (using the xtype property as the factory method.
45925      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45926      * @param {Object} config 
45927      */
45928     
45929     addxtype : function()
45930     {
45931         var ar = Array.prototype.slice.call(arguments, 0);
45932         var ret = false;
45933         for(var i = 0; i < ar.length; i++) {
45934             if (!ar[i]) {
45935                 continue; // skip -- if this happends something invalid got sent, we 
45936                 // should ignore it, as basically that interface element will not show up
45937                 // and that should be pretty obvious!!
45938             }
45939             
45940             if (Roo.form[ar[i].xtype]) {
45941                 ar[i].form = this;
45942                 var fe = Roo.factory(ar[i], Roo.form);
45943                 if (!ret) {
45944                     ret = fe;
45945                 }
45946                 fe.form = this;
45947                 if (fe.store) {
45948                     fe.store.form = this;
45949                 }
45950                 if (fe.isLayout) {  
45951                          
45952                     this.start(fe);
45953                     this.allItems.push(fe);
45954                     if (fe.items && fe.addxtype) {
45955                         fe.addxtype.apply(fe, fe.items);
45956                         delete fe.items;
45957                     }
45958                      this.end();
45959                     continue;
45960                 }
45961                 
45962                 
45963                  
45964                 this.add(fe);
45965               //  console.log('adding ' + ar[i].xtype);
45966             }
45967             if (ar[i].xtype == 'Button') {  
45968                 //console.log('adding button');
45969                 //console.log(ar[i]);
45970                 this.addButton(ar[i]);
45971                 this.allItems.push(fe);
45972                 continue;
45973             }
45974             
45975             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45976                 alert('end is not supported on xtype any more, use items');
45977             //    this.end();
45978             //    //console.log('adding end');
45979             }
45980             
45981         }
45982         return ret;
45983     },
45984     
45985     /**
45986      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45987      * option "monitorValid"
45988      */
45989     startMonitoring : function(){
45990         if(!this.bound){
45991             this.bound = true;
45992             Roo.TaskMgr.start({
45993                 run : this.bindHandler,
45994                 interval : this.monitorPoll || 200,
45995                 scope: this
45996             });
45997         }
45998     },
45999
46000     /**
46001      * Stops monitoring of the valid state of this form
46002      */
46003     stopMonitoring : function(){
46004         this.bound = false;
46005     },
46006
46007     // private
46008     bindHandler : function(){
46009         if(!this.bound){
46010             return false; // stops binding
46011         }
46012         var valid = true;
46013         this.items.each(function(f){
46014             if(!f.isValid(true)){
46015                 valid = false;
46016                 return false;
46017             }
46018         });
46019         for(var i = 0, len = this.buttons.length; i < len; i++){
46020             var btn = this.buttons[i];
46021             if(btn.formBind === true && btn.disabled === valid){
46022                 btn.setDisabled(!valid);
46023             }
46024         }
46025         this.fireEvent('clientvalidation', this, valid);
46026     }
46027     
46028     
46029     
46030     
46031     
46032     
46033     
46034     
46035 });
46036
46037
46038 // back compat
46039 Roo.Form = Roo.form.Form;
46040 /*
46041  * Based on:
46042  * Ext JS Library 1.1.1
46043  * Copyright(c) 2006-2007, Ext JS, LLC.
46044  *
46045  * Originally Released Under LGPL - original licence link has changed is not relivant.
46046  *
46047  * Fork - LGPL
46048  * <script type="text/javascript">
46049  */
46050
46051 // as we use this in bootstrap.
46052 Roo.namespace('Roo.form');
46053  /**
46054  * @class Roo.form.Action
46055  * Internal Class used to handle form actions
46056  * @constructor
46057  * @param {Roo.form.BasicForm} el The form element or its id
46058  * @param {Object} config Configuration options
46059  */
46060
46061  
46062  
46063 // define the action interface
46064 Roo.form.Action = function(form, options){
46065     this.form = form;
46066     this.options = options || {};
46067 };
46068 /**
46069  * Client Validation Failed
46070  * @const 
46071  */
46072 Roo.form.Action.CLIENT_INVALID = 'client';
46073 /**
46074  * Server Validation Failed
46075  * @const 
46076  */
46077 Roo.form.Action.SERVER_INVALID = 'server';
46078  /**
46079  * Connect to Server Failed
46080  * @const 
46081  */
46082 Roo.form.Action.CONNECT_FAILURE = 'connect';
46083 /**
46084  * Reading Data from Server Failed
46085  * @const 
46086  */
46087 Roo.form.Action.LOAD_FAILURE = 'load';
46088
46089 Roo.form.Action.prototype = {
46090     type : 'default',
46091     failureType : undefined,
46092     response : undefined,
46093     result : undefined,
46094
46095     // interface method
46096     run : function(options){
46097
46098     },
46099
46100     // interface method
46101     success : function(response){
46102
46103     },
46104
46105     // interface method
46106     handleResponse : function(response){
46107
46108     },
46109
46110     // default connection failure
46111     failure : function(response){
46112         
46113         this.response = response;
46114         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46115         this.form.afterAction(this, false);
46116     },
46117
46118     processResponse : function(response){
46119         this.response = response;
46120         if(!response.responseText){
46121             return true;
46122         }
46123         this.result = this.handleResponse(response);
46124         return this.result;
46125     },
46126
46127     // utility functions used internally
46128     getUrl : function(appendParams){
46129         var url = this.options.url || this.form.url || this.form.el.dom.action;
46130         if(appendParams){
46131             var p = this.getParams();
46132             if(p){
46133                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
46134             }
46135         }
46136         return url;
46137     },
46138
46139     getMethod : function(){
46140         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
46141     },
46142
46143     getParams : function(){
46144         var bp = this.form.baseParams;
46145         var p = this.options.params;
46146         if(p){
46147             if(typeof p == "object"){
46148                 p = Roo.urlEncode(Roo.applyIf(p, bp));
46149             }else if(typeof p == 'string' && bp){
46150                 p += '&' + Roo.urlEncode(bp);
46151             }
46152         }else if(bp){
46153             p = Roo.urlEncode(bp);
46154         }
46155         return p;
46156     },
46157
46158     createCallback : function(){
46159         return {
46160             success: this.success,
46161             failure: this.failure,
46162             scope: this,
46163             timeout: (this.form.timeout*1000),
46164             upload: this.form.fileUpload ? this.success : undefined
46165         };
46166     }
46167 };
46168
46169 Roo.form.Action.Submit = function(form, options){
46170     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
46171 };
46172
46173 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
46174     type : 'submit',
46175
46176     haveProgress : false,
46177     uploadComplete : false,
46178     
46179     // uploadProgress indicator.
46180     uploadProgress : function()
46181     {
46182         if (!this.form.progressUrl) {
46183             return;
46184         }
46185         
46186         if (!this.haveProgress) {
46187             Roo.MessageBox.progress("Uploading", "Uploading");
46188         }
46189         if (this.uploadComplete) {
46190            Roo.MessageBox.hide();
46191            return;
46192         }
46193         
46194         this.haveProgress = true;
46195    
46196         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
46197         
46198         var c = new Roo.data.Connection();
46199         c.request({
46200             url : this.form.progressUrl,
46201             params: {
46202                 id : uid
46203             },
46204             method: 'GET',
46205             success : function(req){
46206                //console.log(data);
46207                 var rdata = false;
46208                 var edata;
46209                 try  {
46210                    rdata = Roo.decode(req.responseText)
46211                 } catch (e) {
46212                     Roo.log("Invalid data from server..");
46213                     Roo.log(edata);
46214                     return;
46215                 }
46216                 if (!rdata || !rdata.success) {
46217                     Roo.log(rdata);
46218                     Roo.MessageBox.alert(Roo.encode(rdata));
46219                     return;
46220                 }
46221                 var data = rdata.data;
46222                 
46223                 if (this.uploadComplete) {
46224                    Roo.MessageBox.hide();
46225                    return;
46226                 }
46227                    
46228                 if (data){
46229                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
46230                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
46231                     );
46232                 }
46233                 this.uploadProgress.defer(2000,this);
46234             },
46235        
46236             failure: function(data) {
46237                 Roo.log('progress url failed ');
46238                 Roo.log(data);
46239             },
46240             scope : this
46241         });
46242            
46243     },
46244     
46245     
46246     run : function()
46247     {
46248         // run get Values on the form, so it syncs any secondary forms.
46249         this.form.getValues();
46250         
46251         var o = this.options;
46252         var method = this.getMethod();
46253         var isPost = method == 'POST';
46254         if(o.clientValidation === false || this.form.isValid()){
46255             
46256             if (this.form.progressUrl) {
46257                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
46258                     (new Date() * 1) + '' + Math.random());
46259                     
46260             } 
46261             
46262             
46263             Roo.Ajax.request(Roo.apply(this.createCallback(), {
46264                 form:this.form.el.dom,
46265                 url:this.getUrl(!isPost),
46266                 method: method,
46267                 params:isPost ? this.getParams() : null,
46268                 isUpload: this.form.fileUpload
46269             }));
46270             
46271             this.uploadProgress();
46272
46273         }else if (o.clientValidation !== false){ // client validation failed
46274             this.failureType = Roo.form.Action.CLIENT_INVALID;
46275             this.form.afterAction(this, false);
46276         }
46277     },
46278
46279     success : function(response)
46280     {
46281         this.uploadComplete= true;
46282         if (this.haveProgress) {
46283             Roo.MessageBox.hide();
46284         }
46285         
46286         
46287         var result = this.processResponse(response);
46288         if(result === true || result.success){
46289             this.form.afterAction(this, true);
46290             return;
46291         }
46292         if(result.errors){
46293             this.form.markInvalid(result.errors);
46294             this.failureType = Roo.form.Action.SERVER_INVALID;
46295         }
46296         this.form.afterAction(this, false);
46297     },
46298     failure : function(response)
46299     {
46300         this.uploadComplete= true;
46301         if (this.haveProgress) {
46302             Roo.MessageBox.hide();
46303         }
46304         
46305         this.response = response;
46306         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46307         this.form.afterAction(this, false);
46308     },
46309     
46310     handleResponse : function(response){
46311         if(this.form.errorReader){
46312             var rs = this.form.errorReader.read(response);
46313             var errors = [];
46314             if(rs.records){
46315                 for(var i = 0, len = rs.records.length; i < len; i++) {
46316                     var r = rs.records[i];
46317                     errors[i] = r.data;
46318                 }
46319             }
46320             if(errors.length < 1){
46321                 errors = null;
46322             }
46323             return {
46324                 success : rs.success,
46325                 errors : errors
46326             };
46327         }
46328         var ret = false;
46329         try {
46330             ret = Roo.decode(response.responseText);
46331         } catch (e) {
46332             ret = {
46333                 success: false,
46334                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
46335                 errors : []
46336             };
46337         }
46338         return ret;
46339         
46340     }
46341 });
46342
46343
46344 Roo.form.Action.Load = function(form, options){
46345     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
46346     this.reader = this.form.reader;
46347 };
46348
46349 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
46350     type : 'load',
46351
46352     run : function(){
46353         
46354         Roo.Ajax.request(Roo.apply(
46355                 this.createCallback(), {
46356                     method:this.getMethod(),
46357                     url:this.getUrl(false),
46358                     params:this.getParams()
46359         }));
46360     },
46361
46362     success : function(response){
46363         
46364         var result = this.processResponse(response);
46365         if(result === true || !result.success || !result.data){
46366             this.failureType = Roo.form.Action.LOAD_FAILURE;
46367             this.form.afterAction(this, false);
46368             return;
46369         }
46370         this.form.clearInvalid();
46371         this.form.setValues(result.data);
46372         this.form.afterAction(this, true);
46373     },
46374
46375     handleResponse : function(response){
46376         if(this.form.reader){
46377             var rs = this.form.reader.read(response);
46378             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
46379             return {
46380                 success : rs.success,
46381                 data : data
46382             };
46383         }
46384         return Roo.decode(response.responseText);
46385     }
46386 });
46387
46388 Roo.form.Action.ACTION_TYPES = {
46389     'load' : Roo.form.Action.Load,
46390     'submit' : Roo.form.Action.Submit
46391 };/*
46392  * Based on:
46393  * Ext JS Library 1.1.1
46394  * Copyright(c) 2006-2007, Ext JS, LLC.
46395  *
46396  * Originally Released Under LGPL - original licence link has changed is not relivant.
46397  *
46398  * Fork - LGPL
46399  * <script type="text/javascript">
46400  */
46401  
46402 /**
46403  * @class Roo.form.Layout
46404  * @extends Roo.Component
46405  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
46406  * @constructor
46407  * @param {Object} config Configuration options
46408  */
46409 Roo.form.Layout = function(config){
46410     var xitems = [];
46411     if (config.items) {
46412         xitems = config.items;
46413         delete config.items;
46414     }
46415     Roo.form.Layout.superclass.constructor.call(this, config);
46416     this.stack = [];
46417     Roo.each(xitems, this.addxtype, this);
46418      
46419 };
46420
46421 Roo.extend(Roo.form.Layout, Roo.Component, {
46422     /**
46423      * @cfg {String/Object} autoCreate
46424      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
46425      */
46426     /**
46427      * @cfg {String/Object/Function} style
46428      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
46429      * a function which returns such a specification.
46430      */
46431     /**
46432      * @cfg {String} labelAlign
46433      * Valid values are "left," "top" and "right" (defaults to "left")
46434      */
46435     /**
46436      * @cfg {Number} labelWidth
46437      * Fixed width in pixels of all field labels (defaults to undefined)
46438      */
46439     /**
46440      * @cfg {Boolean} clear
46441      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
46442      */
46443     clear : true,
46444     /**
46445      * @cfg {String} labelSeparator
46446      * The separator to use after field labels (defaults to ':')
46447      */
46448     labelSeparator : ':',
46449     /**
46450      * @cfg {Boolean} hideLabels
46451      * True to suppress the display of field labels in this layout (defaults to false)
46452      */
46453     hideLabels : false,
46454
46455     // private
46456     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
46457     
46458     isLayout : true,
46459     
46460     // private
46461     onRender : function(ct, position){
46462         if(this.el){ // from markup
46463             this.el = Roo.get(this.el);
46464         }else {  // generate
46465             var cfg = this.getAutoCreate();
46466             this.el = ct.createChild(cfg, position);
46467         }
46468         if(this.style){
46469             this.el.applyStyles(this.style);
46470         }
46471         if(this.labelAlign){
46472             this.el.addClass('x-form-label-'+this.labelAlign);
46473         }
46474         if(this.hideLabels){
46475             this.labelStyle = "display:none";
46476             this.elementStyle = "padding-left:0;";
46477         }else{
46478             if(typeof this.labelWidth == 'number'){
46479                 this.labelStyle = "width:"+this.labelWidth+"px;";
46480                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
46481             }
46482             if(this.labelAlign == 'top'){
46483                 this.labelStyle = "width:auto;";
46484                 this.elementStyle = "padding-left:0;";
46485             }
46486         }
46487         var stack = this.stack;
46488         var slen = stack.length;
46489         if(slen > 0){
46490             if(!this.fieldTpl){
46491                 var t = new Roo.Template(
46492                     '<div class="x-form-item {5}">',
46493                         '<label for="{0}" style="{2}">{1}{4}</label>',
46494                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46495                         '</div>',
46496                     '</div><div class="x-form-clear-left"></div>'
46497                 );
46498                 t.disableFormats = true;
46499                 t.compile();
46500                 Roo.form.Layout.prototype.fieldTpl = t;
46501             }
46502             for(var i = 0; i < slen; i++) {
46503                 if(stack[i].isFormField){
46504                     this.renderField(stack[i]);
46505                 }else{
46506                     this.renderComponent(stack[i]);
46507                 }
46508             }
46509         }
46510         if(this.clear){
46511             this.el.createChild({cls:'x-form-clear'});
46512         }
46513     },
46514
46515     // private
46516     renderField : function(f){
46517         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
46518                f.id, //0
46519                f.fieldLabel, //1
46520                f.labelStyle||this.labelStyle||'', //2
46521                this.elementStyle||'', //3
46522                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
46523                f.itemCls||this.itemCls||''  //5
46524        ], true).getPrevSibling());
46525     },
46526
46527     // private
46528     renderComponent : function(c){
46529         c.render(c.isLayout ? this.el : this.el.createChild());    
46530     },
46531     /**
46532      * Adds a object form elements (using the xtype property as the factory method.)
46533      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
46534      * @param {Object} config 
46535      */
46536     addxtype : function(o)
46537     {
46538         // create the lement.
46539         o.form = this.form;
46540         var fe = Roo.factory(o, Roo.form);
46541         this.form.allItems.push(fe);
46542         this.stack.push(fe);
46543         
46544         if (fe.isFormField) {
46545             this.form.items.add(fe);
46546         }
46547          
46548         return fe;
46549     }
46550 });
46551
46552 /**
46553  * @class Roo.form.Column
46554  * @extends Roo.form.Layout
46555  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
46556  * @constructor
46557  * @param {Object} config Configuration options
46558  */
46559 Roo.form.Column = function(config){
46560     Roo.form.Column.superclass.constructor.call(this, config);
46561 };
46562
46563 Roo.extend(Roo.form.Column, Roo.form.Layout, {
46564     /**
46565      * @cfg {Number/String} width
46566      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46567      */
46568     /**
46569      * @cfg {String/Object} autoCreate
46570      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
46571      */
46572
46573     // private
46574     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
46575
46576     // private
46577     onRender : function(ct, position){
46578         Roo.form.Column.superclass.onRender.call(this, ct, position);
46579         if(this.width){
46580             this.el.setWidth(this.width);
46581         }
46582     }
46583 });
46584
46585
46586 /**
46587  * @class Roo.form.Row
46588  * @extends Roo.form.Layout
46589  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
46590  * @constructor
46591  * @param {Object} config Configuration options
46592  */
46593
46594  
46595 Roo.form.Row = function(config){
46596     Roo.form.Row.superclass.constructor.call(this, config);
46597 };
46598  
46599 Roo.extend(Roo.form.Row, Roo.form.Layout, {
46600       /**
46601      * @cfg {Number/String} width
46602      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46603      */
46604     /**
46605      * @cfg {Number/String} height
46606      * The fixed height of the column in pixels or CSS value (defaults to "auto")
46607      */
46608     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
46609     
46610     padWidth : 20,
46611     // private
46612     onRender : function(ct, position){
46613         //console.log('row render');
46614         if(!this.rowTpl){
46615             var t = new Roo.Template(
46616                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
46617                     '<label for="{0}" style="{2}">{1}{4}</label>',
46618                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46619                     '</div>',
46620                 '</div>'
46621             );
46622             t.disableFormats = true;
46623             t.compile();
46624             Roo.form.Layout.prototype.rowTpl = t;
46625         }
46626         this.fieldTpl = this.rowTpl;
46627         
46628         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46629         var labelWidth = 100;
46630         
46631         if ((this.labelAlign != 'top')) {
46632             if (typeof this.labelWidth == 'number') {
46633                 labelWidth = this.labelWidth
46634             }
46635             this.padWidth =  20 + labelWidth;
46636             
46637         }
46638         
46639         Roo.form.Column.superclass.onRender.call(this, ct, position);
46640         if(this.width){
46641             this.el.setWidth(this.width);
46642         }
46643         if(this.height){
46644             this.el.setHeight(this.height);
46645         }
46646     },
46647     
46648     // private
46649     renderField : function(f){
46650         f.fieldEl = this.fieldTpl.append(this.el, [
46651                f.id, f.fieldLabel,
46652                f.labelStyle||this.labelStyle||'',
46653                this.elementStyle||'',
46654                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46655                f.itemCls||this.itemCls||'',
46656                f.width ? f.width + this.padWidth : 160 + this.padWidth
46657        ],true);
46658     }
46659 });
46660  
46661
46662 /**
46663  * @class Roo.form.FieldSet
46664  * @extends Roo.form.Layout
46665  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46666  * @constructor
46667  * @param {Object} config Configuration options
46668  */
46669 Roo.form.FieldSet = function(config){
46670     Roo.form.FieldSet.superclass.constructor.call(this, config);
46671 };
46672
46673 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46674     /**
46675      * @cfg {String} legend
46676      * The text to display as the legend for the FieldSet (defaults to '')
46677      */
46678     /**
46679      * @cfg {String/Object} autoCreate
46680      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46681      */
46682
46683     // private
46684     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46685
46686     // private
46687     onRender : function(ct, position){
46688         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46689         if(this.legend){
46690             this.setLegend(this.legend);
46691         }
46692     },
46693
46694     // private
46695     setLegend : function(text){
46696         if(this.rendered){
46697             this.el.child('legend').update(text);
46698         }
46699     }
46700 });/*
46701  * Based on:
46702  * Ext JS Library 1.1.1
46703  * Copyright(c) 2006-2007, Ext JS, LLC.
46704  *
46705  * Originally Released Under LGPL - original licence link has changed is not relivant.
46706  *
46707  * Fork - LGPL
46708  * <script type="text/javascript">
46709  */
46710 /**
46711  * @class Roo.form.VTypes
46712  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46713  * @singleton
46714  */
46715 Roo.form.VTypes = function(){
46716     // closure these in so they are only created once.
46717     var alpha = /^[a-zA-Z_]+$/;
46718     var alphanum = /^[a-zA-Z0-9_]+$/;
46719     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46720     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46721
46722     // All these messages and functions are configurable
46723     return {
46724         /**
46725          * The function used to validate email addresses
46726          * @param {String} value The email address
46727          */
46728         'email' : function(v){
46729             return email.test(v);
46730         },
46731         /**
46732          * The error text to display when the email validation function returns false
46733          * @type String
46734          */
46735         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46736         /**
46737          * The keystroke filter mask to be applied on email input
46738          * @type RegExp
46739          */
46740         'emailMask' : /[a-z0-9_\.\-@]/i,
46741
46742         /**
46743          * The function used to validate URLs
46744          * @param {String} value The URL
46745          */
46746         'url' : function(v){
46747             return url.test(v);
46748         },
46749         /**
46750          * The error text to display when the url validation function returns false
46751          * @type String
46752          */
46753         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46754         
46755         /**
46756          * The function used to validate alpha values
46757          * @param {String} value The value
46758          */
46759         'alpha' : function(v){
46760             return alpha.test(v);
46761         },
46762         /**
46763          * The error text to display when the alpha validation function returns false
46764          * @type String
46765          */
46766         'alphaText' : 'This field should only contain letters and _',
46767         /**
46768          * The keystroke filter mask to be applied on alpha input
46769          * @type RegExp
46770          */
46771         'alphaMask' : /[a-z_]/i,
46772
46773         /**
46774          * The function used to validate alphanumeric values
46775          * @param {String} value The value
46776          */
46777         'alphanum' : function(v){
46778             return alphanum.test(v);
46779         },
46780         /**
46781          * The error text to display when the alphanumeric validation function returns false
46782          * @type String
46783          */
46784         'alphanumText' : 'This field should only contain letters, numbers and _',
46785         /**
46786          * The keystroke filter mask to be applied on alphanumeric input
46787          * @type RegExp
46788          */
46789         'alphanumMask' : /[a-z0-9_]/i
46790     };
46791 }();//<script type="text/javascript">
46792
46793 /**
46794  * @class Roo.form.FCKeditor
46795  * @extends Roo.form.TextArea
46796  * Wrapper around the FCKEditor http://www.fckeditor.net
46797  * @constructor
46798  * Creates a new FCKeditor
46799  * @param {Object} config Configuration options
46800  */
46801 Roo.form.FCKeditor = function(config){
46802     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46803     this.addEvents({
46804          /**
46805          * @event editorinit
46806          * Fired when the editor is initialized - you can add extra handlers here..
46807          * @param {FCKeditor} this
46808          * @param {Object} the FCK object.
46809          */
46810         editorinit : true
46811     });
46812     
46813     
46814 };
46815 Roo.form.FCKeditor.editors = { };
46816 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46817 {
46818     //defaultAutoCreate : {
46819     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46820     //},
46821     // private
46822     /**
46823      * @cfg {Object} fck options - see fck manual for details.
46824      */
46825     fckconfig : false,
46826     
46827     /**
46828      * @cfg {Object} fck toolbar set (Basic or Default)
46829      */
46830     toolbarSet : 'Basic',
46831     /**
46832      * @cfg {Object} fck BasePath
46833      */ 
46834     basePath : '/fckeditor/',
46835     
46836     
46837     frame : false,
46838     
46839     value : '',
46840     
46841    
46842     onRender : function(ct, position)
46843     {
46844         if(!this.el){
46845             this.defaultAutoCreate = {
46846                 tag: "textarea",
46847                 style:"width:300px;height:60px;",
46848                 autocomplete: "new-password"
46849             };
46850         }
46851         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46852         /*
46853         if(this.grow){
46854             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46855             if(this.preventScrollbars){
46856                 this.el.setStyle("overflow", "hidden");
46857             }
46858             this.el.setHeight(this.growMin);
46859         }
46860         */
46861         //console.log('onrender' + this.getId() );
46862         Roo.form.FCKeditor.editors[this.getId()] = this;
46863          
46864
46865         this.replaceTextarea() ;
46866         
46867     },
46868     
46869     getEditor : function() {
46870         return this.fckEditor;
46871     },
46872     /**
46873      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46874      * @param {Mixed} value The value to set
46875      */
46876     
46877     
46878     setValue : function(value)
46879     {
46880         //console.log('setValue: ' + value);
46881         
46882         if(typeof(value) == 'undefined') { // not sure why this is happending...
46883             return;
46884         }
46885         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46886         
46887         //if(!this.el || !this.getEditor()) {
46888         //    this.value = value;
46889             //this.setValue.defer(100,this,[value]);    
46890         //    return;
46891         //} 
46892         
46893         if(!this.getEditor()) {
46894             return;
46895         }
46896         
46897         this.getEditor().SetData(value);
46898         
46899         //
46900
46901     },
46902
46903     /**
46904      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46905      * @return {Mixed} value The field value
46906      */
46907     getValue : function()
46908     {
46909         
46910         if (this.frame && this.frame.dom.style.display == 'none') {
46911             return Roo.form.FCKeditor.superclass.getValue.call(this);
46912         }
46913         
46914         if(!this.el || !this.getEditor()) {
46915            
46916            // this.getValue.defer(100,this); 
46917             return this.value;
46918         }
46919        
46920         
46921         var value=this.getEditor().GetData();
46922         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46923         return Roo.form.FCKeditor.superclass.getValue.call(this);
46924         
46925
46926     },
46927
46928     /**
46929      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46930      * @return {Mixed} value The field value
46931      */
46932     getRawValue : function()
46933     {
46934         if (this.frame && this.frame.dom.style.display == 'none') {
46935             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46936         }
46937         
46938         if(!this.el || !this.getEditor()) {
46939             //this.getRawValue.defer(100,this); 
46940             return this.value;
46941             return;
46942         }
46943         
46944         
46945         
46946         var value=this.getEditor().GetData();
46947         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46948         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46949          
46950     },
46951     
46952     setSize : function(w,h) {
46953         
46954         
46955         
46956         //if (this.frame && this.frame.dom.style.display == 'none') {
46957         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46958         //    return;
46959         //}
46960         //if(!this.el || !this.getEditor()) {
46961         //    this.setSize.defer(100,this, [w,h]); 
46962         //    return;
46963         //}
46964         
46965         
46966         
46967         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46968         
46969         this.frame.dom.setAttribute('width', w);
46970         this.frame.dom.setAttribute('height', h);
46971         this.frame.setSize(w,h);
46972         
46973     },
46974     
46975     toggleSourceEdit : function(value) {
46976         
46977       
46978          
46979         this.el.dom.style.display = value ? '' : 'none';
46980         this.frame.dom.style.display = value ?  'none' : '';
46981         
46982     },
46983     
46984     
46985     focus: function(tag)
46986     {
46987         if (this.frame.dom.style.display == 'none') {
46988             return Roo.form.FCKeditor.superclass.focus.call(this);
46989         }
46990         if(!this.el || !this.getEditor()) {
46991             this.focus.defer(100,this, [tag]); 
46992             return;
46993         }
46994         
46995         
46996         
46997         
46998         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46999         this.getEditor().Focus();
47000         if (tgs.length) {
47001             if (!this.getEditor().Selection.GetSelection()) {
47002                 this.focus.defer(100,this, [tag]); 
47003                 return;
47004             }
47005             
47006             
47007             var r = this.getEditor().EditorDocument.createRange();
47008             r.setStart(tgs[0],0);
47009             r.setEnd(tgs[0],0);
47010             this.getEditor().Selection.GetSelection().removeAllRanges();
47011             this.getEditor().Selection.GetSelection().addRange(r);
47012             this.getEditor().Focus();
47013         }
47014         
47015     },
47016     
47017     
47018     
47019     replaceTextarea : function()
47020     {
47021         if ( document.getElementById( this.getId() + '___Frame' ) )
47022             return ;
47023         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
47024         //{
47025             // We must check the elements firstly using the Id and then the name.
47026         var oTextarea = document.getElementById( this.getId() );
47027         
47028         var colElementsByName = document.getElementsByName( this.getId() ) ;
47029          
47030         oTextarea.style.display = 'none' ;
47031
47032         if ( oTextarea.tabIndex ) {            
47033             this.TabIndex = oTextarea.tabIndex ;
47034         }
47035         
47036         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
47037         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
47038         this.frame = Roo.get(this.getId() + '___Frame')
47039     },
47040     
47041     _getConfigHtml : function()
47042     {
47043         var sConfig = '' ;
47044
47045         for ( var o in this.fckconfig ) {
47046             sConfig += sConfig.length > 0  ? '&amp;' : '';
47047             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
47048         }
47049
47050         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
47051     },
47052     
47053     
47054     _getIFrameHtml : function()
47055     {
47056         var sFile = 'fckeditor.html' ;
47057         /* no idea what this is about..
47058         try
47059         {
47060             if ( (/fcksource=true/i).test( window.top.location.search ) )
47061                 sFile = 'fckeditor.original.html' ;
47062         }
47063         catch (e) { 
47064         */
47065
47066         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
47067         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
47068         
47069         
47070         var html = '<iframe id="' + this.getId() +
47071             '___Frame" src="' + sLink +
47072             '" width="' + this.width +
47073             '" height="' + this.height + '"' +
47074             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
47075             ' frameborder="0" scrolling="no"></iframe>' ;
47076
47077         return html ;
47078     },
47079     
47080     _insertHtmlBefore : function( html, element )
47081     {
47082         if ( element.insertAdjacentHTML )       {
47083             // IE
47084             element.insertAdjacentHTML( 'beforeBegin', html ) ;
47085         } else { // Gecko
47086             var oRange = document.createRange() ;
47087             oRange.setStartBefore( element ) ;
47088             var oFragment = oRange.createContextualFragment( html );
47089             element.parentNode.insertBefore( oFragment, element ) ;
47090         }
47091     }
47092     
47093     
47094   
47095     
47096     
47097     
47098     
47099
47100 });
47101
47102 //Roo.reg('fckeditor', Roo.form.FCKeditor);
47103
47104 function FCKeditor_OnComplete(editorInstance){
47105     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
47106     f.fckEditor = editorInstance;
47107     //console.log("loaded");
47108     f.fireEvent('editorinit', f, editorInstance);
47109
47110   
47111
47112  
47113
47114
47115
47116
47117
47118
47119
47120
47121
47122
47123
47124
47125
47126
47127
47128 //<script type="text/javascript">
47129 /**
47130  * @class Roo.form.GridField
47131  * @extends Roo.form.Field
47132  * Embed a grid (or editable grid into a form)
47133  * STATUS ALPHA
47134  * 
47135  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
47136  * it needs 
47137  * xgrid.store = Roo.data.Store
47138  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
47139  * xgrid.store.reader = Roo.data.JsonReader 
47140  * 
47141  * 
47142  * @constructor
47143  * Creates a new GridField
47144  * @param {Object} config Configuration options
47145  */
47146 Roo.form.GridField = function(config){
47147     Roo.form.GridField.superclass.constructor.call(this, config);
47148      
47149 };
47150
47151 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
47152     /**
47153      * @cfg {Number} width  - used to restrict width of grid..
47154      */
47155     width : 100,
47156     /**
47157      * @cfg {Number} height - used to restrict height of grid..
47158      */
47159     height : 50,
47160      /**
47161      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
47162          * 
47163          *}
47164      */
47165     xgrid : false, 
47166     /**
47167      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47168      * {tag: "input", type: "checkbox", autocomplete: "off"})
47169      */
47170    // defaultAutoCreate : { tag: 'div' },
47171     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
47172     /**
47173      * @cfg {String} addTitle Text to include for adding a title.
47174      */
47175     addTitle : false,
47176     //
47177     onResize : function(){
47178         Roo.form.Field.superclass.onResize.apply(this, arguments);
47179     },
47180
47181     initEvents : function(){
47182         // Roo.form.Checkbox.superclass.initEvents.call(this);
47183         // has no events...
47184        
47185     },
47186
47187
47188     getResizeEl : function(){
47189         return this.wrap;
47190     },
47191
47192     getPositionEl : function(){
47193         return this.wrap;
47194     },
47195
47196     // private
47197     onRender : function(ct, position){
47198         
47199         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
47200         var style = this.style;
47201         delete this.style;
47202         
47203         Roo.form.GridField.superclass.onRender.call(this, ct, position);
47204         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
47205         this.viewEl = this.wrap.createChild({ tag: 'div' });
47206         if (style) {
47207             this.viewEl.applyStyles(style);
47208         }
47209         if (this.width) {
47210             this.viewEl.setWidth(this.width);
47211         }
47212         if (this.height) {
47213             this.viewEl.setHeight(this.height);
47214         }
47215         //if(this.inputValue !== undefined){
47216         //this.setValue(this.value);
47217         
47218         
47219         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
47220         
47221         
47222         this.grid.render();
47223         this.grid.getDataSource().on('remove', this.refreshValue, this);
47224         this.grid.getDataSource().on('update', this.refreshValue, this);
47225         this.grid.on('afteredit', this.refreshValue, this);
47226  
47227     },
47228      
47229     
47230     /**
47231      * Sets the value of the item. 
47232      * @param {String} either an object  or a string..
47233      */
47234     setValue : function(v){
47235         //this.value = v;
47236         v = v || []; // empty set..
47237         // this does not seem smart - it really only affects memoryproxy grids..
47238         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
47239             var ds = this.grid.getDataSource();
47240             // assumes a json reader..
47241             var data = {}
47242             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
47243             ds.loadData( data);
47244         }
47245         // clear selection so it does not get stale.
47246         if (this.grid.sm) { 
47247             this.grid.sm.clearSelections();
47248         }
47249         
47250         Roo.form.GridField.superclass.setValue.call(this, v);
47251         this.refreshValue();
47252         // should load data in the grid really....
47253     },
47254     
47255     // private
47256     refreshValue: function() {
47257          var val = [];
47258         this.grid.getDataSource().each(function(r) {
47259             val.push(r.data);
47260         });
47261         this.el.dom.value = Roo.encode(val);
47262     }
47263     
47264      
47265     
47266     
47267 });/*
47268  * Based on:
47269  * Ext JS Library 1.1.1
47270  * Copyright(c) 2006-2007, Ext JS, LLC.
47271  *
47272  * Originally Released Under LGPL - original licence link has changed is not relivant.
47273  *
47274  * Fork - LGPL
47275  * <script type="text/javascript">
47276  */
47277 /**
47278  * @class Roo.form.DisplayField
47279  * @extends Roo.form.Field
47280  * A generic Field to display non-editable data.
47281  * @constructor
47282  * Creates a new Display Field item.
47283  * @param {Object} config Configuration options
47284  */
47285 Roo.form.DisplayField = function(config){
47286     Roo.form.DisplayField.superclass.constructor.call(this, config);
47287     
47288 };
47289
47290 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
47291     inputType:      'hidden',
47292     allowBlank:     true,
47293     readOnly:         true,
47294     
47295  
47296     /**
47297      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47298      */
47299     focusClass : undefined,
47300     /**
47301      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47302      */
47303     fieldClass: 'x-form-field',
47304     
47305      /**
47306      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
47307      */
47308     valueRenderer: undefined,
47309     
47310     width: 100,
47311     /**
47312      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47313      * {tag: "input", type: "checkbox", autocomplete: "off"})
47314      */
47315      
47316  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
47317
47318     onResize : function(){
47319         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
47320         
47321     },
47322
47323     initEvents : function(){
47324         // Roo.form.Checkbox.superclass.initEvents.call(this);
47325         // has no events...
47326        
47327     },
47328
47329
47330     getResizeEl : function(){
47331         return this.wrap;
47332     },
47333
47334     getPositionEl : function(){
47335         return this.wrap;
47336     },
47337
47338     // private
47339     onRender : function(ct, position){
47340         
47341         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
47342         //if(this.inputValue !== undefined){
47343         this.wrap = this.el.wrap();
47344         
47345         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
47346         
47347         if (this.bodyStyle) {
47348             this.viewEl.applyStyles(this.bodyStyle);
47349         }
47350         //this.viewEl.setStyle('padding', '2px');
47351         
47352         this.setValue(this.value);
47353         
47354     },
47355 /*
47356     // private
47357     initValue : Roo.emptyFn,
47358
47359   */
47360
47361         // private
47362     onClick : function(){
47363         
47364     },
47365
47366     /**
47367      * Sets the checked state of the checkbox.
47368      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
47369      */
47370     setValue : function(v){
47371         this.value = v;
47372         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
47373         // this might be called before we have a dom element..
47374         if (!this.viewEl) {
47375             return;
47376         }
47377         this.viewEl.dom.innerHTML = html;
47378         Roo.form.DisplayField.superclass.setValue.call(this, v);
47379
47380     }
47381 });/*
47382  * 
47383  * Licence- LGPL
47384  * 
47385  */
47386
47387 /**
47388  * @class Roo.form.DayPicker
47389  * @extends Roo.form.Field
47390  * A Day picker show [M] [T] [W] ....
47391  * @constructor
47392  * Creates a new Day Picker
47393  * @param {Object} config Configuration options
47394  */
47395 Roo.form.DayPicker= function(config){
47396     Roo.form.DayPicker.superclass.constructor.call(this, config);
47397      
47398 };
47399
47400 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
47401     /**
47402      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47403      */
47404     focusClass : undefined,
47405     /**
47406      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47407      */
47408     fieldClass: "x-form-field",
47409    
47410     /**
47411      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47412      * {tag: "input", type: "checkbox", autocomplete: "off"})
47413      */
47414     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
47415     
47416    
47417     actionMode : 'viewEl', 
47418     //
47419     // private
47420  
47421     inputType : 'hidden',
47422     
47423      
47424     inputElement: false, // real input element?
47425     basedOn: false, // ????
47426     
47427     isFormField: true, // not sure where this is needed!!!!
47428
47429     onResize : function(){
47430         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
47431         if(!this.boxLabel){
47432             this.el.alignTo(this.wrap, 'c-c');
47433         }
47434     },
47435
47436     initEvents : function(){
47437         Roo.form.Checkbox.superclass.initEvents.call(this);
47438         this.el.on("click", this.onClick,  this);
47439         this.el.on("change", this.onClick,  this);
47440     },
47441
47442
47443     getResizeEl : function(){
47444         return this.wrap;
47445     },
47446
47447     getPositionEl : function(){
47448         return this.wrap;
47449     },
47450
47451     
47452     // private
47453     onRender : function(ct, position){
47454         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
47455        
47456         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
47457         
47458         var r1 = '<table><tr>';
47459         var r2 = '<tr class="x-form-daypick-icons">';
47460         for (var i=0; i < 7; i++) {
47461             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
47462             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
47463         }
47464         
47465         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
47466         viewEl.select('img').on('click', this.onClick, this);
47467         this.viewEl = viewEl;   
47468         
47469         
47470         // this will not work on Chrome!!!
47471         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
47472         this.el.on('propertychange', this.setFromHidden,  this);  //ie
47473         
47474         
47475           
47476
47477     },
47478
47479     // private
47480     initValue : Roo.emptyFn,
47481
47482     /**
47483      * Returns the checked state of the checkbox.
47484      * @return {Boolean} True if checked, else false
47485      */
47486     getValue : function(){
47487         return this.el.dom.value;
47488         
47489     },
47490
47491         // private
47492     onClick : function(e){ 
47493         //this.setChecked(!this.checked);
47494         Roo.get(e.target).toggleClass('x-menu-item-checked');
47495         this.refreshValue();
47496         //if(this.el.dom.checked != this.checked){
47497         //    this.setValue(this.el.dom.checked);
47498        // }
47499     },
47500     
47501     // private
47502     refreshValue : function()
47503     {
47504         var val = '';
47505         this.viewEl.select('img',true).each(function(e,i,n)  {
47506             val += e.is(".x-menu-item-checked") ? String(n) : '';
47507         });
47508         this.setValue(val, true);
47509     },
47510
47511     /**
47512      * Sets the checked state of the checkbox.
47513      * On is always based on a string comparison between inputValue and the param.
47514      * @param {Boolean/String} value - the value to set 
47515      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47516      */
47517     setValue : function(v,suppressEvent){
47518         if (!this.el.dom) {
47519             return;
47520         }
47521         var old = this.el.dom.value ;
47522         this.el.dom.value = v;
47523         if (suppressEvent) {
47524             return ;
47525         }
47526          
47527         // update display..
47528         this.viewEl.select('img',true).each(function(e,i,n)  {
47529             
47530             var on = e.is(".x-menu-item-checked");
47531             var newv = v.indexOf(String(n)) > -1;
47532             if (on != newv) {
47533                 e.toggleClass('x-menu-item-checked');
47534             }
47535             
47536         });
47537         
47538         
47539         this.fireEvent('change', this, v, old);
47540         
47541         
47542     },
47543    
47544     // handle setting of hidden value by some other method!!?!?
47545     setFromHidden: function()
47546     {
47547         if(!this.el){
47548             return;
47549         }
47550         //console.log("SET FROM HIDDEN");
47551         //alert('setFrom hidden');
47552         this.setValue(this.el.dom.value);
47553     },
47554     
47555     onDestroy : function()
47556     {
47557         if(this.viewEl){
47558             Roo.get(this.viewEl).remove();
47559         }
47560          
47561         Roo.form.DayPicker.superclass.onDestroy.call(this);
47562     }
47563
47564 });/*
47565  * RooJS Library 1.1.1
47566  * Copyright(c) 2008-2011  Alan Knowles
47567  *
47568  * License - LGPL
47569  */
47570  
47571
47572 /**
47573  * @class Roo.form.ComboCheck
47574  * @extends Roo.form.ComboBox
47575  * A combobox for multiple select items.
47576  *
47577  * FIXME - could do with a reset button..
47578  * 
47579  * @constructor
47580  * Create a new ComboCheck
47581  * @param {Object} config Configuration options
47582  */
47583 Roo.form.ComboCheck = function(config){
47584     Roo.form.ComboCheck.superclass.constructor.call(this, config);
47585     // should verify some data...
47586     // like
47587     // hiddenName = required..
47588     // displayField = required
47589     // valudField == required
47590     var req= [ 'hiddenName', 'displayField', 'valueField' ];
47591     var _t = this;
47592     Roo.each(req, function(e) {
47593         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
47594             throw "Roo.form.ComboCheck : missing value for: " + e;
47595         }
47596     });
47597     
47598     
47599 };
47600
47601 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
47602      
47603      
47604     editable : false,
47605      
47606     selectedClass: 'x-menu-item-checked', 
47607     
47608     // private
47609     onRender : function(ct, position){
47610         var _t = this;
47611         
47612         
47613         
47614         if(!this.tpl){
47615             var cls = 'x-combo-list';
47616
47617             
47618             this.tpl =  new Roo.Template({
47619                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
47620                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47621                    '<span>{' + this.displayField + '}</span>' +
47622                     '</div>' 
47623                 
47624             });
47625         }
47626  
47627         
47628         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47629         this.view.singleSelect = false;
47630         this.view.multiSelect = true;
47631         this.view.toggleSelect = true;
47632         this.pageTb.add(new Roo.Toolbar.Fill(), {
47633             
47634             text: 'Done',
47635             handler: function()
47636             {
47637                 _t.collapse();
47638             }
47639         });
47640     },
47641     
47642     onViewOver : function(e, t){
47643         // do nothing...
47644         return;
47645         
47646     },
47647     
47648     onViewClick : function(doFocus,index){
47649         return;
47650         
47651     },
47652     select: function () {
47653         //Roo.log("SELECT CALLED");
47654     },
47655      
47656     selectByValue : function(xv, scrollIntoView){
47657         var ar = this.getValueArray();
47658         var sels = [];
47659         
47660         Roo.each(ar, function(v) {
47661             if(v === undefined || v === null){
47662                 return;
47663             }
47664             var r = this.findRecord(this.valueField, v);
47665             if(r){
47666                 sels.push(this.store.indexOf(r))
47667                 
47668             }
47669         },this);
47670         this.view.select(sels);
47671         return false;
47672     },
47673     
47674     
47675     
47676     onSelect : function(record, index){
47677        // Roo.log("onselect Called");
47678        // this is only called by the clear button now..
47679         this.view.clearSelections();
47680         this.setValue('[]');
47681         if (this.value != this.valueBefore) {
47682             this.fireEvent('change', this, this.value, this.valueBefore);
47683             this.valueBefore = this.value;
47684         }
47685     },
47686     getValueArray : function()
47687     {
47688         var ar = [] ;
47689         
47690         try {
47691             //Roo.log(this.value);
47692             if (typeof(this.value) == 'undefined') {
47693                 return [];
47694             }
47695             var ar = Roo.decode(this.value);
47696             return  ar instanceof Array ? ar : []; //?? valid?
47697             
47698         } catch(e) {
47699             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47700             return [];
47701         }
47702          
47703     },
47704     expand : function ()
47705     {
47706         
47707         Roo.form.ComboCheck.superclass.expand.call(this);
47708         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47709         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47710         
47711
47712     },
47713     
47714     collapse : function(){
47715         Roo.form.ComboCheck.superclass.collapse.call(this);
47716         var sl = this.view.getSelectedIndexes();
47717         var st = this.store;
47718         var nv = [];
47719         var tv = [];
47720         var r;
47721         Roo.each(sl, function(i) {
47722             r = st.getAt(i);
47723             nv.push(r.get(this.valueField));
47724         },this);
47725         this.setValue(Roo.encode(nv));
47726         if (this.value != this.valueBefore) {
47727
47728             this.fireEvent('change', this, this.value, this.valueBefore);
47729             this.valueBefore = this.value;
47730         }
47731         
47732     },
47733     
47734     setValue : function(v){
47735         // Roo.log(v);
47736         this.value = v;
47737         
47738         var vals = this.getValueArray();
47739         var tv = [];
47740         Roo.each(vals, function(k) {
47741             var r = this.findRecord(this.valueField, k);
47742             if(r){
47743                 tv.push(r.data[this.displayField]);
47744             }else if(this.valueNotFoundText !== undefined){
47745                 tv.push( this.valueNotFoundText );
47746             }
47747         },this);
47748        // Roo.log(tv);
47749         
47750         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47751         this.hiddenField.value = v;
47752         this.value = v;
47753     }
47754     
47755 });/*
47756  * Based on:
47757  * Ext JS Library 1.1.1
47758  * Copyright(c) 2006-2007, Ext JS, LLC.
47759  *
47760  * Originally Released Under LGPL - original licence link has changed is not relivant.
47761  *
47762  * Fork - LGPL
47763  * <script type="text/javascript">
47764  */
47765  
47766 /**
47767  * @class Roo.form.Signature
47768  * @extends Roo.form.Field
47769  * Signature field.  
47770  * @constructor
47771  * 
47772  * @param {Object} config Configuration options
47773  */
47774
47775 Roo.form.Signature = function(config){
47776     Roo.form.Signature.superclass.constructor.call(this, config);
47777     
47778     this.addEvents({// not in used??
47779          /**
47780          * @event confirm
47781          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47782              * @param {Roo.form.Signature} combo This combo box
47783              */
47784         'confirm' : true,
47785         /**
47786          * @event reset
47787          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47788              * @param {Roo.form.ComboBox} combo This combo box
47789              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47790              */
47791         'reset' : true
47792     });
47793 };
47794
47795 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47796     /**
47797      * @cfg {Object} labels Label to use when rendering a form.
47798      * defaults to 
47799      * labels : { 
47800      *      clear : "Clear",
47801      *      confirm : "Confirm"
47802      *  }
47803      */
47804     labels : { 
47805         clear : "Clear",
47806         confirm : "Confirm"
47807     },
47808     /**
47809      * @cfg {Number} width The signature panel width (defaults to 300)
47810      */
47811     width: 300,
47812     /**
47813      * @cfg {Number} height The signature panel height (defaults to 100)
47814      */
47815     height : 100,
47816     /**
47817      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47818      */
47819     allowBlank : false,
47820     
47821     //private
47822     // {Object} signPanel The signature SVG panel element (defaults to {})
47823     signPanel : {},
47824     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47825     isMouseDown : false,
47826     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47827     isConfirmed : false,
47828     // {String} signatureTmp SVG mapping string (defaults to empty string)
47829     signatureTmp : '',
47830     
47831     
47832     defaultAutoCreate : { // modified by initCompnoent..
47833         tag: "input",
47834         type:"hidden"
47835     },
47836
47837     // private
47838     onRender : function(ct, position){
47839         
47840         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47841         
47842         this.wrap = this.el.wrap({
47843             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47844         });
47845         
47846         this.createToolbar(this);
47847         this.signPanel = this.wrap.createChild({
47848                 tag: 'div',
47849                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47850             }, this.el
47851         );
47852             
47853         this.svgID = Roo.id();
47854         this.svgEl = this.signPanel.createChild({
47855               xmlns : 'http://www.w3.org/2000/svg',
47856               tag : 'svg',
47857               id : this.svgID + "-svg",
47858               width: this.width,
47859               height: this.height,
47860               viewBox: '0 0 '+this.width+' '+this.height,
47861               cn : [
47862                 {
47863                     tag: "rect",
47864                     id: this.svgID + "-svg-r",
47865                     width: this.width,
47866                     height: this.height,
47867                     fill: "#ffa"
47868                 },
47869                 {
47870                     tag: "line",
47871                     id: this.svgID + "-svg-l",
47872                     x1: "0", // start
47873                     y1: (this.height*0.8), // start set the line in 80% of height
47874                     x2: this.width, // end
47875                     y2: (this.height*0.8), // end set the line in 80% of height
47876                     'stroke': "#666",
47877                     'stroke-width': "1",
47878                     'stroke-dasharray': "3",
47879                     'shape-rendering': "crispEdges",
47880                     'pointer-events': "none"
47881                 },
47882                 {
47883                     tag: "path",
47884                     id: this.svgID + "-svg-p",
47885                     'stroke': "navy",
47886                     'stroke-width': "3",
47887                     'fill': "none",
47888                     'pointer-events': 'none'
47889                 }
47890               ]
47891         });
47892         this.createSVG();
47893         this.svgBox = this.svgEl.dom.getScreenCTM();
47894     },
47895     createSVG : function(){ 
47896         var svg = this.signPanel;
47897         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47898         var t = this;
47899
47900         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47901         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47902         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47903         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47904         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47905         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47906         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47907         
47908     },
47909     isTouchEvent : function(e){
47910         return e.type.match(/^touch/);
47911     },
47912     getCoords : function (e) {
47913         var pt    = this.svgEl.dom.createSVGPoint();
47914         pt.x = e.clientX; 
47915         pt.y = e.clientY;
47916         if (this.isTouchEvent(e)) {
47917             pt.x =  e.targetTouches[0].clientX 
47918             pt.y = e.targetTouches[0].clientY;
47919         }
47920         var a = this.svgEl.dom.getScreenCTM();
47921         var b = a.inverse();
47922         var mx = pt.matrixTransform(b);
47923         return mx.x + ',' + mx.y;
47924     },
47925     //mouse event headler 
47926     down : function (e) {
47927         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47928         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47929         
47930         this.isMouseDown = true;
47931         
47932         e.preventDefault();
47933     },
47934     move : function (e) {
47935         if (this.isMouseDown) {
47936             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
47937             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
47938         }
47939         
47940         e.preventDefault();
47941     },
47942     up : function (e) {
47943         this.isMouseDown = false;
47944         var sp = this.signatureTmp.split(' ');
47945         
47946         if(sp.length > 1){
47947             if(!sp[sp.length-2].match(/^L/)){
47948                 sp.pop();
47949                 sp.pop();
47950                 sp.push("");
47951                 this.signatureTmp = sp.join(" ");
47952             }
47953         }
47954         if(this.getValue() != this.signatureTmp){
47955             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47956             this.isConfirmed = false;
47957         }
47958         e.preventDefault();
47959     },
47960     
47961     /**
47962      * Protected method that will not generally be called directly. It
47963      * is called when the editor creates its toolbar. Override this method if you need to
47964      * add custom toolbar buttons.
47965      * @param {HtmlEditor} editor
47966      */
47967     createToolbar : function(editor){
47968          function btn(id, toggle, handler){
47969             var xid = fid + '-'+ id ;
47970             return {
47971                 id : xid,
47972                 cmd : id,
47973                 cls : 'x-btn-icon x-edit-'+id,
47974                 enableToggle:toggle !== false,
47975                 scope: editor, // was editor...
47976                 handler:handler||editor.relayBtnCmd,
47977                 clickEvent:'mousedown',
47978                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47979                 tabIndex:-1
47980             };
47981         }
47982         
47983         
47984         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47985         this.tb = tb;
47986         this.tb.add(
47987            {
47988                 cls : ' x-signature-btn x-signature-'+id,
47989                 scope: editor, // was editor...
47990                 handler: this.reset,
47991                 clickEvent:'mousedown',
47992                 text: this.labels.clear
47993             },
47994             {
47995                  xtype : 'Fill',
47996                  xns: Roo.Toolbar
47997             }, 
47998             {
47999                 cls : '  x-signature-btn x-signature-'+id,
48000                 scope: editor, // was editor...
48001                 handler: this.confirmHandler,
48002                 clickEvent:'mousedown',
48003                 text: this.labels.confirm
48004             }
48005         );
48006     
48007     },
48008     //public
48009     /**
48010      * when user is clicked confirm then show this image.....
48011      * 
48012      * @return {String} Image Data URI
48013      */
48014     getImageDataURI : function(){
48015         var svg = this.svgEl.dom.parentNode.innerHTML;
48016         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
48017         return src; 
48018     },
48019     /**
48020      * 
48021      * @return {Boolean} this.isConfirmed
48022      */
48023     getConfirmed : function(){
48024         return this.isConfirmed;
48025     },
48026     /**
48027      * 
48028      * @return {Number} this.width
48029      */
48030     getWidth : function(){
48031         return this.width;
48032     },
48033     /**
48034      * 
48035      * @return {Number} this.height
48036      */
48037     getHeight : function(){
48038         return this.height;
48039     },
48040     // private
48041     getSignature : function(){
48042         return this.signatureTmp;
48043     },
48044     // private
48045     reset : function(){
48046         this.signatureTmp = '';
48047         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48048         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
48049         this.isConfirmed = false;
48050         Roo.form.Signature.superclass.reset.call(this);
48051     },
48052     setSignature : function(s){
48053         this.signatureTmp = s;
48054         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48055         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
48056         this.setValue(s);
48057         this.isConfirmed = false;
48058         Roo.form.Signature.superclass.reset.call(this);
48059     }, 
48060     test : function(){
48061 //        Roo.log(this.signPanel.dom.contentWindow.up())
48062     },
48063     //private
48064     setConfirmed : function(){
48065         
48066         
48067         
48068 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
48069     },
48070     // private
48071     confirmHandler : function(){
48072         if(!this.getSignature()){
48073             return;
48074         }
48075         
48076         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
48077         this.setValue(this.getSignature());
48078         this.isConfirmed = true;
48079         
48080         this.fireEvent('confirm', this);
48081     },
48082     // private
48083     // Subclasses should provide the validation implementation by overriding this
48084     validateValue : function(value){
48085         if(this.allowBlank){
48086             return true;
48087         }
48088         
48089         if(this.isConfirmed){
48090             return true;
48091         }
48092         return false;
48093     }
48094 });/*
48095  * Based on:
48096  * Ext JS Library 1.1.1
48097  * Copyright(c) 2006-2007, Ext JS, LLC.
48098  *
48099  * Originally Released Under LGPL - original licence link has changed is not relivant.
48100  *
48101  * Fork - LGPL
48102  * <script type="text/javascript">
48103  */
48104  
48105
48106 /**
48107  * @class Roo.form.ComboBox
48108  * @extends Roo.form.TriggerField
48109  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
48110  * @constructor
48111  * Create a new ComboBox.
48112  * @param {Object} config Configuration options
48113  */
48114 Roo.form.Select = function(config){
48115     Roo.form.Select.superclass.constructor.call(this, config);
48116      
48117 };
48118
48119 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
48120     /**
48121      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
48122      */
48123     /**
48124      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
48125      * rendering into an Roo.Editor, defaults to false)
48126      */
48127     /**
48128      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
48129      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
48130      */
48131     /**
48132      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
48133      */
48134     /**
48135      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
48136      * the dropdown list (defaults to undefined, with no header element)
48137      */
48138
48139      /**
48140      * @cfg {String/Roo.Template} tpl The template to use to render the output
48141      */
48142      
48143     // private
48144     defaultAutoCreate : {tag: "select"  },
48145     /**
48146      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
48147      */
48148     listWidth: undefined,
48149     /**
48150      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
48151      * mode = 'remote' or 'text' if mode = 'local')
48152      */
48153     displayField: undefined,
48154     /**
48155      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
48156      * mode = 'remote' or 'value' if mode = 'local'). 
48157      * Note: use of a valueField requires the user make a selection
48158      * in order for a value to be mapped.
48159      */
48160     valueField: undefined,
48161     
48162     
48163     /**
48164      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
48165      * field's data value (defaults to the underlying DOM element's name)
48166      */
48167     hiddenName: undefined,
48168     /**
48169      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
48170      */
48171     listClass: '',
48172     /**
48173      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
48174      */
48175     selectedClass: 'x-combo-selected',
48176     /**
48177      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
48178      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
48179      * which displays a downward arrow icon).
48180      */
48181     triggerClass : 'x-form-arrow-trigger',
48182     /**
48183      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
48184      */
48185     shadow:'sides',
48186     /**
48187      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
48188      * anchor positions (defaults to 'tl-bl')
48189      */
48190     listAlign: 'tl-bl?',
48191     /**
48192      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
48193      */
48194     maxHeight: 300,
48195     /**
48196      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
48197      * query specified by the allQuery config option (defaults to 'query')
48198      */
48199     triggerAction: 'query',
48200     /**
48201      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
48202      * (defaults to 4, does not apply if editable = false)
48203      */
48204     minChars : 4,
48205     /**
48206      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
48207      * delay (typeAheadDelay) if it matches a known value (defaults to false)
48208      */
48209     typeAhead: false,
48210     /**
48211      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
48212      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
48213      */
48214     queryDelay: 500,
48215     /**
48216      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
48217      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
48218      */
48219     pageSize: 0,
48220     /**
48221      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
48222      * when editable = true (defaults to false)
48223      */
48224     selectOnFocus:false,
48225     /**
48226      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
48227      */
48228     queryParam: 'query',
48229     /**
48230      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
48231      * when mode = 'remote' (defaults to 'Loading...')
48232      */
48233     loadingText: 'Loading...',
48234     /**
48235      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
48236      */
48237     resizable: false,
48238     /**
48239      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
48240      */
48241     handleHeight : 8,
48242     /**
48243      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
48244      * traditional select (defaults to true)
48245      */
48246     editable: true,
48247     /**
48248      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
48249      */
48250     allQuery: '',
48251     /**
48252      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
48253      */
48254     mode: 'remote',
48255     /**
48256      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
48257      * listWidth has a higher value)
48258      */
48259     minListWidth : 70,
48260     /**
48261      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
48262      * allow the user to set arbitrary text into the field (defaults to false)
48263      */
48264     forceSelection:false,
48265     /**
48266      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
48267      * if typeAhead = true (defaults to 250)
48268      */
48269     typeAheadDelay : 250,
48270     /**
48271      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
48272      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
48273      */
48274     valueNotFoundText : undefined,
48275     
48276     /**
48277      * @cfg {String} defaultValue The value displayed after loading the store.
48278      */
48279     defaultValue: '',
48280     
48281     /**
48282      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
48283      */
48284     blockFocus : false,
48285     
48286     /**
48287      * @cfg {Boolean} disableClear Disable showing of clear button.
48288      */
48289     disableClear : false,
48290     /**
48291      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
48292      */
48293     alwaysQuery : false,
48294     
48295     //private
48296     addicon : false,
48297     editicon: false,
48298     
48299     // element that contains real text value.. (when hidden is used..)
48300      
48301     // private
48302     onRender : function(ct, position){
48303         Roo.form.Field.prototype.onRender.call(this, ct, position);
48304         
48305         if(this.store){
48306             this.store.on('beforeload', this.onBeforeLoad, this);
48307             this.store.on('load', this.onLoad, this);
48308             this.store.on('loadexception', this.onLoadException, this);
48309             this.store.load({});
48310         }
48311         
48312         
48313         
48314     },
48315
48316     // private
48317     initEvents : function(){
48318         //Roo.form.ComboBox.superclass.initEvents.call(this);
48319  
48320     },
48321
48322     onDestroy : function(){
48323        
48324         if(this.store){
48325             this.store.un('beforeload', this.onBeforeLoad, this);
48326             this.store.un('load', this.onLoad, this);
48327             this.store.un('loadexception', this.onLoadException, this);
48328         }
48329         //Roo.form.ComboBox.superclass.onDestroy.call(this);
48330     },
48331
48332     // private
48333     fireKey : function(e){
48334         if(e.isNavKeyPress() && !this.list.isVisible()){
48335             this.fireEvent("specialkey", this, e);
48336         }
48337     },
48338
48339     // private
48340     onResize: function(w, h){
48341         
48342         return; 
48343     
48344         
48345     },
48346
48347     /**
48348      * Allow or prevent the user from directly editing the field text.  If false is passed,
48349      * the user will only be able to select from the items defined in the dropdown list.  This method
48350      * is the runtime equivalent of setting the 'editable' config option at config time.
48351      * @param {Boolean} value True to allow the user to directly edit the field text
48352      */
48353     setEditable : function(value){
48354          
48355     },
48356
48357     // private
48358     onBeforeLoad : function(){
48359         
48360         Roo.log("Select before load");
48361         return;
48362     
48363         this.innerList.update(this.loadingText ?
48364                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
48365         //this.restrictHeight();
48366         this.selectedIndex = -1;
48367     },
48368
48369     // private
48370     onLoad : function(){
48371
48372     
48373         var dom = this.el.dom;
48374         dom.innerHTML = '';
48375          var od = dom.ownerDocument;
48376          
48377         if (this.emptyText) {
48378             var op = od.createElement('option');
48379             op.setAttribute('value', '');
48380             op.innerHTML = String.format('{0}', this.emptyText);
48381             dom.appendChild(op);
48382         }
48383         if(this.store.getCount() > 0){
48384            
48385             var vf = this.valueField;
48386             var df = this.displayField;
48387             this.store.data.each(function(r) {
48388                 // which colmsn to use... testing - cdoe / title..
48389                 var op = od.createElement('option');
48390                 op.setAttribute('value', r.data[vf]);
48391                 op.innerHTML = String.format('{0}', r.data[df]);
48392                 dom.appendChild(op);
48393             });
48394             if (typeof(this.defaultValue != 'undefined')) {
48395                 this.setValue(this.defaultValue);
48396             }
48397             
48398              
48399         }else{
48400             //this.onEmptyResults();
48401         }
48402         //this.el.focus();
48403     },
48404     // private
48405     onLoadException : function()
48406     {
48407         dom.innerHTML = '';
48408             
48409         Roo.log("Select on load exception");
48410         return;
48411     
48412         this.collapse();
48413         Roo.log(this.store.reader.jsonData);
48414         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
48415             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
48416         }
48417         
48418         
48419     },
48420     // private
48421     onTypeAhead : function(){
48422          
48423     },
48424
48425     // private
48426     onSelect : function(record, index){
48427         Roo.log('on select?');
48428         return;
48429         if(this.fireEvent('beforeselect', this, record, index) !== false){
48430             this.setFromData(index > -1 ? record.data : false);
48431             this.collapse();
48432             this.fireEvent('select', this, record, index);
48433         }
48434     },
48435
48436     /**
48437      * Returns the currently selected field value or empty string if no value is set.
48438      * @return {String} value The selected value
48439      */
48440     getValue : function(){
48441         var dom = this.el.dom;
48442         this.value = dom.options[dom.selectedIndex].value;
48443         return this.value;
48444         
48445     },
48446
48447     /**
48448      * Clears any text/value currently set in the field
48449      */
48450     clearValue : function(){
48451         this.value = '';
48452         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
48453         
48454     },
48455
48456     /**
48457      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
48458      * will be displayed in the field.  If the value does not match the data value of an existing item,
48459      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
48460      * Otherwise the field will be blank (although the value will still be set).
48461      * @param {String} value The value to match
48462      */
48463     setValue : function(v){
48464         var d = this.el.dom;
48465         for (var i =0; i < d.options.length;i++) {
48466             if (v == d.options[i].value) {
48467                 d.selectedIndex = i;
48468                 this.value = v;
48469                 return;
48470             }
48471         }
48472         this.clearValue();
48473     },
48474     /**
48475      * @property {Object} the last set data for the element
48476      */
48477     
48478     lastData : false,
48479     /**
48480      * Sets the value of the field based on a object which is related to the record format for the store.
48481      * @param {Object} value the value to set as. or false on reset?
48482      */
48483     setFromData : function(o){
48484         Roo.log('setfrom data?');
48485          
48486         
48487         
48488     },
48489     // private
48490     reset : function(){
48491         this.clearValue();
48492     },
48493     // private
48494     findRecord : function(prop, value){
48495         
48496         return false;
48497     
48498         var record;
48499         if(this.store.getCount() > 0){
48500             this.store.each(function(r){
48501                 if(r.data[prop] == value){
48502                     record = r;
48503                     return false;
48504                 }
48505                 return true;
48506             });
48507         }
48508         return record;
48509     },
48510     
48511     getName: function()
48512     {
48513         // returns hidden if it's set..
48514         if (!this.rendered) {return ''};
48515         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
48516         
48517     },
48518      
48519
48520     
48521
48522     // private
48523     onEmptyResults : function(){
48524         Roo.log('empty results');
48525         //this.collapse();
48526     },
48527
48528     /**
48529      * Returns true if the dropdown list is expanded, else false.
48530      */
48531     isExpanded : function(){
48532         return false;
48533     },
48534
48535     /**
48536      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
48537      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48538      * @param {String} value The data value of the item to select
48539      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48540      * selected item if it is not currently in view (defaults to true)
48541      * @return {Boolean} True if the value matched an item in the list, else false
48542      */
48543     selectByValue : function(v, scrollIntoView){
48544         Roo.log('select By Value');
48545         return false;
48546     
48547         if(v !== undefined && v !== null){
48548             var r = this.findRecord(this.valueField || this.displayField, v);
48549             if(r){
48550                 this.select(this.store.indexOf(r), scrollIntoView);
48551                 return true;
48552             }
48553         }
48554         return false;
48555     },
48556
48557     /**
48558      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
48559      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48560      * @param {Number} index The zero-based index of the list item to select
48561      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48562      * selected item if it is not currently in view (defaults to true)
48563      */
48564     select : function(index, scrollIntoView){
48565         Roo.log('select ');
48566         return  ;
48567         
48568         this.selectedIndex = index;
48569         this.view.select(index);
48570         if(scrollIntoView !== false){
48571             var el = this.view.getNode(index);
48572             if(el){
48573                 this.innerList.scrollChildIntoView(el, false);
48574             }
48575         }
48576     },
48577
48578       
48579
48580     // private
48581     validateBlur : function(){
48582         
48583         return;
48584         
48585     },
48586
48587     // private
48588     initQuery : function(){
48589         this.doQuery(this.getRawValue());
48590     },
48591
48592     // private
48593     doForce : function(){
48594         if(this.el.dom.value.length > 0){
48595             this.el.dom.value =
48596                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
48597              
48598         }
48599     },
48600
48601     /**
48602      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
48603      * query allowing the query action to be canceled if needed.
48604      * @param {String} query The SQL query to execute
48605      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
48606      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
48607      * saved in the current store (defaults to false)
48608      */
48609     doQuery : function(q, forceAll){
48610         
48611         Roo.log('doQuery?');
48612         if(q === undefined || q === null){
48613             q = '';
48614         }
48615         var qe = {
48616             query: q,
48617             forceAll: forceAll,
48618             combo: this,
48619             cancel:false
48620         };
48621         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48622             return false;
48623         }
48624         q = qe.query;
48625         forceAll = qe.forceAll;
48626         if(forceAll === true || (q.length >= this.minChars)){
48627             if(this.lastQuery != q || this.alwaysQuery){
48628                 this.lastQuery = q;
48629                 if(this.mode == 'local'){
48630                     this.selectedIndex = -1;
48631                     if(forceAll){
48632                         this.store.clearFilter();
48633                     }else{
48634                         this.store.filter(this.displayField, q);
48635                     }
48636                     this.onLoad();
48637                 }else{
48638                     this.store.baseParams[this.queryParam] = q;
48639                     this.store.load({
48640                         params: this.getParams(q)
48641                     });
48642                     this.expand();
48643                 }
48644             }else{
48645                 this.selectedIndex = -1;
48646                 this.onLoad();   
48647             }
48648         }
48649     },
48650
48651     // private
48652     getParams : function(q){
48653         var p = {};
48654         //p[this.queryParam] = q;
48655         if(this.pageSize){
48656             p.start = 0;
48657             p.limit = this.pageSize;
48658         }
48659         return p;
48660     },
48661
48662     /**
48663      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48664      */
48665     collapse : function(){
48666         
48667     },
48668
48669     // private
48670     collapseIf : function(e){
48671         
48672     },
48673
48674     /**
48675      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48676      */
48677     expand : function(){
48678         
48679     } ,
48680
48681     // private
48682      
48683
48684     /** 
48685     * @cfg {Boolean} grow 
48686     * @hide 
48687     */
48688     /** 
48689     * @cfg {Number} growMin 
48690     * @hide 
48691     */
48692     /** 
48693     * @cfg {Number} growMax 
48694     * @hide 
48695     */
48696     /**
48697      * @hide
48698      * @method autoSize
48699      */
48700     
48701     setWidth : function()
48702     {
48703         
48704     },
48705     getResizeEl : function(){
48706         return this.el;
48707     }
48708 });//<script type="text/javasscript">
48709  
48710
48711 /**
48712  * @class Roo.DDView
48713  * A DnD enabled version of Roo.View.
48714  * @param {Element/String} container The Element in which to create the View.
48715  * @param {String} tpl The template string used to create the markup for each element of the View
48716  * @param {Object} config The configuration properties. These include all the config options of
48717  * {@link Roo.View} plus some specific to this class.<br>
48718  * <p>
48719  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48720  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48721  * <p>
48722  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48723 .x-view-drag-insert-above {
48724         border-top:1px dotted #3366cc;
48725 }
48726 .x-view-drag-insert-below {
48727         border-bottom:1px dotted #3366cc;
48728 }
48729 </code></pre>
48730  * 
48731  */
48732  
48733 Roo.DDView = function(container, tpl, config) {
48734     Roo.DDView.superclass.constructor.apply(this, arguments);
48735     this.getEl().setStyle("outline", "0px none");
48736     this.getEl().unselectable();
48737     if (this.dragGroup) {
48738                 this.setDraggable(this.dragGroup.split(","));
48739     }
48740     if (this.dropGroup) {
48741                 this.setDroppable(this.dropGroup.split(","));
48742     }
48743     if (this.deletable) {
48744         this.setDeletable();
48745     }
48746     this.isDirtyFlag = false;
48747         this.addEvents({
48748                 "drop" : true
48749         });
48750 };
48751
48752 Roo.extend(Roo.DDView, Roo.View, {
48753 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48754 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48755 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48756 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48757
48758         isFormField: true,
48759
48760         reset: Roo.emptyFn,
48761         
48762         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48763
48764         validate: function() {
48765                 return true;
48766         },
48767         
48768         destroy: function() {
48769                 this.purgeListeners();
48770                 this.getEl.removeAllListeners();
48771                 this.getEl().remove();
48772                 if (this.dragZone) {
48773                         if (this.dragZone.destroy) {
48774                                 this.dragZone.destroy();
48775                         }
48776                 }
48777                 if (this.dropZone) {
48778                         if (this.dropZone.destroy) {
48779                                 this.dropZone.destroy();
48780                         }
48781                 }
48782         },
48783
48784 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48785         getName: function() {
48786                 return this.name;
48787         },
48788
48789 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48790         setValue: function(v) {
48791                 if (!this.store) {
48792                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48793                 }
48794                 var data = {};
48795                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48796                 this.store.proxy = new Roo.data.MemoryProxy(data);
48797                 this.store.load();
48798         },
48799
48800 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48801         getValue: function() {
48802                 var result = '(';
48803                 this.store.each(function(rec) {
48804                         result += rec.id + ',';
48805                 });
48806                 return result.substr(0, result.length - 1) + ')';
48807         },
48808         
48809         getIds: function() {
48810                 var i = 0, result = new Array(this.store.getCount());
48811                 this.store.each(function(rec) {
48812                         result[i++] = rec.id;
48813                 });
48814                 return result;
48815         },
48816         
48817         isDirty: function() {
48818                 return this.isDirtyFlag;
48819         },
48820
48821 /**
48822  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48823  *      whole Element becomes the target, and this causes the drop gesture to append.
48824  */
48825     getTargetFromEvent : function(e) {
48826                 var target = e.getTarget();
48827                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48828                 target = target.parentNode;
48829                 }
48830                 if (!target) {
48831                         target = this.el.dom.lastChild || this.el.dom;
48832                 }
48833                 return target;
48834     },
48835
48836 /**
48837  *      Create the drag data which consists of an object which has the property "ddel" as
48838  *      the drag proxy element. 
48839  */
48840     getDragData : function(e) {
48841         var target = this.findItemFromChild(e.getTarget());
48842                 if(target) {
48843                         this.handleSelection(e);
48844                         var selNodes = this.getSelectedNodes();
48845             var dragData = {
48846                 source: this,
48847                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48848                 nodes: selNodes,
48849                 records: []
48850                         };
48851                         var selectedIndices = this.getSelectedIndexes();
48852                         for (var i = 0; i < selectedIndices.length; i++) {
48853                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48854                         }
48855                         if (selNodes.length == 1) {
48856                                 dragData.ddel = target.cloneNode(true); // the div element
48857                         } else {
48858                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48859                                 div.className = 'multi-proxy';
48860                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48861                                         div.appendChild(selNodes[i].cloneNode(true));
48862                                 }
48863                                 dragData.ddel = div;
48864                         }
48865             //console.log(dragData)
48866             //console.log(dragData.ddel.innerHTML)
48867                         return dragData;
48868                 }
48869         //console.log('nodragData')
48870                 return false;
48871     },
48872     
48873 /**     Specify to which ddGroup items in this DDView may be dragged. */
48874     setDraggable: function(ddGroup) {
48875         if (ddGroup instanceof Array) {
48876                 Roo.each(ddGroup, this.setDraggable, this);
48877                 return;
48878         }
48879         if (this.dragZone) {
48880                 this.dragZone.addToGroup(ddGroup);
48881         } else {
48882                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48883                                 containerScroll: true,
48884                                 ddGroup: ddGroup 
48885
48886                         });
48887 //                      Draggability implies selection. DragZone's mousedown selects the element.
48888                         if (!this.multiSelect) { this.singleSelect = true; }
48889
48890 //                      Wire the DragZone's handlers up to methods in *this*
48891                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48892                 }
48893     },
48894
48895 /**     Specify from which ddGroup this DDView accepts drops. */
48896     setDroppable: function(ddGroup) {
48897         if (ddGroup instanceof Array) {
48898                 Roo.each(ddGroup, this.setDroppable, this);
48899                 return;
48900         }
48901         if (this.dropZone) {
48902                 this.dropZone.addToGroup(ddGroup);
48903         } else {
48904                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48905                                 containerScroll: true,
48906                                 ddGroup: ddGroup
48907                         });
48908
48909 //                      Wire the DropZone's handlers up to methods in *this*
48910                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48911                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48912                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48913                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48914                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48915                 }
48916     },
48917
48918 /**     Decide whether to drop above or below a View node. */
48919     getDropPoint : function(e, n, dd){
48920         if (n == this.el.dom) { return "above"; }
48921                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48922                 var c = t + (b - t) / 2;
48923                 var y = Roo.lib.Event.getPageY(e);
48924                 if(y <= c) {
48925                         return "above";
48926                 }else{
48927                         return "below";
48928                 }
48929     },
48930
48931     onNodeEnter : function(n, dd, e, data){
48932                 return false;
48933     },
48934     
48935     onNodeOver : function(n, dd, e, data){
48936                 var pt = this.getDropPoint(e, n, dd);
48937                 // set the insert point style on the target node
48938                 var dragElClass = this.dropNotAllowed;
48939                 if (pt) {
48940                         var targetElClass;
48941                         if (pt == "above"){
48942                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
48943                                 targetElClass = "x-view-drag-insert-above";
48944                         } else {
48945                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
48946                                 targetElClass = "x-view-drag-insert-below";
48947                         }
48948                         if (this.lastInsertClass != targetElClass){
48949                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
48950                                 this.lastInsertClass = targetElClass;
48951                         }
48952                 }
48953                 return dragElClass;
48954         },
48955
48956     onNodeOut : function(n, dd, e, data){
48957                 this.removeDropIndicators(n);
48958     },
48959
48960     onNodeDrop : function(n, dd, e, data){
48961         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
48962                 return false;
48963         }
48964         var pt = this.getDropPoint(e, n, dd);
48965                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
48966                 if (pt == "below") { insertAt++; }
48967                 for (var i = 0; i < data.records.length; i++) {
48968                         var r = data.records[i];
48969                         var dup = this.store.getById(r.id);
48970                         if (dup && (dd != this.dragZone)) {
48971                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48972                         } else {
48973                                 if (data.copy) {
48974                                         this.store.insert(insertAt++, r.copy());
48975                                 } else {
48976                                         data.source.isDirtyFlag = true;
48977                                         r.store.remove(r);
48978                                         this.store.insert(insertAt++, r);
48979                                 }
48980                                 this.isDirtyFlag = true;
48981                         }
48982                 }
48983                 this.dragZone.cachedTarget = null;
48984                 return true;
48985     },
48986
48987     removeDropIndicators : function(n){
48988                 if(n){
48989                         Roo.fly(n).removeClass([
48990                                 "x-view-drag-insert-above",
48991                                 "x-view-drag-insert-below"]);
48992                         this.lastInsertClass = "_noclass";
48993                 }
48994     },
48995
48996 /**
48997  *      Utility method. Add a delete option to the DDView's context menu.
48998  *      @param {String} imageUrl The URL of the "delete" icon image.
48999  */
49000         setDeletable: function(imageUrl) {
49001                 if (!this.singleSelect && !this.multiSelect) {
49002                         this.singleSelect = true;
49003                 }
49004                 var c = this.getContextMenu();
49005                 this.contextMenu.on("itemclick", function(item) {
49006                         switch (item.id) {
49007                                 case "delete":
49008                                         this.remove(this.getSelectedIndexes());
49009                                         break;
49010                         }
49011                 }, this);
49012                 this.contextMenu.add({
49013                         icon: imageUrl,
49014                         id: "delete",
49015                         text: 'Delete'
49016                 });
49017         },
49018         
49019 /**     Return the context menu for this DDView. */
49020         getContextMenu: function() {
49021                 if (!this.contextMenu) {
49022 //                      Create the View's context menu
49023                         this.contextMenu = new Roo.menu.Menu({
49024                                 id: this.id + "-contextmenu"
49025                         });
49026                         this.el.on("contextmenu", this.showContextMenu, this);
49027                 }
49028                 return this.contextMenu;
49029         },
49030         
49031         disableContextMenu: function() {
49032                 if (this.contextMenu) {
49033                         this.el.un("contextmenu", this.showContextMenu, this);
49034                 }
49035         },
49036
49037         showContextMenu: function(e, item) {
49038         item = this.findItemFromChild(e.getTarget());
49039                 if (item) {
49040                         e.stopEvent();
49041                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
49042                         this.contextMenu.showAt(e.getXY());
49043             }
49044     },
49045
49046 /**
49047  *      Remove {@link Roo.data.Record}s at the specified indices.
49048  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
49049  */
49050     remove: function(selectedIndices) {
49051                 selectedIndices = [].concat(selectedIndices);
49052                 for (var i = 0; i < selectedIndices.length; i++) {
49053                         var rec = this.store.getAt(selectedIndices[i]);
49054                         this.store.remove(rec);
49055                 }
49056     },
49057
49058 /**
49059  *      Double click fires the event, but also, if this is draggable, and there is only one other
49060  *      related DropZone, it transfers the selected node.
49061  */
49062     onDblClick : function(e){
49063         var item = this.findItemFromChild(e.getTarget());
49064         if(item){
49065             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
49066                 return false;
49067             }
49068             if (this.dragGroup) {
49069                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
49070                     while (targets.indexOf(this.dropZone) > -1) {
49071                             targets.remove(this.dropZone);
49072                                 }
49073                     if (targets.length == 1) {
49074                                         this.dragZone.cachedTarget = null;
49075                         var el = Roo.get(targets[0].getEl());
49076                         var box = el.getBox(true);
49077                         targets[0].onNodeDrop(el.dom, {
49078                                 target: el.dom,
49079                                 xy: [box.x, box.y + box.height - 1]
49080                         }, null, this.getDragData(e));
49081                     }
49082                 }
49083         }
49084     },
49085     
49086     handleSelection: function(e) {
49087                 this.dragZone.cachedTarget = null;
49088         var item = this.findItemFromChild(e.getTarget());
49089         if (!item) {
49090                 this.clearSelections(true);
49091                 return;
49092         }
49093                 if (item && (this.multiSelect || this.singleSelect)){
49094                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
49095                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
49096                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
49097                                 this.unselect(item);
49098                         } else {
49099                                 this.select(item, this.multiSelect && e.ctrlKey);
49100                                 this.lastSelection = item;
49101                         }
49102                 }
49103     },
49104
49105     onItemClick : function(item, index, e){
49106                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
49107                         return false;
49108                 }
49109                 return true;
49110     },
49111
49112     unselect : function(nodeInfo, suppressEvent){
49113                 var node = this.getNode(nodeInfo);
49114                 if(node && this.isSelected(node)){
49115                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
49116                                 Roo.fly(node).removeClass(this.selectedClass);
49117                                 this.selections.remove(node);
49118                                 if(!suppressEvent){
49119                                         this.fireEvent("selectionchange", this, this.selections);
49120                                 }
49121                         }
49122                 }
49123     }
49124 });
49125 /*
49126  * Based on:
49127  * Ext JS Library 1.1.1
49128  * Copyright(c) 2006-2007, Ext JS, LLC.
49129  *
49130  * Originally Released Under LGPL - original licence link has changed is not relivant.
49131  *
49132  * Fork - LGPL
49133  * <script type="text/javascript">
49134  */
49135  
49136 /**
49137  * @class Roo.LayoutManager
49138  * @extends Roo.util.Observable
49139  * Base class for layout managers.
49140  */
49141 Roo.LayoutManager = function(container, config){
49142     Roo.LayoutManager.superclass.constructor.call(this);
49143     this.el = Roo.get(container);
49144     // ie scrollbar fix
49145     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
49146         document.body.scroll = "no";
49147     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
49148         this.el.position('relative');
49149     }
49150     this.id = this.el.id;
49151     this.el.addClass("x-layout-container");
49152     /** false to disable window resize monitoring @type Boolean */
49153     this.monitorWindowResize = true;
49154     this.regions = {};
49155     this.addEvents({
49156         /**
49157          * @event layout
49158          * Fires when a layout is performed. 
49159          * @param {Roo.LayoutManager} this
49160          */
49161         "layout" : true,
49162         /**
49163          * @event regionresized
49164          * Fires when the user resizes a region. 
49165          * @param {Roo.LayoutRegion} region The resized region
49166          * @param {Number} newSize The new size (width for east/west, height for north/south)
49167          */
49168         "regionresized" : true,
49169         /**
49170          * @event regioncollapsed
49171          * Fires when a region is collapsed. 
49172          * @param {Roo.LayoutRegion} region The collapsed region
49173          */
49174         "regioncollapsed" : true,
49175         /**
49176          * @event regionexpanded
49177          * Fires when a region is expanded.  
49178          * @param {Roo.LayoutRegion} region The expanded region
49179          */
49180         "regionexpanded" : true
49181     });
49182     this.updating = false;
49183     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49184 };
49185
49186 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
49187     /**
49188      * Returns true if this layout is currently being updated
49189      * @return {Boolean}
49190      */
49191     isUpdating : function(){
49192         return this.updating; 
49193     },
49194     
49195     /**
49196      * Suspend the LayoutManager from doing auto-layouts while
49197      * making multiple add or remove calls
49198      */
49199     beginUpdate : function(){
49200         this.updating = true;    
49201     },
49202     
49203     /**
49204      * Restore auto-layouts and optionally disable the manager from performing a layout
49205      * @param {Boolean} noLayout true to disable a layout update 
49206      */
49207     endUpdate : function(noLayout){
49208         this.updating = false;
49209         if(!noLayout){
49210             this.layout();
49211         }    
49212     },
49213     
49214     layout: function(){
49215         
49216     },
49217     
49218     onRegionResized : function(region, newSize){
49219         this.fireEvent("regionresized", region, newSize);
49220         this.layout();
49221     },
49222     
49223     onRegionCollapsed : function(region){
49224         this.fireEvent("regioncollapsed", region);
49225     },
49226     
49227     onRegionExpanded : function(region){
49228         this.fireEvent("regionexpanded", region);
49229     },
49230         
49231     /**
49232      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
49233      * performs box-model adjustments.
49234      * @return {Object} The size as an object {width: (the width), height: (the height)}
49235      */
49236     getViewSize : function(){
49237         var size;
49238         if(this.el.dom != document.body){
49239             size = this.el.getSize();
49240         }else{
49241             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
49242         }
49243         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
49244         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
49245         return size;
49246     },
49247     
49248     /**
49249      * Returns the Element this layout is bound to.
49250      * @return {Roo.Element}
49251      */
49252     getEl : function(){
49253         return this.el;
49254     },
49255     
49256     /**
49257      * Returns the specified region.
49258      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
49259      * @return {Roo.LayoutRegion}
49260      */
49261     getRegion : function(target){
49262         return this.regions[target.toLowerCase()];
49263     },
49264     
49265     onWindowResize : function(){
49266         if(this.monitorWindowResize){
49267             this.layout();
49268         }
49269     }
49270 });/*
49271  * Based on:
49272  * Ext JS Library 1.1.1
49273  * Copyright(c) 2006-2007, Ext JS, LLC.
49274  *
49275  * Originally Released Under LGPL - original licence link has changed is not relivant.
49276  *
49277  * Fork - LGPL
49278  * <script type="text/javascript">
49279  */
49280 /**
49281  * @class Roo.BorderLayout
49282  * @extends Roo.LayoutManager
49283  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
49284  * please see: <br><br>
49285  * <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>
49286  * <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>
49287  * Example:
49288  <pre><code>
49289  var layout = new Roo.BorderLayout(document.body, {
49290     north: {
49291         initialSize: 25,
49292         titlebar: false
49293     },
49294     west: {
49295         split:true,
49296         initialSize: 200,
49297         minSize: 175,
49298         maxSize: 400,
49299         titlebar: true,
49300         collapsible: true
49301     },
49302     east: {
49303         split:true,
49304         initialSize: 202,
49305         minSize: 175,
49306         maxSize: 400,
49307         titlebar: true,
49308         collapsible: true
49309     },
49310     south: {
49311         split:true,
49312         initialSize: 100,
49313         minSize: 100,
49314         maxSize: 200,
49315         titlebar: true,
49316         collapsible: true
49317     },
49318     center: {
49319         titlebar: true,
49320         autoScroll:true,
49321         resizeTabs: true,
49322         minTabWidth: 50,
49323         preferredTabWidth: 150
49324     }
49325 });
49326
49327 // shorthand
49328 var CP = Roo.ContentPanel;
49329
49330 layout.beginUpdate();
49331 layout.add("north", new CP("north", "North"));
49332 layout.add("south", new CP("south", {title: "South", closable: true}));
49333 layout.add("west", new CP("west", {title: "West"}));
49334 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
49335 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
49336 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
49337 layout.getRegion("center").showPanel("center1");
49338 layout.endUpdate();
49339 </code></pre>
49340
49341 <b>The container the layout is rendered into can be either the body element or any other element.
49342 If it is not the body element, the container needs to either be an absolute positioned element,
49343 or you will need to add "position:relative" to the css of the container.  You will also need to specify
49344 the container size if it is not the body element.</b>
49345
49346 * @constructor
49347 * Create a new BorderLayout
49348 * @param {String/HTMLElement/Element} container The container this layout is bound to
49349 * @param {Object} config Configuration options
49350  */
49351 Roo.BorderLayout = function(container, config){
49352     config = config || {};
49353     Roo.BorderLayout.superclass.constructor.call(this, container, config);
49354     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
49355     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
49356         var target = this.factory.validRegions[i];
49357         if(config[target]){
49358             this.addRegion(target, config[target]);
49359         }
49360     }
49361 };
49362
49363 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
49364     /**
49365      * Creates and adds a new region if it doesn't already exist.
49366      * @param {String} target The target region key (north, south, east, west or center).
49367      * @param {Object} config The regions config object
49368      * @return {BorderLayoutRegion} The new region
49369      */
49370     addRegion : function(target, config){
49371         if(!this.regions[target]){
49372             var r = this.factory.create(target, this, config);
49373             this.bindRegion(target, r);
49374         }
49375         return this.regions[target];
49376     },
49377
49378     // private (kinda)
49379     bindRegion : function(name, r){
49380         this.regions[name] = r;
49381         r.on("visibilitychange", this.layout, this);
49382         r.on("paneladded", this.layout, this);
49383         r.on("panelremoved", this.layout, this);
49384         r.on("invalidated", this.layout, this);
49385         r.on("resized", this.onRegionResized, this);
49386         r.on("collapsed", this.onRegionCollapsed, this);
49387         r.on("expanded", this.onRegionExpanded, this);
49388     },
49389
49390     /**
49391      * Performs a layout update.
49392      */
49393     layout : function(){
49394         if(this.updating) return;
49395         var size = this.getViewSize();
49396         var w = size.width;
49397         var h = size.height;
49398         var centerW = w;
49399         var centerH = h;
49400         var centerY = 0;
49401         var centerX = 0;
49402         //var x = 0, y = 0;
49403
49404         var rs = this.regions;
49405         var north = rs["north"];
49406         var south = rs["south"]; 
49407         var west = rs["west"];
49408         var east = rs["east"];
49409         var center = rs["center"];
49410         //if(this.hideOnLayout){ // not supported anymore
49411             //c.el.setStyle("display", "none");
49412         //}
49413         if(north && north.isVisible()){
49414             var b = north.getBox();
49415             var m = north.getMargins();
49416             b.width = w - (m.left+m.right);
49417             b.x = m.left;
49418             b.y = m.top;
49419             centerY = b.height + b.y + m.bottom;
49420             centerH -= centerY;
49421             north.updateBox(this.safeBox(b));
49422         }
49423         if(south && south.isVisible()){
49424             var b = south.getBox();
49425             var m = south.getMargins();
49426             b.width = w - (m.left+m.right);
49427             b.x = m.left;
49428             var totalHeight = (b.height + m.top + m.bottom);
49429             b.y = h - totalHeight + m.top;
49430             centerH -= totalHeight;
49431             south.updateBox(this.safeBox(b));
49432         }
49433         if(west && west.isVisible()){
49434             var b = west.getBox();
49435             var m = west.getMargins();
49436             b.height = centerH - (m.top+m.bottom);
49437             b.x = m.left;
49438             b.y = centerY + m.top;
49439             var totalWidth = (b.width + m.left + m.right);
49440             centerX += totalWidth;
49441             centerW -= totalWidth;
49442             west.updateBox(this.safeBox(b));
49443         }
49444         if(east && east.isVisible()){
49445             var b = east.getBox();
49446             var m = east.getMargins();
49447             b.height = centerH - (m.top+m.bottom);
49448             var totalWidth = (b.width + m.left + m.right);
49449             b.x = w - totalWidth + m.left;
49450             b.y = centerY + m.top;
49451             centerW -= totalWidth;
49452             east.updateBox(this.safeBox(b));
49453         }
49454         if(center){
49455             var m = center.getMargins();
49456             var centerBox = {
49457                 x: centerX + m.left,
49458                 y: centerY + m.top,
49459                 width: centerW - (m.left+m.right),
49460                 height: centerH - (m.top+m.bottom)
49461             };
49462             //if(this.hideOnLayout){
49463                 //center.el.setStyle("display", "block");
49464             //}
49465             center.updateBox(this.safeBox(centerBox));
49466         }
49467         this.el.repaint();
49468         this.fireEvent("layout", this);
49469     },
49470
49471     // private
49472     safeBox : function(box){
49473         box.width = Math.max(0, box.width);
49474         box.height = Math.max(0, box.height);
49475         return box;
49476     },
49477
49478     /**
49479      * Adds a ContentPanel (or subclass) to this layout.
49480      * @param {String} target The target region key (north, south, east, west or center).
49481      * @param {Roo.ContentPanel} panel The panel to add
49482      * @return {Roo.ContentPanel} The added panel
49483      */
49484     add : function(target, panel){
49485          
49486         target = target.toLowerCase();
49487         return this.regions[target].add(panel);
49488     },
49489
49490     /**
49491      * Remove a ContentPanel (or subclass) to this layout.
49492      * @param {String} target The target region key (north, south, east, west or center).
49493      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
49494      * @return {Roo.ContentPanel} The removed panel
49495      */
49496     remove : function(target, panel){
49497         target = target.toLowerCase();
49498         return this.regions[target].remove(panel);
49499     },
49500
49501     /**
49502      * Searches all regions for a panel with the specified id
49503      * @param {String} panelId
49504      * @return {Roo.ContentPanel} The panel or null if it wasn't found
49505      */
49506     findPanel : function(panelId){
49507         var rs = this.regions;
49508         for(var target in rs){
49509             if(typeof rs[target] != "function"){
49510                 var p = rs[target].getPanel(panelId);
49511                 if(p){
49512                     return p;
49513                 }
49514             }
49515         }
49516         return null;
49517     },
49518
49519     /**
49520      * Searches all regions for a panel with the specified id and activates (shows) it.
49521      * @param {String/ContentPanel} panelId The panels id or the panel itself
49522      * @return {Roo.ContentPanel} The shown panel or null
49523      */
49524     showPanel : function(panelId) {
49525       var rs = this.regions;
49526       for(var target in rs){
49527          var r = rs[target];
49528          if(typeof r != "function"){
49529             if(r.hasPanel(panelId)){
49530                return r.showPanel(panelId);
49531             }
49532          }
49533       }
49534       return null;
49535    },
49536
49537    /**
49538      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
49539      * @param {Roo.state.Provider} provider (optional) An alternate state provider
49540      */
49541     restoreState : function(provider){
49542         if(!provider){
49543             provider = Roo.state.Manager;
49544         }
49545         var sm = new Roo.LayoutStateManager();
49546         sm.init(this, provider);
49547     },
49548
49549     /**
49550      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
49551      * object should contain properties for each region to add ContentPanels to, and each property's value should be
49552      * a valid ContentPanel config object.  Example:
49553      * <pre><code>
49554 // Create the main layout
49555 var layout = new Roo.BorderLayout('main-ct', {
49556     west: {
49557         split:true,
49558         minSize: 175,
49559         titlebar: true
49560     },
49561     center: {
49562         title:'Components'
49563     }
49564 }, 'main-ct');
49565
49566 // Create and add multiple ContentPanels at once via configs
49567 layout.batchAdd({
49568    west: {
49569        id: 'source-files',
49570        autoCreate:true,
49571        title:'Ext Source Files',
49572        autoScroll:true,
49573        fitToFrame:true
49574    },
49575    center : {
49576        el: cview,
49577        autoScroll:true,
49578        fitToFrame:true,
49579        toolbar: tb,
49580        resizeEl:'cbody'
49581    }
49582 });
49583 </code></pre>
49584      * @param {Object} regions An object containing ContentPanel configs by region name
49585      */
49586     batchAdd : function(regions){
49587         this.beginUpdate();
49588         for(var rname in regions){
49589             var lr = this.regions[rname];
49590             if(lr){
49591                 this.addTypedPanels(lr, regions[rname]);
49592             }
49593         }
49594         this.endUpdate();
49595     },
49596
49597     // private
49598     addTypedPanels : function(lr, ps){
49599         if(typeof ps == 'string'){
49600             lr.add(new Roo.ContentPanel(ps));
49601         }
49602         else if(ps instanceof Array){
49603             for(var i =0, len = ps.length; i < len; i++){
49604                 this.addTypedPanels(lr, ps[i]);
49605             }
49606         }
49607         else if(!ps.events){ // raw config?
49608             var el = ps.el;
49609             delete ps.el; // prevent conflict
49610             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
49611         }
49612         else {  // panel object assumed!
49613             lr.add(ps);
49614         }
49615     },
49616     /**
49617      * Adds a xtype elements to the layout.
49618      * <pre><code>
49619
49620 layout.addxtype({
49621        xtype : 'ContentPanel',
49622        region: 'west',
49623        items: [ .... ]
49624    }
49625 );
49626
49627 layout.addxtype({
49628         xtype : 'NestedLayoutPanel',
49629         region: 'west',
49630         layout: {
49631            center: { },
49632            west: { }   
49633         },
49634         items : [ ... list of content panels or nested layout panels.. ]
49635    }
49636 );
49637 </code></pre>
49638      * @param {Object} cfg Xtype definition of item to add.
49639      */
49640     addxtype : function(cfg)
49641     {
49642         // basically accepts a pannel...
49643         // can accept a layout region..!?!?
49644         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49645         
49646         if (!cfg.xtype.match(/Panel$/)) {
49647             return false;
49648         }
49649         var ret = false;
49650         
49651         if (typeof(cfg.region) == 'undefined') {
49652             Roo.log("Failed to add Panel, region was not set");
49653             Roo.log(cfg);
49654             return false;
49655         }
49656         var region = cfg.region;
49657         delete cfg.region;
49658         
49659           
49660         var xitems = [];
49661         if (cfg.items) {
49662             xitems = cfg.items;
49663             delete cfg.items;
49664         }
49665         var nb = false;
49666         
49667         switch(cfg.xtype) 
49668         {
49669             case 'ContentPanel':  // ContentPanel (el, cfg)
49670             case 'ScrollPanel':  // ContentPanel (el, cfg)
49671             case 'ViewPanel': 
49672                 if(cfg.autoCreate) {
49673                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49674                 } else {
49675                     var el = this.el.createChild();
49676                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49677                 }
49678                 
49679                 this.add(region, ret);
49680                 break;
49681             
49682             
49683             case 'TreePanel': // our new panel!
49684                 cfg.el = this.el.createChild();
49685                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49686                 this.add(region, ret);
49687                 break;
49688             
49689             case 'NestedLayoutPanel': 
49690                 // create a new Layout (which is  a Border Layout...
49691                 var el = this.el.createChild();
49692                 var clayout = cfg.layout;
49693                 delete cfg.layout;
49694                 clayout.items   = clayout.items  || [];
49695                 // replace this exitems with the clayout ones..
49696                 xitems = clayout.items;
49697                  
49698                 
49699                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49700                     cfg.background = false;
49701                 }
49702                 var layout = new Roo.BorderLayout(el, clayout);
49703                 
49704                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49705                 //console.log('adding nested layout panel '  + cfg.toSource());
49706                 this.add(region, ret);
49707                 nb = {}; /// find first...
49708                 break;
49709                 
49710             case 'GridPanel': 
49711             
49712                 // needs grid and region
49713                 
49714                 //var el = this.getRegion(region).el.createChild();
49715                 var el = this.el.createChild();
49716                 // create the grid first...
49717                 
49718                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49719                 delete cfg.grid;
49720                 if (region == 'center' && this.active ) {
49721                     cfg.background = false;
49722                 }
49723                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49724                 
49725                 this.add(region, ret);
49726                 if (cfg.background) {
49727                     ret.on('activate', function(gp) {
49728                         if (!gp.grid.rendered) {
49729                             gp.grid.render();
49730                         }
49731                     });
49732                 } else {
49733                     grid.render();
49734                 }
49735                 break;
49736            
49737            
49738            
49739                 
49740                 
49741                 
49742             default:
49743                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49744                     
49745                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49746                     this.add(region, ret);
49747                 } else {
49748                 
49749                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49750                     return null;
49751                 }
49752                 
49753              // GridPanel (grid, cfg)
49754             
49755         }
49756         this.beginUpdate();
49757         // add children..
49758         var region = '';
49759         var abn = {};
49760         Roo.each(xitems, function(i)  {
49761             region = nb && i.region ? i.region : false;
49762             
49763             var add = ret.addxtype(i);
49764            
49765             if (region) {
49766                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49767                 if (!i.background) {
49768                     abn[region] = nb[region] ;
49769                 }
49770             }
49771             
49772         });
49773         this.endUpdate();
49774
49775         // make the last non-background panel active..
49776         //if (nb) { Roo.log(abn); }
49777         if (nb) {
49778             
49779             for(var r in abn) {
49780                 region = this.getRegion(r);
49781                 if (region) {
49782                     // tried using nb[r], but it does not work..
49783                      
49784                     region.showPanel(abn[r]);
49785                    
49786                 }
49787             }
49788         }
49789         return ret;
49790         
49791     }
49792 });
49793
49794 /**
49795  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49796  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49797  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49798  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49799  * <pre><code>
49800 // shorthand
49801 var CP = Roo.ContentPanel;
49802
49803 var layout = Roo.BorderLayout.create({
49804     north: {
49805         initialSize: 25,
49806         titlebar: false,
49807         panels: [new CP("north", "North")]
49808     },
49809     west: {
49810         split:true,
49811         initialSize: 200,
49812         minSize: 175,
49813         maxSize: 400,
49814         titlebar: true,
49815         collapsible: true,
49816         panels: [new CP("west", {title: "West"})]
49817     },
49818     east: {
49819         split:true,
49820         initialSize: 202,
49821         minSize: 175,
49822         maxSize: 400,
49823         titlebar: true,
49824         collapsible: true,
49825         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49826     },
49827     south: {
49828         split:true,
49829         initialSize: 100,
49830         minSize: 100,
49831         maxSize: 200,
49832         titlebar: true,
49833         collapsible: true,
49834         panels: [new CP("south", {title: "South", closable: true})]
49835     },
49836     center: {
49837         titlebar: true,
49838         autoScroll:true,
49839         resizeTabs: true,
49840         minTabWidth: 50,
49841         preferredTabWidth: 150,
49842         panels: [
49843             new CP("center1", {title: "Close Me", closable: true}),
49844             new CP("center2", {title: "Center Panel", closable: false})
49845         ]
49846     }
49847 }, document.body);
49848
49849 layout.getRegion("center").showPanel("center1");
49850 </code></pre>
49851  * @param config
49852  * @param targetEl
49853  */
49854 Roo.BorderLayout.create = function(config, targetEl){
49855     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49856     layout.beginUpdate();
49857     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49858     for(var j = 0, jlen = regions.length; j < jlen; j++){
49859         var lr = regions[j];
49860         if(layout.regions[lr] && config[lr].panels){
49861             var r = layout.regions[lr];
49862             var ps = config[lr].panels;
49863             layout.addTypedPanels(r, ps);
49864         }
49865     }
49866     layout.endUpdate();
49867     return layout;
49868 };
49869
49870 // private
49871 Roo.BorderLayout.RegionFactory = {
49872     // private
49873     validRegions : ["north","south","east","west","center"],
49874
49875     // private
49876     create : function(target, mgr, config){
49877         target = target.toLowerCase();
49878         if(config.lightweight || config.basic){
49879             return new Roo.BasicLayoutRegion(mgr, config, target);
49880         }
49881         switch(target){
49882             case "north":
49883                 return new Roo.NorthLayoutRegion(mgr, config);
49884             case "south":
49885                 return new Roo.SouthLayoutRegion(mgr, config);
49886             case "east":
49887                 return new Roo.EastLayoutRegion(mgr, config);
49888             case "west":
49889                 return new Roo.WestLayoutRegion(mgr, config);
49890             case "center":
49891                 return new Roo.CenterLayoutRegion(mgr, config);
49892         }
49893         throw 'Layout region "'+target+'" not supported.';
49894     }
49895 };/*
49896  * Based on:
49897  * Ext JS Library 1.1.1
49898  * Copyright(c) 2006-2007, Ext JS, LLC.
49899  *
49900  * Originally Released Under LGPL - original licence link has changed is not relivant.
49901  *
49902  * Fork - LGPL
49903  * <script type="text/javascript">
49904  */
49905  
49906 /**
49907  * @class Roo.BasicLayoutRegion
49908  * @extends Roo.util.Observable
49909  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49910  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49911  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49912  */
49913 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49914     this.mgr = mgr;
49915     this.position  = pos;
49916     this.events = {
49917         /**
49918          * @scope Roo.BasicLayoutRegion
49919          */
49920         
49921         /**
49922          * @event beforeremove
49923          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49924          * @param {Roo.LayoutRegion} this
49925          * @param {Roo.ContentPanel} panel The panel
49926          * @param {Object} e The cancel event object
49927          */
49928         "beforeremove" : true,
49929         /**
49930          * @event invalidated
49931          * Fires when the layout for this region is changed.
49932          * @param {Roo.LayoutRegion} this
49933          */
49934         "invalidated" : true,
49935         /**
49936          * @event visibilitychange
49937          * Fires when this region is shown or hidden 
49938          * @param {Roo.LayoutRegion} this
49939          * @param {Boolean} visibility true or false
49940          */
49941         "visibilitychange" : true,
49942         /**
49943          * @event paneladded
49944          * Fires when a panel is added. 
49945          * @param {Roo.LayoutRegion} this
49946          * @param {Roo.ContentPanel} panel The panel
49947          */
49948         "paneladded" : true,
49949         /**
49950          * @event panelremoved
49951          * Fires when a panel is removed. 
49952          * @param {Roo.LayoutRegion} this
49953          * @param {Roo.ContentPanel} panel The panel
49954          */
49955         "panelremoved" : true,
49956         /**
49957          * @event collapsed
49958          * Fires when this region is collapsed.
49959          * @param {Roo.LayoutRegion} this
49960          */
49961         "collapsed" : true,
49962         /**
49963          * @event expanded
49964          * Fires when this region is expanded.
49965          * @param {Roo.LayoutRegion} this
49966          */
49967         "expanded" : true,
49968         /**
49969          * @event slideshow
49970          * Fires when this region is slid into view.
49971          * @param {Roo.LayoutRegion} this
49972          */
49973         "slideshow" : true,
49974         /**
49975          * @event slidehide
49976          * Fires when this region slides out of view. 
49977          * @param {Roo.LayoutRegion} this
49978          */
49979         "slidehide" : true,
49980         /**
49981          * @event panelactivated
49982          * Fires when a panel is activated. 
49983          * @param {Roo.LayoutRegion} this
49984          * @param {Roo.ContentPanel} panel The activated panel
49985          */
49986         "panelactivated" : true,
49987         /**
49988          * @event resized
49989          * Fires when the user resizes this region. 
49990          * @param {Roo.LayoutRegion} this
49991          * @param {Number} newSize The new size (width for east/west, height for north/south)
49992          */
49993         "resized" : true
49994     };
49995     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49996     this.panels = new Roo.util.MixedCollection();
49997     this.panels.getKey = this.getPanelId.createDelegate(this);
49998     this.box = null;
49999     this.activePanel = null;
50000     // ensure listeners are added...
50001     
50002     if (config.listeners || config.events) {
50003         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
50004             listeners : config.listeners || {},
50005             events : config.events || {}
50006         });
50007     }
50008     
50009     if(skipConfig !== true){
50010         this.applyConfig(config);
50011     }
50012 };
50013
50014 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
50015     getPanelId : function(p){
50016         return p.getId();
50017     },
50018     
50019     applyConfig : function(config){
50020         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50021         this.config = config;
50022         
50023     },
50024     
50025     /**
50026      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
50027      * the width, for horizontal (north, south) the height.
50028      * @param {Number} newSize The new width or height
50029      */
50030     resizeTo : function(newSize){
50031         var el = this.el ? this.el :
50032                  (this.activePanel ? this.activePanel.getEl() : null);
50033         if(el){
50034             switch(this.position){
50035                 case "east":
50036                 case "west":
50037                     el.setWidth(newSize);
50038                     this.fireEvent("resized", this, newSize);
50039                 break;
50040                 case "north":
50041                 case "south":
50042                     el.setHeight(newSize);
50043                     this.fireEvent("resized", this, newSize);
50044                 break;                
50045             }
50046         }
50047     },
50048     
50049     getBox : function(){
50050         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
50051     },
50052     
50053     getMargins : function(){
50054         return this.margins;
50055     },
50056     
50057     updateBox : function(box){
50058         this.box = box;
50059         var el = this.activePanel.getEl();
50060         el.dom.style.left = box.x + "px";
50061         el.dom.style.top = box.y + "px";
50062         this.activePanel.setSize(box.width, box.height);
50063     },
50064     
50065     /**
50066      * Returns the container element for this region.
50067      * @return {Roo.Element}
50068      */
50069     getEl : function(){
50070         return this.activePanel;
50071     },
50072     
50073     /**
50074      * Returns true if this region is currently visible.
50075      * @return {Boolean}
50076      */
50077     isVisible : function(){
50078         return this.activePanel ? true : false;
50079     },
50080     
50081     setActivePanel : function(panel){
50082         panel = this.getPanel(panel);
50083         if(this.activePanel && this.activePanel != panel){
50084             this.activePanel.setActiveState(false);
50085             this.activePanel.getEl().setLeftTop(-10000,-10000);
50086         }
50087         this.activePanel = panel;
50088         panel.setActiveState(true);
50089         if(this.box){
50090             panel.setSize(this.box.width, this.box.height);
50091         }
50092         this.fireEvent("panelactivated", this, panel);
50093         this.fireEvent("invalidated");
50094     },
50095     
50096     /**
50097      * Show the specified panel.
50098      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
50099      * @return {Roo.ContentPanel} The shown panel or null
50100      */
50101     showPanel : function(panel){
50102         if(panel = this.getPanel(panel)){
50103             this.setActivePanel(panel);
50104         }
50105         return panel;
50106     },
50107     
50108     /**
50109      * Get the active panel for this region.
50110      * @return {Roo.ContentPanel} The active panel or null
50111      */
50112     getActivePanel : function(){
50113         return this.activePanel;
50114     },
50115     
50116     /**
50117      * Add the passed ContentPanel(s)
50118      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50119      * @return {Roo.ContentPanel} The panel added (if only one was added)
50120      */
50121     add : function(panel){
50122         if(arguments.length > 1){
50123             for(var i = 0, len = arguments.length; i < len; i++) {
50124                 this.add(arguments[i]);
50125             }
50126             return null;
50127         }
50128         if(this.hasPanel(panel)){
50129             this.showPanel(panel);
50130             return panel;
50131         }
50132         var el = panel.getEl();
50133         if(el.dom.parentNode != this.mgr.el.dom){
50134             this.mgr.el.dom.appendChild(el.dom);
50135         }
50136         if(panel.setRegion){
50137             panel.setRegion(this);
50138         }
50139         this.panels.add(panel);
50140         el.setStyle("position", "absolute");
50141         if(!panel.background){
50142             this.setActivePanel(panel);
50143             if(this.config.initialSize && this.panels.getCount()==1){
50144                 this.resizeTo(this.config.initialSize);
50145             }
50146         }
50147         this.fireEvent("paneladded", this, panel);
50148         return panel;
50149     },
50150     
50151     /**
50152      * Returns true if the panel is in this region.
50153      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50154      * @return {Boolean}
50155      */
50156     hasPanel : function(panel){
50157         if(typeof panel == "object"){ // must be panel obj
50158             panel = panel.getId();
50159         }
50160         return this.getPanel(panel) ? true : false;
50161     },
50162     
50163     /**
50164      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50165      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50166      * @param {Boolean} preservePanel Overrides the config preservePanel option
50167      * @return {Roo.ContentPanel} The panel that was removed
50168      */
50169     remove : function(panel, preservePanel){
50170         panel = this.getPanel(panel);
50171         if(!panel){
50172             return null;
50173         }
50174         var e = {};
50175         this.fireEvent("beforeremove", this, panel, e);
50176         if(e.cancel === true){
50177             return null;
50178         }
50179         var panelId = panel.getId();
50180         this.panels.removeKey(panelId);
50181         return panel;
50182     },
50183     
50184     /**
50185      * Returns the panel specified or null if it's not in this region.
50186      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50187      * @return {Roo.ContentPanel}
50188      */
50189     getPanel : function(id){
50190         if(typeof id == "object"){ // must be panel obj
50191             return id;
50192         }
50193         return this.panels.get(id);
50194     },
50195     
50196     /**
50197      * Returns this regions position (north/south/east/west/center).
50198      * @return {String} 
50199      */
50200     getPosition: function(){
50201         return this.position;    
50202     }
50203 });/*
50204  * Based on:
50205  * Ext JS Library 1.1.1
50206  * Copyright(c) 2006-2007, Ext JS, LLC.
50207  *
50208  * Originally Released Under LGPL - original licence link has changed is not relivant.
50209  *
50210  * Fork - LGPL
50211  * <script type="text/javascript">
50212  */
50213  
50214 /**
50215  * @class Roo.LayoutRegion
50216  * @extends Roo.BasicLayoutRegion
50217  * This class represents a region in a layout manager.
50218  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
50219  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
50220  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
50221  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
50222  * @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})
50223  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
50224  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
50225  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
50226  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
50227  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
50228  * @cfg {String}    title           The title for the region (overrides panel titles)
50229  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
50230  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
50231  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
50232  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
50233  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
50234  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
50235  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
50236  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
50237  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
50238  * @cfg {Boolean}   showPin         True to show a pin button
50239  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
50240  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
50241  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
50242  * @cfg {Number}    width           For East/West panels
50243  * @cfg {Number}    height          For North/South panels
50244  * @cfg {Boolean}   split           To show the splitter
50245  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
50246  */
50247 Roo.LayoutRegion = function(mgr, config, pos){
50248     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
50249     var dh = Roo.DomHelper;
50250     /** This region's container element 
50251     * @type Roo.Element */
50252     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
50253     /** This region's title element 
50254     * @type Roo.Element */
50255
50256     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
50257         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
50258         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
50259     ]}, true);
50260     this.titleEl.enableDisplayMode();
50261     /** This region's title text element 
50262     * @type HTMLElement */
50263     this.titleTextEl = this.titleEl.dom.firstChild;
50264     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
50265     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
50266     this.closeBtn.enableDisplayMode();
50267     this.closeBtn.on("click", this.closeClicked, this);
50268     this.closeBtn.hide();
50269
50270     this.createBody(config);
50271     this.visible = true;
50272     this.collapsed = false;
50273
50274     if(config.hideWhenEmpty){
50275         this.hide();
50276         this.on("paneladded", this.validateVisibility, this);
50277         this.on("panelremoved", this.validateVisibility, this);
50278     }
50279     this.applyConfig(config);
50280 };
50281
50282 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
50283
50284     createBody : function(){
50285         /** This region's body element 
50286         * @type Roo.Element */
50287         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
50288     },
50289
50290     applyConfig : function(c){
50291         if(c.collapsible && this.position != "center" && !this.collapsedEl){
50292             var dh = Roo.DomHelper;
50293             if(c.titlebar !== false){
50294                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
50295                 this.collapseBtn.on("click", this.collapse, this);
50296                 this.collapseBtn.enableDisplayMode();
50297
50298                 if(c.showPin === true || this.showPin){
50299                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
50300                     this.stickBtn.enableDisplayMode();
50301                     this.stickBtn.on("click", this.expand, this);
50302                     this.stickBtn.hide();
50303                 }
50304             }
50305             /** This region's collapsed element
50306             * @type Roo.Element */
50307             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
50308                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
50309             ]}, true);
50310             if(c.floatable !== false){
50311                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
50312                this.collapsedEl.on("click", this.collapseClick, this);
50313             }
50314
50315             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
50316                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
50317                    id: "message", unselectable: "on", style:{"float":"left"}});
50318                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
50319              }
50320             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
50321             this.expandBtn.on("click", this.expand, this);
50322         }
50323         if(this.collapseBtn){
50324             this.collapseBtn.setVisible(c.collapsible == true);
50325         }
50326         this.cmargins = c.cmargins || this.cmargins ||
50327                          (this.position == "west" || this.position == "east" ?
50328                              {top: 0, left: 2, right:2, bottom: 0} :
50329                              {top: 2, left: 0, right:0, bottom: 2});
50330         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50331         this.bottomTabs = c.tabPosition != "top";
50332         this.autoScroll = c.autoScroll || false;
50333         if(this.autoScroll){
50334             this.bodyEl.setStyle("overflow", "auto");
50335         }else{
50336             this.bodyEl.setStyle("overflow", "hidden");
50337         }
50338         //if(c.titlebar !== false){
50339             if((!c.titlebar && !c.title) || c.titlebar === false){
50340                 this.titleEl.hide();
50341             }else{
50342                 this.titleEl.show();
50343                 if(c.title){
50344                     this.titleTextEl.innerHTML = c.title;
50345                 }
50346             }
50347         //}
50348         this.duration = c.duration || .30;
50349         this.slideDuration = c.slideDuration || .45;
50350         this.config = c;
50351         if(c.collapsed){
50352             this.collapse(true);
50353         }
50354         if(c.hidden){
50355             this.hide();
50356         }
50357     },
50358     /**
50359      * Returns true if this region is currently visible.
50360      * @return {Boolean}
50361      */
50362     isVisible : function(){
50363         return this.visible;
50364     },
50365
50366     /**
50367      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
50368      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
50369      */
50370     setCollapsedTitle : function(title){
50371         title = title || "&#160;";
50372         if(this.collapsedTitleTextEl){
50373             this.collapsedTitleTextEl.innerHTML = title;
50374         }
50375     },
50376
50377     getBox : function(){
50378         var b;
50379         if(!this.collapsed){
50380             b = this.el.getBox(false, true);
50381         }else{
50382             b = this.collapsedEl.getBox(false, true);
50383         }
50384         return b;
50385     },
50386
50387     getMargins : function(){
50388         return this.collapsed ? this.cmargins : this.margins;
50389     },
50390
50391     highlight : function(){
50392         this.el.addClass("x-layout-panel-dragover");
50393     },
50394
50395     unhighlight : function(){
50396         this.el.removeClass("x-layout-panel-dragover");
50397     },
50398
50399     updateBox : function(box){
50400         this.box = box;
50401         if(!this.collapsed){
50402             this.el.dom.style.left = box.x + "px";
50403             this.el.dom.style.top = box.y + "px";
50404             this.updateBody(box.width, box.height);
50405         }else{
50406             this.collapsedEl.dom.style.left = box.x + "px";
50407             this.collapsedEl.dom.style.top = box.y + "px";
50408             this.collapsedEl.setSize(box.width, box.height);
50409         }
50410         if(this.tabs){
50411             this.tabs.autoSizeTabs();
50412         }
50413     },
50414
50415     updateBody : function(w, h){
50416         if(w !== null){
50417             this.el.setWidth(w);
50418             w -= this.el.getBorderWidth("rl");
50419             if(this.config.adjustments){
50420                 w += this.config.adjustments[0];
50421             }
50422         }
50423         if(h !== null){
50424             this.el.setHeight(h);
50425             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
50426             h -= this.el.getBorderWidth("tb");
50427             if(this.config.adjustments){
50428                 h += this.config.adjustments[1];
50429             }
50430             this.bodyEl.setHeight(h);
50431             if(this.tabs){
50432                 h = this.tabs.syncHeight(h);
50433             }
50434         }
50435         if(this.panelSize){
50436             w = w !== null ? w : this.panelSize.width;
50437             h = h !== null ? h : this.panelSize.height;
50438         }
50439         if(this.activePanel){
50440             var el = this.activePanel.getEl();
50441             w = w !== null ? w : el.getWidth();
50442             h = h !== null ? h : el.getHeight();
50443             this.panelSize = {width: w, height: h};
50444             this.activePanel.setSize(w, h);
50445         }
50446         if(Roo.isIE && this.tabs){
50447             this.tabs.el.repaint();
50448         }
50449     },
50450
50451     /**
50452      * Returns the container element for this region.
50453      * @return {Roo.Element}
50454      */
50455     getEl : function(){
50456         return this.el;
50457     },
50458
50459     /**
50460      * Hides this region.
50461      */
50462     hide : function(){
50463         if(!this.collapsed){
50464             this.el.dom.style.left = "-2000px";
50465             this.el.hide();
50466         }else{
50467             this.collapsedEl.dom.style.left = "-2000px";
50468             this.collapsedEl.hide();
50469         }
50470         this.visible = false;
50471         this.fireEvent("visibilitychange", this, false);
50472     },
50473
50474     /**
50475      * Shows this region if it was previously hidden.
50476      */
50477     show : function(){
50478         if(!this.collapsed){
50479             this.el.show();
50480         }else{
50481             this.collapsedEl.show();
50482         }
50483         this.visible = true;
50484         this.fireEvent("visibilitychange", this, true);
50485     },
50486
50487     closeClicked : function(){
50488         if(this.activePanel){
50489             this.remove(this.activePanel);
50490         }
50491     },
50492
50493     collapseClick : function(e){
50494         if(this.isSlid){
50495            e.stopPropagation();
50496            this.slideIn();
50497         }else{
50498            e.stopPropagation();
50499            this.slideOut();
50500         }
50501     },
50502
50503     /**
50504      * Collapses this region.
50505      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
50506      */
50507     collapse : function(skipAnim){
50508         if(this.collapsed) return;
50509         this.collapsed = true;
50510         if(this.split){
50511             this.split.el.hide();
50512         }
50513         if(this.config.animate && skipAnim !== true){
50514             this.fireEvent("invalidated", this);
50515             this.animateCollapse();
50516         }else{
50517             this.el.setLocation(-20000,-20000);
50518             this.el.hide();
50519             this.collapsedEl.show();
50520             this.fireEvent("collapsed", this);
50521             this.fireEvent("invalidated", this);
50522         }
50523     },
50524
50525     animateCollapse : function(){
50526         // overridden
50527     },
50528
50529     /**
50530      * Expands this region if it was previously collapsed.
50531      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
50532      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
50533      */
50534     expand : function(e, skipAnim){
50535         if(e) e.stopPropagation();
50536         if(!this.collapsed || this.el.hasActiveFx()) return;
50537         if(this.isSlid){
50538             this.afterSlideIn();
50539             skipAnim = true;
50540         }
50541         this.collapsed = false;
50542         if(this.config.animate && skipAnim !== true){
50543             this.animateExpand();
50544         }else{
50545             this.el.show();
50546             if(this.split){
50547                 this.split.el.show();
50548             }
50549             this.collapsedEl.setLocation(-2000,-2000);
50550             this.collapsedEl.hide();
50551             this.fireEvent("invalidated", this);
50552             this.fireEvent("expanded", this);
50553         }
50554     },
50555
50556     animateExpand : function(){
50557         // overridden
50558     },
50559
50560     initTabs : function()
50561     {
50562         this.bodyEl.setStyle("overflow", "hidden");
50563         var ts = new Roo.TabPanel(
50564                 this.bodyEl.dom,
50565                 {
50566                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
50567                     disableTooltips: this.config.disableTabTips,
50568                     toolbar : this.config.toolbar
50569                 }
50570         );
50571         if(this.config.hideTabs){
50572             ts.stripWrap.setDisplayed(false);
50573         }
50574         this.tabs = ts;
50575         ts.resizeTabs = this.config.resizeTabs === true;
50576         ts.minTabWidth = this.config.minTabWidth || 40;
50577         ts.maxTabWidth = this.config.maxTabWidth || 250;
50578         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
50579         ts.monitorResize = false;
50580         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50581         ts.bodyEl.addClass('x-layout-tabs-body');
50582         this.panels.each(this.initPanelAsTab, this);
50583     },
50584
50585     initPanelAsTab : function(panel){
50586         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
50587                     this.config.closeOnTab && panel.isClosable());
50588         if(panel.tabTip !== undefined){
50589             ti.setTooltip(panel.tabTip);
50590         }
50591         ti.on("activate", function(){
50592               this.setActivePanel(panel);
50593         }, this);
50594         if(this.config.closeOnTab){
50595             ti.on("beforeclose", function(t, e){
50596                 e.cancel = true;
50597                 this.remove(panel);
50598             }, this);
50599         }
50600         return ti;
50601     },
50602
50603     updatePanelTitle : function(panel, title){
50604         if(this.activePanel == panel){
50605             this.updateTitle(title);
50606         }
50607         if(this.tabs){
50608             var ti = this.tabs.getTab(panel.getEl().id);
50609             ti.setText(title);
50610             if(panel.tabTip !== undefined){
50611                 ti.setTooltip(panel.tabTip);
50612             }
50613         }
50614     },
50615
50616     updateTitle : function(title){
50617         if(this.titleTextEl && !this.config.title){
50618             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
50619         }
50620     },
50621
50622     setActivePanel : function(panel){
50623         panel = this.getPanel(panel);
50624         if(this.activePanel && this.activePanel != panel){
50625             this.activePanel.setActiveState(false);
50626         }
50627         this.activePanel = panel;
50628         panel.setActiveState(true);
50629         if(this.panelSize){
50630             panel.setSize(this.panelSize.width, this.panelSize.height);
50631         }
50632         if(this.closeBtn){
50633             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50634         }
50635         this.updateTitle(panel.getTitle());
50636         if(this.tabs){
50637             this.fireEvent("invalidated", this);
50638         }
50639         this.fireEvent("panelactivated", this, panel);
50640     },
50641
50642     /**
50643      * Shows the specified panel.
50644      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50645      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50646      */
50647     showPanel : function(panel)
50648     {
50649         panel = this.getPanel(panel);
50650         if(panel){
50651             if(this.tabs){
50652                 var tab = this.tabs.getTab(panel.getEl().id);
50653                 if(tab.isHidden()){
50654                     this.tabs.unhideTab(tab.id);
50655                 }
50656                 tab.activate();
50657             }else{
50658                 this.setActivePanel(panel);
50659             }
50660         }
50661         return panel;
50662     },
50663
50664     /**
50665      * Get the active panel for this region.
50666      * @return {Roo.ContentPanel} The active panel or null
50667      */
50668     getActivePanel : function(){
50669         return this.activePanel;
50670     },
50671
50672     validateVisibility : function(){
50673         if(this.panels.getCount() < 1){
50674             this.updateTitle("&#160;");
50675             this.closeBtn.hide();
50676             this.hide();
50677         }else{
50678             if(!this.isVisible()){
50679                 this.show();
50680             }
50681         }
50682     },
50683
50684     /**
50685      * Adds the passed ContentPanel(s) to this region.
50686      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50687      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50688      */
50689     add : function(panel){
50690         if(arguments.length > 1){
50691             for(var i = 0, len = arguments.length; i < len; i++) {
50692                 this.add(arguments[i]);
50693             }
50694             return null;
50695         }
50696         if(this.hasPanel(panel)){
50697             this.showPanel(panel);
50698             return panel;
50699         }
50700         panel.setRegion(this);
50701         this.panels.add(panel);
50702         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50703             this.bodyEl.dom.appendChild(panel.getEl().dom);
50704             if(panel.background !== true){
50705                 this.setActivePanel(panel);
50706             }
50707             this.fireEvent("paneladded", this, panel);
50708             return panel;
50709         }
50710         if(!this.tabs){
50711             this.initTabs();
50712         }else{
50713             this.initPanelAsTab(panel);
50714         }
50715         if(panel.background !== true){
50716             this.tabs.activate(panel.getEl().id);
50717         }
50718         this.fireEvent("paneladded", this, panel);
50719         return panel;
50720     },
50721
50722     /**
50723      * Hides the tab for the specified panel.
50724      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50725      */
50726     hidePanel : function(panel){
50727         if(this.tabs && (panel = this.getPanel(panel))){
50728             this.tabs.hideTab(panel.getEl().id);
50729         }
50730     },
50731
50732     /**
50733      * Unhides the tab for a previously hidden panel.
50734      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50735      */
50736     unhidePanel : function(panel){
50737         if(this.tabs && (panel = this.getPanel(panel))){
50738             this.tabs.unhideTab(panel.getEl().id);
50739         }
50740     },
50741
50742     clearPanels : function(){
50743         while(this.panels.getCount() > 0){
50744              this.remove(this.panels.first());
50745         }
50746     },
50747
50748     /**
50749      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50750      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50751      * @param {Boolean} preservePanel Overrides the config preservePanel option
50752      * @return {Roo.ContentPanel} The panel that was removed
50753      */
50754     remove : function(panel, preservePanel){
50755         panel = this.getPanel(panel);
50756         if(!panel){
50757             return null;
50758         }
50759         var e = {};
50760         this.fireEvent("beforeremove", this, panel, e);
50761         if(e.cancel === true){
50762             return null;
50763         }
50764         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50765         var panelId = panel.getId();
50766         this.panels.removeKey(panelId);
50767         if(preservePanel){
50768             document.body.appendChild(panel.getEl().dom);
50769         }
50770         if(this.tabs){
50771             this.tabs.removeTab(panel.getEl().id);
50772         }else if (!preservePanel){
50773             this.bodyEl.dom.removeChild(panel.getEl().dom);
50774         }
50775         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50776             var p = this.panels.first();
50777             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50778             tempEl.appendChild(p.getEl().dom);
50779             this.bodyEl.update("");
50780             this.bodyEl.dom.appendChild(p.getEl().dom);
50781             tempEl = null;
50782             this.updateTitle(p.getTitle());
50783             this.tabs = null;
50784             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50785             this.setActivePanel(p);
50786         }
50787         panel.setRegion(null);
50788         if(this.activePanel == panel){
50789             this.activePanel = null;
50790         }
50791         if(this.config.autoDestroy !== false && preservePanel !== true){
50792             try{panel.destroy();}catch(e){}
50793         }
50794         this.fireEvent("panelremoved", this, panel);
50795         return panel;
50796     },
50797
50798     /**
50799      * Returns the TabPanel component used by this region
50800      * @return {Roo.TabPanel}
50801      */
50802     getTabs : function(){
50803         return this.tabs;
50804     },
50805
50806     createTool : function(parentEl, className){
50807         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50808             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50809         btn.addClassOnOver("x-layout-tools-button-over");
50810         return btn;
50811     }
50812 });/*
50813  * Based on:
50814  * Ext JS Library 1.1.1
50815  * Copyright(c) 2006-2007, Ext JS, LLC.
50816  *
50817  * Originally Released Under LGPL - original licence link has changed is not relivant.
50818  *
50819  * Fork - LGPL
50820  * <script type="text/javascript">
50821  */
50822  
50823
50824
50825 /**
50826  * @class Roo.SplitLayoutRegion
50827  * @extends Roo.LayoutRegion
50828  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50829  */
50830 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50831     this.cursor = cursor;
50832     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50833 };
50834
50835 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50836     splitTip : "Drag to resize.",
50837     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50838     useSplitTips : false,
50839
50840     applyConfig : function(config){
50841         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50842         if(config.split){
50843             if(!this.split){
50844                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50845                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50846                 /** The SplitBar for this region 
50847                 * @type Roo.SplitBar */
50848                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50849                 this.split.on("moved", this.onSplitMove, this);
50850                 this.split.useShim = config.useShim === true;
50851                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50852                 if(this.useSplitTips){
50853                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50854                 }
50855                 if(config.collapsible){
50856                     this.split.el.on("dblclick", this.collapse,  this);
50857                 }
50858             }
50859             if(typeof config.minSize != "undefined"){
50860                 this.split.minSize = config.minSize;
50861             }
50862             if(typeof config.maxSize != "undefined"){
50863                 this.split.maxSize = config.maxSize;
50864             }
50865             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50866                 this.hideSplitter();
50867             }
50868         }
50869     },
50870
50871     getHMaxSize : function(){
50872          var cmax = this.config.maxSize || 10000;
50873          var center = this.mgr.getRegion("center");
50874          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50875     },
50876
50877     getVMaxSize : function(){
50878          var cmax = this.config.maxSize || 10000;
50879          var center = this.mgr.getRegion("center");
50880          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50881     },
50882
50883     onSplitMove : function(split, newSize){
50884         this.fireEvent("resized", this, newSize);
50885     },
50886     
50887     /** 
50888      * Returns the {@link Roo.SplitBar} for this region.
50889      * @return {Roo.SplitBar}
50890      */
50891     getSplitBar : function(){
50892         return this.split;
50893     },
50894     
50895     hide : function(){
50896         this.hideSplitter();
50897         Roo.SplitLayoutRegion.superclass.hide.call(this);
50898     },
50899
50900     hideSplitter : function(){
50901         if(this.split){
50902             this.split.el.setLocation(-2000,-2000);
50903             this.split.el.hide();
50904         }
50905     },
50906
50907     show : function(){
50908         if(this.split){
50909             this.split.el.show();
50910         }
50911         Roo.SplitLayoutRegion.superclass.show.call(this);
50912     },
50913     
50914     beforeSlide: function(){
50915         if(Roo.isGecko){// firefox overflow auto bug workaround
50916             this.bodyEl.clip();
50917             if(this.tabs) this.tabs.bodyEl.clip();
50918             if(this.activePanel){
50919                 this.activePanel.getEl().clip();
50920                 
50921                 if(this.activePanel.beforeSlide){
50922                     this.activePanel.beforeSlide();
50923                 }
50924             }
50925         }
50926     },
50927     
50928     afterSlide : function(){
50929         if(Roo.isGecko){// firefox overflow auto bug workaround
50930             this.bodyEl.unclip();
50931             if(this.tabs) this.tabs.bodyEl.unclip();
50932             if(this.activePanel){
50933                 this.activePanel.getEl().unclip();
50934                 if(this.activePanel.afterSlide){
50935                     this.activePanel.afterSlide();
50936                 }
50937             }
50938         }
50939     },
50940
50941     initAutoHide : function(){
50942         if(this.autoHide !== false){
50943             if(!this.autoHideHd){
50944                 var st = new Roo.util.DelayedTask(this.slideIn, this);
50945                 this.autoHideHd = {
50946                     "mouseout": function(e){
50947                         if(!e.within(this.el, true)){
50948                             st.delay(500);
50949                         }
50950                     },
50951                     "mouseover" : function(e){
50952                         st.cancel();
50953                     },
50954                     scope : this
50955                 };
50956             }
50957             this.el.on(this.autoHideHd);
50958         }
50959     },
50960
50961     clearAutoHide : function(){
50962         if(this.autoHide !== false){
50963             this.el.un("mouseout", this.autoHideHd.mouseout);
50964             this.el.un("mouseover", this.autoHideHd.mouseover);
50965         }
50966     },
50967
50968     clearMonitor : function(){
50969         Roo.get(document).un("click", this.slideInIf, this);
50970     },
50971
50972     // these names are backwards but not changed for compat
50973     slideOut : function(){
50974         if(this.isSlid || this.el.hasActiveFx()){
50975             return;
50976         }
50977         this.isSlid = true;
50978         if(this.collapseBtn){
50979             this.collapseBtn.hide();
50980         }
50981         this.closeBtnState = this.closeBtn.getStyle('display');
50982         this.closeBtn.hide();
50983         if(this.stickBtn){
50984             this.stickBtn.show();
50985         }
50986         this.el.show();
50987         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50988         this.beforeSlide();
50989         this.el.setStyle("z-index", 10001);
50990         this.el.slideIn(this.getSlideAnchor(), {
50991             callback: function(){
50992                 this.afterSlide();
50993                 this.initAutoHide();
50994                 Roo.get(document).on("click", this.slideInIf, this);
50995                 this.fireEvent("slideshow", this);
50996             },
50997             scope: this,
50998             block: true
50999         });
51000     },
51001
51002     afterSlideIn : function(){
51003         this.clearAutoHide();
51004         this.isSlid = false;
51005         this.clearMonitor();
51006         this.el.setStyle("z-index", "");
51007         if(this.collapseBtn){
51008             this.collapseBtn.show();
51009         }
51010         this.closeBtn.setStyle('display', this.closeBtnState);
51011         if(this.stickBtn){
51012             this.stickBtn.hide();
51013         }
51014         this.fireEvent("slidehide", this);
51015     },
51016
51017     slideIn : function(cb){
51018         if(!this.isSlid || this.el.hasActiveFx()){
51019             Roo.callback(cb);
51020             return;
51021         }
51022         this.isSlid = false;
51023         this.beforeSlide();
51024         this.el.slideOut(this.getSlideAnchor(), {
51025             callback: function(){
51026                 this.el.setLeftTop(-10000, -10000);
51027                 this.afterSlide();
51028                 this.afterSlideIn();
51029                 Roo.callback(cb);
51030             },
51031             scope: this,
51032             block: true
51033         });
51034     },
51035     
51036     slideInIf : function(e){
51037         if(!e.within(this.el)){
51038             this.slideIn();
51039         }
51040     },
51041
51042     animateCollapse : function(){
51043         this.beforeSlide();
51044         this.el.setStyle("z-index", 20000);
51045         var anchor = this.getSlideAnchor();
51046         this.el.slideOut(anchor, {
51047             callback : function(){
51048                 this.el.setStyle("z-index", "");
51049                 this.collapsedEl.slideIn(anchor, {duration:.3});
51050                 this.afterSlide();
51051                 this.el.setLocation(-10000,-10000);
51052                 this.el.hide();
51053                 this.fireEvent("collapsed", this);
51054             },
51055             scope: this,
51056             block: true
51057         });
51058     },
51059
51060     animateExpand : function(){
51061         this.beforeSlide();
51062         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
51063         this.el.setStyle("z-index", 20000);
51064         this.collapsedEl.hide({
51065             duration:.1
51066         });
51067         this.el.slideIn(this.getSlideAnchor(), {
51068             callback : function(){
51069                 this.el.setStyle("z-index", "");
51070                 this.afterSlide();
51071                 if(this.split){
51072                     this.split.el.show();
51073                 }
51074                 this.fireEvent("invalidated", this);
51075                 this.fireEvent("expanded", this);
51076             },
51077             scope: this,
51078             block: true
51079         });
51080     },
51081
51082     anchors : {
51083         "west" : "left",
51084         "east" : "right",
51085         "north" : "top",
51086         "south" : "bottom"
51087     },
51088
51089     sanchors : {
51090         "west" : "l",
51091         "east" : "r",
51092         "north" : "t",
51093         "south" : "b"
51094     },
51095
51096     canchors : {
51097         "west" : "tl-tr",
51098         "east" : "tr-tl",
51099         "north" : "tl-bl",
51100         "south" : "bl-tl"
51101     },
51102
51103     getAnchor : function(){
51104         return this.anchors[this.position];
51105     },
51106
51107     getCollapseAnchor : function(){
51108         return this.canchors[this.position];
51109     },
51110
51111     getSlideAnchor : function(){
51112         return this.sanchors[this.position];
51113     },
51114
51115     getAlignAdj : function(){
51116         var cm = this.cmargins;
51117         switch(this.position){
51118             case "west":
51119                 return [0, 0];
51120             break;
51121             case "east":
51122                 return [0, 0];
51123             break;
51124             case "north":
51125                 return [0, 0];
51126             break;
51127             case "south":
51128                 return [0, 0];
51129             break;
51130         }
51131     },
51132
51133     getExpandAdj : function(){
51134         var c = this.collapsedEl, cm = this.cmargins;
51135         switch(this.position){
51136             case "west":
51137                 return [-(cm.right+c.getWidth()+cm.left), 0];
51138             break;
51139             case "east":
51140                 return [cm.right+c.getWidth()+cm.left, 0];
51141             break;
51142             case "north":
51143                 return [0, -(cm.top+cm.bottom+c.getHeight())];
51144             break;
51145             case "south":
51146                 return [0, cm.top+cm.bottom+c.getHeight()];
51147             break;
51148         }
51149     }
51150 });/*
51151  * Based on:
51152  * Ext JS Library 1.1.1
51153  * Copyright(c) 2006-2007, Ext JS, LLC.
51154  *
51155  * Originally Released Under LGPL - original licence link has changed is not relivant.
51156  *
51157  * Fork - LGPL
51158  * <script type="text/javascript">
51159  */
51160 /*
51161  * These classes are private internal classes
51162  */
51163 Roo.CenterLayoutRegion = function(mgr, config){
51164     Roo.LayoutRegion.call(this, mgr, config, "center");
51165     this.visible = true;
51166     this.minWidth = config.minWidth || 20;
51167     this.minHeight = config.minHeight || 20;
51168 };
51169
51170 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
51171     hide : function(){
51172         // center panel can't be hidden
51173     },
51174     
51175     show : function(){
51176         // center panel can't be hidden
51177     },
51178     
51179     getMinWidth: function(){
51180         return this.minWidth;
51181     },
51182     
51183     getMinHeight: function(){
51184         return this.minHeight;
51185     }
51186 });
51187
51188
51189 Roo.NorthLayoutRegion = function(mgr, config){
51190     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
51191     if(this.split){
51192         this.split.placement = Roo.SplitBar.TOP;
51193         this.split.orientation = Roo.SplitBar.VERTICAL;
51194         this.split.el.addClass("x-layout-split-v");
51195     }
51196     var size = config.initialSize || config.height;
51197     if(typeof size != "undefined"){
51198         this.el.setHeight(size);
51199     }
51200 };
51201 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
51202     orientation: Roo.SplitBar.VERTICAL,
51203     getBox : function(){
51204         if(this.collapsed){
51205             return this.collapsedEl.getBox();
51206         }
51207         var box = this.el.getBox();
51208         if(this.split){
51209             box.height += this.split.el.getHeight();
51210         }
51211         return box;
51212     },
51213     
51214     updateBox : function(box){
51215         if(this.split && !this.collapsed){
51216             box.height -= this.split.el.getHeight();
51217             this.split.el.setLeft(box.x);
51218             this.split.el.setTop(box.y+box.height);
51219             this.split.el.setWidth(box.width);
51220         }
51221         if(this.collapsed){
51222             this.updateBody(box.width, null);
51223         }
51224         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51225     }
51226 });
51227
51228 Roo.SouthLayoutRegion = function(mgr, config){
51229     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
51230     if(this.split){
51231         this.split.placement = Roo.SplitBar.BOTTOM;
51232         this.split.orientation = Roo.SplitBar.VERTICAL;
51233         this.split.el.addClass("x-layout-split-v");
51234     }
51235     var size = config.initialSize || config.height;
51236     if(typeof size != "undefined"){
51237         this.el.setHeight(size);
51238     }
51239 };
51240 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
51241     orientation: Roo.SplitBar.VERTICAL,
51242     getBox : function(){
51243         if(this.collapsed){
51244             return this.collapsedEl.getBox();
51245         }
51246         var box = this.el.getBox();
51247         if(this.split){
51248             var sh = this.split.el.getHeight();
51249             box.height += sh;
51250             box.y -= sh;
51251         }
51252         return box;
51253     },
51254     
51255     updateBox : function(box){
51256         if(this.split && !this.collapsed){
51257             var sh = this.split.el.getHeight();
51258             box.height -= sh;
51259             box.y += sh;
51260             this.split.el.setLeft(box.x);
51261             this.split.el.setTop(box.y-sh);
51262             this.split.el.setWidth(box.width);
51263         }
51264         if(this.collapsed){
51265             this.updateBody(box.width, null);
51266         }
51267         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51268     }
51269 });
51270
51271 Roo.EastLayoutRegion = function(mgr, config){
51272     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
51273     if(this.split){
51274         this.split.placement = Roo.SplitBar.RIGHT;
51275         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51276         this.split.el.addClass("x-layout-split-h");
51277     }
51278     var size = config.initialSize || config.width;
51279     if(typeof size != "undefined"){
51280         this.el.setWidth(size);
51281     }
51282 };
51283 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
51284     orientation: Roo.SplitBar.HORIZONTAL,
51285     getBox : function(){
51286         if(this.collapsed){
51287             return this.collapsedEl.getBox();
51288         }
51289         var box = this.el.getBox();
51290         if(this.split){
51291             var sw = this.split.el.getWidth();
51292             box.width += sw;
51293             box.x -= sw;
51294         }
51295         return box;
51296     },
51297
51298     updateBox : function(box){
51299         if(this.split && !this.collapsed){
51300             var sw = this.split.el.getWidth();
51301             box.width -= sw;
51302             this.split.el.setLeft(box.x);
51303             this.split.el.setTop(box.y);
51304             this.split.el.setHeight(box.height);
51305             box.x += sw;
51306         }
51307         if(this.collapsed){
51308             this.updateBody(null, box.height);
51309         }
51310         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51311     }
51312 });
51313
51314 Roo.WestLayoutRegion = function(mgr, config){
51315     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
51316     if(this.split){
51317         this.split.placement = Roo.SplitBar.LEFT;
51318         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51319         this.split.el.addClass("x-layout-split-h");
51320     }
51321     var size = config.initialSize || config.width;
51322     if(typeof size != "undefined"){
51323         this.el.setWidth(size);
51324     }
51325 };
51326 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
51327     orientation: Roo.SplitBar.HORIZONTAL,
51328     getBox : function(){
51329         if(this.collapsed){
51330             return this.collapsedEl.getBox();
51331         }
51332         var box = this.el.getBox();
51333         if(this.split){
51334             box.width += this.split.el.getWidth();
51335         }
51336         return box;
51337     },
51338     
51339     updateBox : function(box){
51340         if(this.split && !this.collapsed){
51341             var sw = this.split.el.getWidth();
51342             box.width -= sw;
51343             this.split.el.setLeft(box.x+box.width);
51344             this.split.el.setTop(box.y);
51345             this.split.el.setHeight(box.height);
51346         }
51347         if(this.collapsed){
51348             this.updateBody(null, box.height);
51349         }
51350         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51351     }
51352 });
51353 /*
51354  * Based on:
51355  * Ext JS Library 1.1.1
51356  * Copyright(c) 2006-2007, Ext JS, LLC.
51357  *
51358  * Originally Released Under LGPL - original licence link has changed is not relivant.
51359  *
51360  * Fork - LGPL
51361  * <script type="text/javascript">
51362  */
51363  
51364  
51365 /*
51366  * Private internal class for reading and applying state
51367  */
51368 Roo.LayoutStateManager = function(layout){
51369      // default empty state
51370      this.state = {
51371         north: {},
51372         south: {},
51373         east: {},
51374         west: {}       
51375     };
51376 };
51377
51378 Roo.LayoutStateManager.prototype = {
51379     init : function(layout, provider){
51380         this.provider = provider;
51381         var state = provider.get(layout.id+"-layout-state");
51382         if(state){
51383             var wasUpdating = layout.isUpdating();
51384             if(!wasUpdating){
51385                 layout.beginUpdate();
51386             }
51387             for(var key in state){
51388                 if(typeof state[key] != "function"){
51389                     var rstate = state[key];
51390                     var r = layout.getRegion(key);
51391                     if(r && rstate){
51392                         if(rstate.size){
51393                             r.resizeTo(rstate.size);
51394                         }
51395                         if(rstate.collapsed == true){
51396                             r.collapse(true);
51397                         }else{
51398                             r.expand(null, true);
51399                         }
51400                     }
51401                 }
51402             }
51403             if(!wasUpdating){
51404                 layout.endUpdate();
51405             }
51406             this.state = state; 
51407         }
51408         this.layout = layout;
51409         layout.on("regionresized", this.onRegionResized, this);
51410         layout.on("regioncollapsed", this.onRegionCollapsed, this);
51411         layout.on("regionexpanded", this.onRegionExpanded, this);
51412     },
51413     
51414     storeState : function(){
51415         this.provider.set(this.layout.id+"-layout-state", this.state);
51416     },
51417     
51418     onRegionResized : function(region, newSize){
51419         this.state[region.getPosition()].size = newSize;
51420         this.storeState();
51421     },
51422     
51423     onRegionCollapsed : function(region){
51424         this.state[region.getPosition()].collapsed = true;
51425         this.storeState();
51426     },
51427     
51428     onRegionExpanded : function(region){
51429         this.state[region.getPosition()].collapsed = false;
51430         this.storeState();
51431     }
51432 };/*
51433  * Based on:
51434  * Ext JS Library 1.1.1
51435  * Copyright(c) 2006-2007, Ext JS, LLC.
51436  *
51437  * Originally Released Under LGPL - original licence link has changed is not relivant.
51438  *
51439  * Fork - LGPL
51440  * <script type="text/javascript">
51441  */
51442 /**
51443  * @class Roo.ContentPanel
51444  * @extends Roo.util.Observable
51445  * A basic ContentPanel element.
51446  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
51447  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
51448  * @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
51449  * @cfg {Boolean}   closable      True if the panel can be closed/removed
51450  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
51451  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
51452  * @cfg {Toolbar}   toolbar       A toolbar for this panel
51453  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
51454  * @cfg {String} title          The title for this panel
51455  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
51456  * @cfg {String} url            Calls {@link #setUrl} with this value
51457  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
51458  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
51459  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
51460  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
51461
51462  * @constructor
51463  * Create a new ContentPanel.
51464  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
51465  * @param {String/Object} config A string to set only the title or a config object
51466  * @param {String} content (optional) Set the HTML content for this panel
51467  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
51468  */
51469 Roo.ContentPanel = function(el, config, content){
51470     
51471      
51472     /*
51473     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
51474         config = el;
51475         el = Roo.id();
51476     }
51477     if (config && config.parentLayout) { 
51478         el = config.parentLayout.el.createChild(); 
51479     }
51480     */
51481     if(el.autoCreate){ // xtype is available if this is called from factory
51482         config = el;
51483         el = Roo.id();
51484     }
51485     this.el = Roo.get(el);
51486     if(!this.el && config && config.autoCreate){
51487         if(typeof config.autoCreate == "object"){
51488             if(!config.autoCreate.id){
51489                 config.autoCreate.id = config.id||el;
51490             }
51491             this.el = Roo.DomHelper.append(document.body,
51492                         config.autoCreate, true);
51493         }else{
51494             this.el = Roo.DomHelper.append(document.body,
51495                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
51496         }
51497     }
51498     this.closable = false;
51499     this.loaded = false;
51500     this.active = false;
51501     if(typeof config == "string"){
51502         this.title = config;
51503     }else{
51504         Roo.apply(this, config);
51505     }
51506     
51507     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
51508         this.wrapEl = this.el.wrap();
51509         this.toolbar.container = this.el.insertSibling(false, 'before');
51510         this.toolbar = new Roo.Toolbar(this.toolbar);
51511     }
51512     
51513     // xtype created footer. - not sure if will work as we normally have to render first..
51514     if (this.footer && !this.footer.el && this.footer.xtype) {
51515         if (!this.wrapEl) {
51516             this.wrapEl = this.el.wrap();
51517         }
51518     
51519         this.footer.container = this.wrapEl.createChild();
51520          
51521         this.footer = Roo.factory(this.footer, Roo);
51522         
51523     }
51524     
51525     if(this.resizeEl){
51526         this.resizeEl = Roo.get(this.resizeEl, true);
51527     }else{
51528         this.resizeEl = this.el;
51529     }
51530     // handle view.xtype
51531     
51532  
51533     
51534     
51535     this.addEvents({
51536         /**
51537          * @event activate
51538          * Fires when this panel is activated. 
51539          * @param {Roo.ContentPanel} this
51540          */
51541         "activate" : true,
51542         /**
51543          * @event deactivate
51544          * Fires when this panel is activated. 
51545          * @param {Roo.ContentPanel} this
51546          */
51547         "deactivate" : true,
51548
51549         /**
51550          * @event resize
51551          * Fires when this panel is resized if fitToFrame is true.
51552          * @param {Roo.ContentPanel} this
51553          * @param {Number} width The width after any component adjustments
51554          * @param {Number} height The height after any component adjustments
51555          */
51556         "resize" : true,
51557         
51558          /**
51559          * @event render
51560          * Fires when this tab is created
51561          * @param {Roo.ContentPanel} this
51562          */
51563         "render" : true
51564         
51565         
51566         
51567     });
51568     
51569
51570     
51571     
51572     if(this.autoScroll){
51573         this.resizeEl.setStyle("overflow", "auto");
51574     } else {
51575         // fix randome scrolling
51576         this.el.on('scroll', function() {
51577             Roo.log('fix random scolling');
51578             this.scrollTo('top',0); 
51579         });
51580     }
51581     content = content || this.content;
51582     if(content){
51583         this.setContent(content);
51584     }
51585     if(config && config.url){
51586         this.setUrl(this.url, this.params, this.loadOnce);
51587     }
51588     
51589     
51590     
51591     Roo.ContentPanel.superclass.constructor.call(this);
51592     
51593     if (this.view && typeof(this.view.xtype) != 'undefined') {
51594         this.view.el = this.el.appendChild(document.createElement("div"));
51595         this.view = Roo.factory(this.view); 
51596         this.view.render  &&  this.view.render(false, '');  
51597     }
51598     
51599     
51600     this.fireEvent('render', this);
51601 };
51602
51603 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
51604     tabTip:'',
51605     setRegion : function(region){
51606         this.region = region;
51607         if(region){
51608            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
51609         }else{
51610            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
51611         } 
51612     },
51613     
51614     /**
51615      * Returns the toolbar for this Panel if one was configured. 
51616      * @return {Roo.Toolbar} 
51617      */
51618     getToolbar : function(){
51619         return this.toolbar;
51620     },
51621     
51622     setActiveState : function(active){
51623         this.active = active;
51624         if(!active){
51625             this.fireEvent("deactivate", this);
51626         }else{
51627             this.fireEvent("activate", this);
51628         }
51629     },
51630     /**
51631      * Updates this panel's element
51632      * @param {String} content The new content
51633      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51634     */
51635     setContent : function(content, loadScripts){
51636         this.el.update(content, loadScripts);
51637     },
51638
51639     ignoreResize : function(w, h){
51640         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51641             return true;
51642         }else{
51643             this.lastSize = {width: w, height: h};
51644             return false;
51645         }
51646     },
51647     /**
51648      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51649      * @return {Roo.UpdateManager} The UpdateManager
51650      */
51651     getUpdateManager : function(){
51652         return this.el.getUpdateManager();
51653     },
51654      /**
51655      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51656      * @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:
51657 <pre><code>
51658 panel.load({
51659     url: "your-url.php",
51660     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51661     callback: yourFunction,
51662     scope: yourObject, //(optional scope)
51663     discardUrl: false,
51664     nocache: false,
51665     text: "Loading...",
51666     timeout: 30,
51667     scripts: false
51668 });
51669 </code></pre>
51670      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51671      * 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.
51672      * @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}
51673      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51674      * @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.
51675      * @return {Roo.ContentPanel} this
51676      */
51677     load : function(){
51678         var um = this.el.getUpdateManager();
51679         um.update.apply(um, arguments);
51680         return this;
51681     },
51682
51683
51684     /**
51685      * 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.
51686      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51687      * @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)
51688      * @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)
51689      * @return {Roo.UpdateManager} The UpdateManager
51690      */
51691     setUrl : function(url, params, loadOnce){
51692         if(this.refreshDelegate){
51693             this.removeListener("activate", this.refreshDelegate);
51694         }
51695         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51696         this.on("activate", this.refreshDelegate);
51697         return this.el.getUpdateManager();
51698     },
51699     
51700     _handleRefresh : function(url, params, loadOnce){
51701         if(!loadOnce || !this.loaded){
51702             var updater = this.el.getUpdateManager();
51703             updater.update(url, params, this._setLoaded.createDelegate(this));
51704         }
51705     },
51706     
51707     _setLoaded : function(){
51708         this.loaded = true;
51709     }, 
51710     
51711     /**
51712      * Returns this panel's id
51713      * @return {String} 
51714      */
51715     getId : function(){
51716         return this.el.id;
51717     },
51718     
51719     /** 
51720      * Returns this panel's element - used by regiosn to add.
51721      * @return {Roo.Element} 
51722      */
51723     getEl : function(){
51724         return this.wrapEl || this.el;
51725     },
51726     
51727     adjustForComponents : function(width, height)
51728     {
51729         //Roo.log('adjustForComponents ');
51730         if(this.resizeEl != this.el){
51731             width -= this.el.getFrameWidth('lr');
51732             height -= this.el.getFrameWidth('tb');
51733         }
51734         if(this.toolbar){
51735             var te = this.toolbar.getEl();
51736             height -= te.getHeight();
51737             te.setWidth(width);
51738         }
51739         if(this.footer){
51740             var te = this.footer.getEl();
51741             Roo.log("footer:" + te.getHeight());
51742             
51743             height -= te.getHeight();
51744             te.setWidth(width);
51745         }
51746         
51747         
51748         if(this.adjustments){
51749             width += this.adjustments[0];
51750             height += this.adjustments[1];
51751         }
51752         return {"width": width, "height": height};
51753     },
51754     
51755     setSize : function(width, height){
51756         if(this.fitToFrame && !this.ignoreResize(width, height)){
51757             if(this.fitContainer && this.resizeEl != this.el){
51758                 this.el.setSize(width, height);
51759             }
51760             var size = this.adjustForComponents(width, height);
51761             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51762             this.fireEvent('resize', this, size.width, size.height);
51763         }
51764     },
51765     
51766     /**
51767      * Returns this panel's title
51768      * @return {String} 
51769      */
51770     getTitle : function(){
51771         return this.title;
51772     },
51773     
51774     /**
51775      * Set this panel's title
51776      * @param {String} title
51777      */
51778     setTitle : function(title){
51779         this.title = title;
51780         if(this.region){
51781             this.region.updatePanelTitle(this, title);
51782         }
51783     },
51784     
51785     /**
51786      * Returns true is this panel was configured to be closable
51787      * @return {Boolean} 
51788      */
51789     isClosable : function(){
51790         return this.closable;
51791     },
51792     
51793     beforeSlide : function(){
51794         this.el.clip();
51795         this.resizeEl.clip();
51796     },
51797     
51798     afterSlide : function(){
51799         this.el.unclip();
51800         this.resizeEl.unclip();
51801     },
51802     
51803     /**
51804      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51805      *   Will fail silently if the {@link #setUrl} method has not been called.
51806      *   This does not activate the panel, just updates its content.
51807      */
51808     refresh : function(){
51809         if(this.refreshDelegate){
51810            this.loaded = false;
51811            this.refreshDelegate();
51812         }
51813     },
51814     
51815     /**
51816      * Destroys this panel
51817      */
51818     destroy : function(){
51819         this.el.removeAllListeners();
51820         var tempEl = document.createElement("span");
51821         tempEl.appendChild(this.el.dom);
51822         tempEl.innerHTML = "";
51823         this.el.remove();
51824         this.el = null;
51825     },
51826     
51827     /**
51828      * form - if the content panel contains a form - this is a reference to it.
51829      * @type {Roo.form.Form}
51830      */
51831     form : false,
51832     /**
51833      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51834      *    This contains a reference to it.
51835      * @type {Roo.View}
51836      */
51837     view : false,
51838     
51839       /**
51840      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51841      * <pre><code>
51842
51843 layout.addxtype({
51844        xtype : 'Form',
51845        items: [ .... ]
51846    }
51847 );
51848
51849 </code></pre>
51850      * @param {Object} cfg Xtype definition of item to add.
51851      */
51852     
51853     addxtype : function(cfg) {
51854         // add form..
51855         if (cfg.xtype.match(/^Form$/)) {
51856             
51857             var el;
51858             //if (this.footer) {
51859             //    el = this.footer.container.insertSibling(false, 'before');
51860             //} else {
51861                 el = this.el.createChild();
51862             //}
51863
51864             this.form = new  Roo.form.Form(cfg);
51865             
51866             
51867             if ( this.form.allItems.length) this.form.render(el.dom);
51868             return this.form;
51869         }
51870         // should only have one of theses..
51871         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51872             // views.. should not be just added - used named prop 'view''
51873             
51874             cfg.el = this.el.appendChild(document.createElement("div"));
51875             // factory?
51876             
51877             var ret = new Roo.factory(cfg);
51878              
51879              ret.render && ret.render(false, ''); // render blank..
51880             this.view = ret;
51881             return ret;
51882         }
51883         return false;
51884     }
51885 });
51886
51887 /**
51888  * @class Roo.GridPanel
51889  * @extends Roo.ContentPanel
51890  * @constructor
51891  * Create a new GridPanel.
51892  * @param {Roo.grid.Grid} grid The grid for this panel
51893  * @param {String/Object} config A string to set only the panel's title, or a config object
51894  */
51895 Roo.GridPanel = function(grid, config){
51896     
51897   
51898     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51899         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51900         
51901     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51902     
51903     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51904     
51905     if(this.toolbar){
51906         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51907     }
51908     // xtype created footer. - not sure if will work as we normally have to render first..
51909     if (this.footer && !this.footer.el && this.footer.xtype) {
51910         
51911         this.footer.container = this.grid.getView().getFooterPanel(true);
51912         this.footer.dataSource = this.grid.dataSource;
51913         this.footer = Roo.factory(this.footer, Roo);
51914         
51915     }
51916     
51917     grid.monitorWindowResize = false; // turn off autosizing
51918     grid.autoHeight = false;
51919     grid.autoWidth = false;
51920     this.grid = grid;
51921     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
51922 };
51923
51924 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
51925     getId : function(){
51926         return this.grid.id;
51927     },
51928     
51929     /**
51930      * Returns the grid for this panel
51931      * @return {Roo.grid.Grid} 
51932      */
51933     getGrid : function(){
51934         return this.grid;    
51935     },
51936     
51937     setSize : function(width, height){
51938         if(!this.ignoreResize(width, height)){
51939             var grid = this.grid;
51940             var size = this.adjustForComponents(width, height);
51941             grid.getGridEl().setSize(size.width, size.height);
51942             grid.autoSize();
51943         }
51944     },
51945     
51946     beforeSlide : function(){
51947         this.grid.getView().scroller.clip();
51948     },
51949     
51950     afterSlide : function(){
51951         this.grid.getView().scroller.unclip();
51952     },
51953     
51954     destroy : function(){
51955         this.grid.destroy();
51956         delete this.grid;
51957         Roo.GridPanel.superclass.destroy.call(this); 
51958     }
51959 });
51960
51961
51962 /**
51963  * @class Roo.NestedLayoutPanel
51964  * @extends Roo.ContentPanel
51965  * @constructor
51966  * Create a new NestedLayoutPanel.
51967  * 
51968  * 
51969  * @param {Roo.BorderLayout} layout The layout for this panel
51970  * @param {String/Object} config A string to set only the title or a config object
51971  */
51972 Roo.NestedLayoutPanel = function(layout, config)
51973 {
51974     // construct with only one argument..
51975     /* FIXME - implement nicer consturctors
51976     if (layout.layout) {
51977         config = layout;
51978         layout = config.layout;
51979         delete config.layout;
51980     }
51981     if (layout.xtype && !layout.getEl) {
51982         // then layout needs constructing..
51983         layout = Roo.factory(layout, Roo);
51984     }
51985     */
51986     
51987     
51988     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51989     
51990     layout.monitorWindowResize = false; // turn off autosizing
51991     this.layout = layout;
51992     this.layout.getEl().addClass("x-layout-nested-layout");
51993     
51994     
51995     
51996     
51997 };
51998
51999 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
52000
52001     setSize : function(width, height){
52002         if(!this.ignoreResize(width, height)){
52003             var size = this.adjustForComponents(width, height);
52004             var el = this.layout.getEl();
52005             el.setSize(size.width, size.height);
52006             var touch = el.dom.offsetWidth;
52007             this.layout.layout();
52008             // ie requires a double layout on the first pass
52009             if(Roo.isIE && !this.initialized){
52010                 this.initialized = true;
52011                 this.layout.layout();
52012             }
52013         }
52014     },
52015     
52016     // activate all subpanels if not currently active..
52017     
52018     setActiveState : function(active){
52019         this.active = active;
52020         if(!active){
52021             this.fireEvent("deactivate", this);
52022             return;
52023         }
52024         
52025         this.fireEvent("activate", this);
52026         // not sure if this should happen before or after..
52027         if (!this.layout) {
52028             return; // should not happen..
52029         }
52030         var reg = false;
52031         for (var r in this.layout.regions) {
52032             reg = this.layout.getRegion(r);
52033             if (reg.getActivePanel()) {
52034                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
52035                 reg.setActivePanel(reg.getActivePanel());
52036                 continue;
52037             }
52038             if (!reg.panels.length) {
52039                 continue;
52040             }
52041             reg.showPanel(reg.getPanel(0));
52042         }
52043         
52044         
52045         
52046         
52047     },
52048     
52049     /**
52050      * Returns the nested BorderLayout for this panel
52051      * @return {Roo.BorderLayout} 
52052      */
52053     getLayout : function(){
52054         return this.layout;
52055     },
52056     
52057      /**
52058      * Adds a xtype elements to the layout of the nested panel
52059      * <pre><code>
52060
52061 panel.addxtype({
52062        xtype : 'ContentPanel',
52063        region: 'west',
52064        items: [ .... ]
52065    }
52066 );
52067
52068 panel.addxtype({
52069         xtype : 'NestedLayoutPanel',
52070         region: 'west',
52071         layout: {
52072            center: { },
52073            west: { }   
52074         },
52075         items : [ ... list of content panels or nested layout panels.. ]
52076    }
52077 );
52078 </code></pre>
52079      * @param {Object} cfg Xtype definition of item to add.
52080      */
52081     addxtype : function(cfg) {
52082         return this.layout.addxtype(cfg);
52083     
52084     }
52085 });
52086
52087 Roo.ScrollPanel = function(el, config, content){
52088     config = config || {};
52089     config.fitToFrame = true;
52090     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
52091     
52092     this.el.dom.style.overflow = "hidden";
52093     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
52094     this.el.removeClass("x-layout-inactive-content");
52095     this.el.on("mousewheel", this.onWheel, this);
52096
52097     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
52098     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
52099     up.unselectable(); down.unselectable();
52100     up.on("click", this.scrollUp, this);
52101     down.on("click", this.scrollDown, this);
52102     up.addClassOnOver("x-scroller-btn-over");
52103     down.addClassOnOver("x-scroller-btn-over");
52104     up.addClassOnClick("x-scroller-btn-click");
52105     down.addClassOnClick("x-scroller-btn-click");
52106     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
52107
52108     this.resizeEl = this.el;
52109     this.el = wrap; this.up = up; this.down = down;
52110 };
52111
52112 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
52113     increment : 100,
52114     wheelIncrement : 5,
52115     scrollUp : function(){
52116         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
52117     },
52118
52119     scrollDown : function(){
52120         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
52121     },
52122
52123     afterScroll : function(){
52124         var el = this.resizeEl;
52125         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
52126         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52127         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52128     },
52129
52130     setSize : function(){
52131         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
52132         this.afterScroll();
52133     },
52134
52135     onWheel : function(e){
52136         var d = e.getWheelDelta();
52137         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
52138         this.afterScroll();
52139         e.stopEvent();
52140     },
52141
52142     setContent : function(content, loadScripts){
52143         this.resizeEl.update(content, loadScripts);
52144     }
52145
52146 });
52147
52148
52149
52150
52151
52152
52153
52154
52155
52156 /**
52157  * @class Roo.TreePanel
52158  * @extends Roo.ContentPanel
52159  * @constructor
52160  * Create a new TreePanel. - defaults to fit/scoll contents.
52161  * @param {String/Object} config A string to set only the panel's title, or a config object
52162  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
52163  */
52164 Roo.TreePanel = function(config){
52165     var el = config.el;
52166     var tree = config.tree;
52167     delete config.tree; 
52168     delete config.el; // hopefull!
52169     
52170     // wrapper for IE7 strict & safari scroll issue
52171     
52172     var treeEl = el.createChild();
52173     config.resizeEl = treeEl;
52174     
52175     
52176     
52177     Roo.TreePanel.superclass.constructor.call(this, el, config);
52178  
52179  
52180     this.tree = new Roo.tree.TreePanel(treeEl , tree);
52181     //console.log(tree);
52182     this.on('activate', function()
52183     {
52184         if (this.tree.rendered) {
52185             return;
52186         }
52187         //console.log('render tree');
52188         this.tree.render();
52189     });
52190     // this should not be needed.. - it's actually the 'el' that resizes?
52191     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
52192     
52193     //this.on('resize',  function (cp, w, h) {
52194     //        this.tree.innerCt.setWidth(w);
52195     //        this.tree.innerCt.setHeight(h);
52196     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
52197     //});
52198
52199         
52200     
52201 };
52202
52203 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
52204     fitToFrame : true,
52205     autoScroll : true
52206 });
52207
52208
52209
52210
52211
52212
52213
52214
52215
52216
52217
52218 /*
52219  * Based on:
52220  * Ext JS Library 1.1.1
52221  * Copyright(c) 2006-2007, Ext JS, LLC.
52222  *
52223  * Originally Released Under LGPL - original licence link has changed is not relivant.
52224  *
52225  * Fork - LGPL
52226  * <script type="text/javascript">
52227  */
52228  
52229
52230 /**
52231  * @class Roo.ReaderLayout
52232  * @extends Roo.BorderLayout
52233  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
52234  * center region containing two nested regions (a top one for a list view and one for item preview below),
52235  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
52236  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
52237  * expedites the setup of the overall layout and regions for this common application style.
52238  * Example:
52239  <pre><code>
52240 var reader = new Roo.ReaderLayout();
52241 var CP = Roo.ContentPanel;  // shortcut for adding
52242
52243 reader.beginUpdate();
52244 reader.add("north", new CP("north", "North"));
52245 reader.add("west", new CP("west", {title: "West"}));
52246 reader.add("east", new CP("east", {title: "East"}));
52247
52248 reader.regions.listView.add(new CP("listView", "List"));
52249 reader.regions.preview.add(new CP("preview", "Preview"));
52250 reader.endUpdate();
52251 </code></pre>
52252 * @constructor
52253 * Create a new ReaderLayout
52254 * @param {Object} config Configuration options
52255 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
52256 * document.body if omitted)
52257 */
52258 Roo.ReaderLayout = function(config, renderTo){
52259     var c = config || {size:{}};
52260     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
52261         north: c.north !== false ? Roo.apply({
52262             split:false,
52263             initialSize: 32,
52264             titlebar: false
52265         }, c.north) : false,
52266         west: c.west !== false ? Roo.apply({
52267             split:true,
52268             initialSize: 200,
52269             minSize: 175,
52270             maxSize: 400,
52271             titlebar: true,
52272             collapsible: true,
52273             animate: true,
52274             margins:{left:5,right:0,bottom:5,top:5},
52275             cmargins:{left:5,right:5,bottom:5,top:5}
52276         }, c.west) : false,
52277         east: c.east !== false ? Roo.apply({
52278             split:true,
52279             initialSize: 200,
52280             minSize: 175,
52281             maxSize: 400,
52282             titlebar: true,
52283             collapsible: true,
52284             animate: true,
52285             margins:{left:0,right:5,bottom:5,top:5},
52286             cmargins:{left:5,right:5,bottom:5,top:5}
52287         }, c.east) : false,
52288         center: Roo.apply({
52289             tabPosition: 'top',
52290             autoScroll:false,
52291             closeOnTab: true,
52292             titlebar:false,
52293             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
52294         }, c.center)
52295     });
52296
52297     this.el.addClass('x-reader');
52298
52299     this.beginUpdate();
52300
52301     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
52302         south: c.preview !== false ? Roo.apply({
52303             split:true,
52304             initialSize: 200,
52305             minSize: 100,
52306             autoScroll:true,
52307             collapsible:true,
52308             titlebar: true,
52309             cmargins:{top:5,left:0, right:0, bottom:0}
52310         }, c.preview) : false,
52311         center: Roo.apply({
52312             autoScroll:false,
52313             titlebar:false,
52314             minHeight:200
52315         }, c.listView)
52316     });
52317     this.add('center', new Roo.NestedLayoutPanel(inner,
52318             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
52319
52320     this.endUpdate();
52321
52322     this.regions.preview = inner.getRegion('south');
52323     this.regions.listView = inner.getRegion('center');
52324 };
52325
52326 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
52327  * Based on:
52328  * Ext JS Library 1.1.1
52329  * Copyright(c) 2006-2007, Ext JS, LLC.
52330  *
52331  * Originally Released Under LGPL - original licence link has changed is not relivant.
52332  *
52333  * Fork - LGPL
52334  * <script type="text/javascript">
52335  */
52336  
52337 /**
52338  * @class Roo.grid.Grid
52339  * @extends Roo.util.Observable
52340  * This class represents the primary interface of a component based grid control.
52341  * <br><br>Usage:<pre><code>
52342  var grid = new Roo.grid.Grid("my-container-id", {
52343      ds: myDataStore,
52344      cm: myColModel,
52345      selModel: mySelectionModel,
52346      autoSizeColumns: true,
52347      monitorWindowResize: false,
52348      trackMouseOver: true
52349  });
52350  // set any options
52351  grid.render();
52352  * </code></pre>
52353  * <b>Common Problems:</b><br/>
52354  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
52355  * element will correct this<br/>
52356  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
52357  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
52358  * are unpredictable.<br/>
52359  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
52360  * grid to calculate dimensions/offsets.<br/>
52361   * @constructor
52362  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
52363  * The container MUST have some type of size defined for the grid to fill. The container will be
52364  * automatically set to position relative if it isn't already.
52365  * @param {Object} config A config object that sets properties on this grid.
52366  */
52367 Roo.grid.Grid = function(container, config){
52368         // initialize the container
52369         this.container = Roo.get(container);
52370         this.container.update("");
52371         this.container.setStyle("overflow", "hidden");
52372     this.container.addClass('x-grid-container');
52373
52374     this.id = this.container.id;
52375
52376     Roo.apply(this, config);
52377     // check and correct shorthanded configs
52378     if(this.ds){
52379         this.dataSource = this.ds;
52380         delete this.ds;
52381     }
52382     if(this.cm){
52383         this.colModel = this.cm;
52384         delete this.cm;
52385     }
52386     if(this.sm){
52387         this.selModel = this.sm;
52388         delete this.sm;
52389     }
52390
52391     if (this.selModel) {
52392         this.selModel = Roo.factory(this.selModel, Roo.grid);
52393         this.sm = this.selModel;
52394         this.sm.xmodule = this.xmodule || false;
52395     }
52396     if (typeof(this.colModel.config) == 'undefined') {
52397         this.colModel = new Roo.grid.ColumnModel(this.colModel);
52398         this.cm = this.colModel;
52399         this.cm.xmodule = this.xmodule || false;
52400     }
52401     if (this.dataSource) {
52402         this.dataSource= Roo.factory(this.dataSource, Roo.data);
52403         this.ds = this.dataSource;
52404         this.ds.xmodule = this.xmodule || false;
52405          
52406     }
52407     
52408     
52409     
52410     if(this.width){
52411         this.container.setWidth(this.width);
52412     }
52413
52414     if(this.height){
52415         this.container.setHeight(this.height);
52416     }
52417     /** @private */
52418         this.addEvents({
52419         // raw events
52420         /**
52421          * @event click
52422          * The raw click event for the entire grid.
52423          * @param {Roo.EventObject} e
52424          */
52425         "click" : true,
52426         /**
52427          * @event dblclick
52428          * The raw dblclick event for the entire grid.
52429          * @param {Roo.EventObject} e
52430          */
52431         "dblclick" : true,
52432         /**
52433          * @event contextmenu
52434          * The raw contextmenu event for the entire grid.
52435          * @param {Roo.EventObject} e
52436          */
52437         "contextmenu" : true,
52438         /**
52439          * @event mousedown
52440          * The raw mousedown event for the entire grid.
52441          * @param {Roo.EventObject} e
52442          */
52443         "mousedown" : true,
52444         /**
52445          * @event mouseup
52446          * The raw mouseup event for the entire grid.
52447          * @param {Roo.EventObject} e
52448          */
52449         "mouseup" : true,
52450         /**
52451          * @event mouseover
52452          * The raw mouseover event for the entire grid.
52453          * @param {Roo.EventObject} e
52454          */
52455         "mouseover" : true,
52456         /**
52457          * @event mouseout
52458          * The raw mouseout event for the entire grid.
52459          * @param {Roo.EventObject} e
52460          */
52461         "mouseout" : true,
52462         /**
52463          * @event keypress
52464          * The raw keypress event for the entire grid.
52465          * @param {Roo.EventObject} e
52466          */
52467         "keypress" : true,
52468         /**
52469          * @event keydown
52470          * The raw keydown event for the entire grid.
52471          * @param {Roo.EventObject} e
52472          */
52473         "keydown" : true,
52474
52475         // custom events
52476
52477         /**
52478          * @event cellclick
52479          * Fires when a cell is clicked
52480          * @param {Grid} this
52481          * @param {Number} rowIndex
52482          * @param {Number} columnIndex
52483          * @param {Roo.EventObject} e
52484          */
52485         "cellclick" : true,
52486         /**
52487          * @event celldblclick
52488          * Fires when a cell is double clicked
52489          * @param {Grid} this
52490          * @param {Number} rowIndex
52491          * @param {Number} columnIndex
52492          * @param {Roo.EventObject} e
52493          */
52494         "celldblclick" : true,
52495         /**
52496          * @event rowclick
52497          * Fires when a row is clicked
52498          * @param {Grid} this
52499          * @param {Number} rowIndex
52500          * @param {Roo.EventObject} e
52501          */
52502         "rowclick" : true,
52503         /**
52504          * @event rowdblclick
52505          * Fires when a row is double clicked
52506          * @param {Grid} this
52507          * @param {Number} rowIndex
52508          * @param {Roo.EventObject} e
52509          */
52510         "rowdblclick" : true,
52511         /**
52512          * @event headerclick
52513          * Fires when a header is clicked
52514          * @param {Grid} this
52515          * @param {Number} columnIndex
52516          * @param {Roo.EventObject} e
52517          */
52518         "headerclick" : true,
52519         /**
52520          * @event headerdblclick
52521          * Fires when a header cell is double clicked
52522          * @param {Grid} this
52523          * @param {Number} columnIndex
52524          * @param {Roo.EventObject} e
52525          */
52526         "headerdblclick" : true,
52527         /**
52528          * @event rowcontextmenu
52529          * Fires when a row is right clicked
52530          * @param {Grid} this
52531          * @param {Number} rowIndex
52532          * @param {Roo.EventObject} e
52533          */
52534         "rowcontextmenu" : true,
52535         /**
52536          * @event cellcontextmenu
52537          * Fires when a cell is right clicked
52538          * @param {Grid} this
52539          * @param {Number} rowIndex
52540          * @param {Number} cellIndex
52541          * @param {Roo.EventObject} e
52542          */
52543          "cellcontextmenu" : true,
52544         /**
52545          * @event headercontextmenu
52546          * Fires when a header is right clicked
52547          * @param {Grid} this
52548          * @param {Number} columnIndex
52549          * @param {Roo.EventObject} e
52550          */
52551         "headercontextmenu" : true,
52552         /**
52553          * @event bodyscroll
52554          * Fires when the body element is scrolled
52555          * @param {Number} scrollLeft
52556          * @param {Number} scrollTop
52557          */
52558         "bodyscroll" : true,
52559         /**
52560          * @event columnresize
52561          * Fires when the user resizes a column
52562          * @param {Number} columnIndex
52563          * @param {Number} newSize
52564          */
52565         "columnresize" : true,
52566         /**
52567          * @event columnmove
52568          * Fires when the user moves a column
52569          * @param {Number} oldIndex
52570          * @param {Number} newIndex
52571          */
52572         "columnmove" : true,
52573         /**
52574          * @event startdrag
52575          * Fires when row(s) start being dragged
52576          * @param {Grid} this
52577          * @param {Roo.GridDD} dd The drag drop object
52578          * @param {event} e The raw browser event
52579          */
52580         "startdrag" : true,
52581         /**
52582          * @event enddrag
52583          * Fires when a drag operation is complete
52584          * @param {Grid} this
52585          * @param {Roo.GridDD} dd The drag drop object
52586          * @param {event} e The raw browser event
52587          */
52588         "enddrag" : true,
52589         /**
52590          * @event dragdrop
52591          * Fires when dragged row(s) are dropped on a valid DD target
52592          * @param {Grid} this
52593          * @param {Roo.GridDD} dd The drag drop object
52594          * @param {String} targetId The target drag drop object
52595          * @param {event} e The raw browser event
52596          */
52597         "dragdrop" : true,
52598         /**
52599          * @event dragover
52600          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
52601          * @param {Grid} this
52602          * @param {Roo.GridDD} dd The drag drop object
52603          * @param {String} targetId The target drag drop object
52604          * @param {event} e The raw browser event
52605          */
52606         "dragover" : true,
52607         /**
52608          * @event dragenter
52609          *  Fires when the dragged row(s) first cross another DD target while being dragged
52610          * @param {Grid} this
52611          * @param {Roo.GridDD} dd The drag drop object
52612          * @param {String} targetId The target drag drop object
52613          * @param {event} e The raw browser event
52614          */
52615         "dragenter" : true,
52616         /**
52617          * @event dragout
52618          * Fires when the dragged row(s) leave another DD target while being dragged
52619          * @param {Grid} this
52620          * @param {Roo.GridDD} dd The drag drop object
52621          * @param {String} targetId The target drag drop object
52622          * @param {event} e The raw browser event
52623          */
52624         "dragout" : true,
52625         /**
52626          * @event rowclass
52627          * Fires when a row is rendered, so you can change add a style to it.
52628          * @param {GridView} gridview   The grid view
52629          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52630          */
52631         'rowclass' : true,
52632
52633         /**
52634          * @event render
52635          * Fires when the grid is rendered
52636          * @param {Grid} grid
52637          */
52638         'render' : true
52639     });
52640
52641     Roo.grid.Grid.superclass.constructor.call(this);
52642 };
52643 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52644     
52645     /**
52646      * @cfg {String} ddGroup - drag drop group.
52647      */
52648
52649     /**
52650      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52651      */
52652     minColumnWidth : 25,
52653
52654     /**
52655      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52656      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52657      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52658      */
52659     autoSizeColumns : false,
52660
52661     /**
52662      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52663      */
52664     autoSizeHeaders : true,
52665
52666     /**
52667      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52668      */
52669     monitorWindowResize : true,
52670
52671     /**
52672      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52673      * rows measured to get a columns size. Default is 0 (all rows).
52674      */
52675     maxRowsToMeasure : 0,
52676
52677     /**
52678      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52679      */
52680     trackMouseOver : true,
52681
52682     /**
52683     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52684     */
52685     
52686     /**
52687     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52688     */
52689     enableDragDrop : false,
52690     
52691     /**
52692     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52693     */
52694     enableColumnMove : true,
52695     
52696     /**
52697     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52698     */
52699     enableColumnHide : true,
52700     
52701     /**
52702     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52703     */
52704     enableRowHeightSync : false,
52705     
52706     /**
52707     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52708     */
52709     stripeRows : true,
52710     
52711     /**
52712     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52713     */
52714     autoHeight : false,
52715
52716     /**
52717      * @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.
52718      */
52719     autoExpandColumn : false,
52720
52721     /**
52722     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52723     * Default is 50.
52724     */
52725     autoExpandMin : 50,
52726
52727     /**
52728     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52729     */
52730     autoExpandMax : 1000,
52731
52732     /**
52733     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52734     */
52735     view : null,
52736
52737     /**
52738     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52739     */
52740     loadMask : false,
52741     /**
52742     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52743     */
52744     dropTarget: false,
52745     
52746    
52747     
52748     // private
52749     rendered : false,
52750
52751     /**
52752     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52753     * of a fixed width. Default is false.
52754     */
52755     /**
52756     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52757     */
52758     /**
52759      * Called once after all setup has been completed and the grid is ready to be rendered.
52760      * @return {Roo.grid.Grid} this
52761      */
52762     render : function()
52763     {
52764         var c = this.container;
52765         // try to detect autoHeight/width mode
52766         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52767             this.autoHeight = true;
52768         }
52769         var view = this.getView();
52770         view.init(this);
52771
52772         c.on("click", this.onClick, this);
52773         c.on("dblclick", this.onDblClick, this);
52774         c.on("contextmenu", this.onContextMenu, this);
52775         c.on("keydown", this.onKeyDown, this);
52776         if (Roo.isTouch) {
52777             c.on("touchstart", this.onTouchStart, this);
52778         }
52779
52780         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52781
52782         this.getSelectionModel().init(this);
52783
52784         view.render();
52785
52786         if(this.loadMask){
52787             this.loadMask = new Roo.LoadMask(this.container,
52788                     Roo.apply({store:this.dataSource}, this.loadMask));
52789         }
52790         
52791         
52792         if (this.toolbar && this.toolbar.xtype) {
52793             this.toolbar.container = this.getView().getHeaderPanel(true);
52794             this.toolbar = new Roo.Toolbar(this.toolbar);
52795         }
52796         if (this.footer && this.footer.xtype) {
52797             this.footer.dataSource = this.getDataSource();
52798             this.footer.container = this.getView().getFooterPanel(true);
52799             this.footer = Roo.factory(this.footer, Roo);
52800         }
52801         if (this.dropTarget && this.dropTarget.xtype) {
52802             delete this.dropTarget.xtype;
52803             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52804         }
52805         
52806         
52807         this.rendered = true;
52808         this.fireEvent('render', this);
52809         return this;
52810     },
52811
52812         /**
52813          * Reconfigures the grid to use a different Store and Column Model.
52814          * The View will be bound to the new objects and refreshed.
52815          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52816          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52817          */
52818     reconfigure : function(dataSource, colModel){
52819         if(this.loadMask){
52820             this.loadMask.destroy();
52821             this.loadMask = new Roo.LoadMask(this.container,
52822                     Roo.apply({store:dataSource}, this.loadMask));
52823         }
52824         this.view.bind(dataSource, colModel);
52825         this.dataSource = dataSource;
52826         this.colModel = colModel;
52827         this.view.refresh(true);
52828     },
52829
52830     // private
52831     onKeyDown : function(e){
52832         this.fireEvent("keydown", e);
52833     },
52834
52835     /**
52836      * Destroy this grid.
52837      * @param {Boolean} removeEl True to remove the element
52838      */
52839     destroy : function(removeEl, keepListeners){
52840         if(this.loadMask){
52841             this.loadMask.destroy();
52842         }
52843         var c = this.container;
52844         c.removeAllListeners();
52845         this.view.destroy();
52846         this.colModel.purgeListeners();
52847         if(!keepListeners){
52848             this.purgeListeners();
52849         }
52850         c.update("");
52851         if(removeEl === true){
52852             c.remove();
52853         }
52854     },
52855
52856     // private
52857     processEvent : function(name, e){
52858         // does this fire select???
52859         //Roo.log('grid:processEvent '  + name);
52860         
52861         if (name != 'touchstart' ) {
52862             this.fireEvent(name, e);    
52863         }
52864         
52865         var t = e.getTarget();
52866         var v = this.view;
52867         var header = v.findHeaderIndex(t);
52868         if(header !== false){
52869             var ename = name == 'touchstart' ? 'click' : name;
52870              
52871             this.fireEvent("header" + ename, this, header, e);
52872         }else{
52873             var row = v.findRowIndex(t);
52874             var cell = v.findCellIndex(t);
52875             if (name == 'touchstart') {
52876                 // first touch is always a click.
52877                 // hopefull this happens after selection is updated.?
52878                 name = false;
52879                 
52880                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52881                     var cs = this.selModel.getSelectedCell();
52882                     if (row == cs[0] && cell == cs[1]){
52883                         name = 'dblclick';
52884                     }
52885                 }
52886                 if (typeof(this.selModel.getSelections) != 'undefined') {
52887                     var cs = this.selModel.getSelections();
52888                     var ds = this.dataSource;
52889                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52890                         name = 'dblclick';
52891                     }
52892                 }
52893                 if (!name) {
52894                     return;
52895                 }
52896             }
52897             
52898             
52899             if(row !== false){
52900                 this.fireEvent("row" + name, this, row, e);
52901                 if(cell !== false){
52902                     this.fireEvent("cell" + name, this, row, cell, e);
52903                 }
52904             }
52905         }
52906     },
52907
52908     // private
52909     onClick : function(e){
52910         this.processEvent("click", e);
52911     },
52912    // private
52913     onTouchStart : function(e){
52914         this.processEvent("touchstart", e);
52915     },
52916
52917     // private
52918     onContextMenu : function(e, t){
52919         this.processEvent("contextmenu", e);
52920     },
52921
52922     // private
52923     onDblClick : function(e){
52924         this.processEvent("dblclick", e);
52925     },
52926
52927     // private
52928     walkCells : function(row, col, step, fn, scope){
52929         var cm = this.colModel, clen = cm.getColumnCount();
52930         var ds = this.dataSource, rlen = ds.getCount(), first = true;
52931         if(step < 0){
52932             if(col < 0){
52933                 row--;
52934                 first = false;
52935             }
52936             while(row >= 0){
52937                 if(!first){
52938                     col = clen-1;
52939                 }
52940                 first = false;
52941                 while(col >= 0){
52942                     if(fn.call(scope || this, row, col, cm) === true){
52943                         return [row, col];
52944                     }
52945                     col--;
52946                 }
52947                 row--;
52948             }
52949         } else {
52950             if(col >= clen){
52951                 row++;
52952                 first = false;
52953             }
52954             while(row < rlen){
52955                 if(!first){
52956                     col = 0;
52957                 }
52958                 first = false;
52959                 while(col < clen){
52960                     if(fn.call(scope || this, row, col, cm) === true){
52961                         return [row, col];
52962                     }
52963                     col++;
52964                 }
52965                 row++;
52966             }
52967         }
52968         return null;
52969     },
52970
52971     // private
52972     getSelections : function(){
52973         return this.selModel.getSelections();
52974     },
52975
52976     /**
52977      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52978      * but if manual update is required this method will initiate it.
52979      */
52980     autoSize : function(){
52981         if(this.rendered){
52982             this.view.layout();
52983             if(this.view.adjustForScroll){
52984                 this.view.adjustForScroll();
52985             }
52986         }
52987     },
52988
52989     /**
52990      * Returns the grid's underlying element.
52991      * @return {Element} The element
52992      */
52993     getGridEl : function(){
52994         return this.container;
52995     },
52996
52997     // private for compatibility, overridden by editor grid
52998     stopEditing : function(){},
52999
53000     /**
53001      * Returns the grid's SelectionModel.
53002      * @return {SelectionModel}
53003      */
53004     getSelectionModel : function(){
53005         if(!this.selModel){
53006             this.selModel = new Roo.grid.RowSelectionModel();
53007         }
53008         return this.selModel;
53009     },
53010
53011     /**
53012      * Returns the grid's DataSource.
53013      * @return {DataSource}
53014      */
53015     getDataSource : function(){
53016         return this.dataSource;
53017     },
53018
53019     /**
53020      * Returns the grid's ColumnModel.
53021      * @return {ColumnModel}
53022      */
53023     getColumnModel : function(){
53024         return this.colModel;
53025     },
53026
53027     /**
53028      * Returns the grid's GridView object.
53029      * @return {GridView}
53030      */
53031     getView : function(){
53032         if(!this.view){
53033             this.view = new Roo.grid.GridView(this.viewConfig);
53034         }
53035         return this.view;
53036     },
53037     /**
53038      * Called to get grid's drag proxy text, by default returns this.ddText.
53039      * @return {String}
53040      */
53041     getDragDropText : function(){
53042         var count = this.selModel.getCount();
53043         return String.format(this.ddText, count, count == 1 ? '' : 's');
53044     }
53045 });
53046 /**
53047  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
53048  * %0 is replaced with the number of selected rows.
53049  * @type String
53050  */
53051 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
53052  * Based on:
53053  * Ext JS Library 1.1.1
53054  * Copyright(c) 2006-2007, Ext JS, LLC.
53055  *
53056  * Originally Released Under LGPL - original licence link has changed is not relivant.
53057  *
53058  * Fork - LGPL
53059  * <script type="text/javascript">
53060  */
53061  
53062 Roo.grid.AbstractGridView = function(){
53063         this.grid = null;
53064         
53065         this.events = {
53066             "beforerowremoved" : true,
53067             "beforerowsinserted" : true,
53068             "beforerefresh" : true,
53069             "rowremoved" : true,
53070             "rowsinserted" : true,
53071             "rowupdated" : true,
53072             "refresh" : true
53073         };
53074     Roo.grid.AbstractGridView.superclass.constructor.call(this);
53075 };
53076
53077 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
53078     rowClass : "x-grid-row",
53079     cellClass : "x-grid-cell",
53080     tdClass : "x-grid-td",
53081     hdClass : "x-grid-hd",
53082     splitClass : "x-grid-hd-split",
53083     
53084     init: function(grid){
53085         this.grid = grid;
53086                 var cid = this.grid.getGridEl().id;
53087         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
53088         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
53089         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
53090         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
53091         },
53092         
53093     getColumnRenderers : function(){
53094         var renderers = [];
53095         var cm = this.grid.colModel;
53096         var colCount = cm.getColumnCount();
53097         for(var i = 0; i < colCount; i++){
53098             renderers[i] = cm.getRenderer(i);
53099         }
53100         return renderers;
53101     },
53102     
53103     getColumnIds : function(){
53104         var ids = [];
53105         var cm = this.grid.colModel;
53106         var colCount = cm.getColumnCount();
53107         for(var i = 0; i < colCount; i++){
53108             ids[i] = cm.getColumnId(i);
53109         }
53110         return ids;
53111     },
53112     
53113     getDataIndexes : function(){
53114         if(!this.indexMap){
53115             this.indexMap = this.buildIndexMap();
53116         }
53117         return this.indexMap.colToData;
53118     },
53119     
53120     getColumnIndexByDataIndex : function(dataIndex){
53121         if(!this.indexMap){
53122             this.indexMap = this.buildIndexMap();
53123         }
53124         return this.indexMap.dataToCol[dataIndex];
53125     },
53126     
53127     /**
53128      * Set a css style for a column dynamically. 
53129      * @param {Number} colIndex The index of the column
53130      * @param {String} name The css property name
53131      * @param {String} value The css value
53132      */
53133     setCSSStyle : function(colIndex, name, value){
53134         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
53135         Roo.util.CSS.updateRule(selector, name, value);
53136     },
53137     
53138     generateRules : function(cm){
53139         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
53140         Roo.util.CSS.removeStyleSheet(rulesId);
53141         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53142             var cid = cm.getColumnId(i);
53143             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
53144                          this.tdSelector, cid, " {\n}\n",
53145                          this.hdSelector, cid, " {\n}\n",
53146                          this.splitSelector, cid, " {\n}\n");
53147         }
53148         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53149     }
53150 });/*
53151  * Based on:
53152  * Ext JS Library 1.1.1
53153  * Copyright(c) 2006-2007, Ext JS, LLC.
53154  *
53155  * Originally Released Under LGPL - original licence link has changed is not relivant.
53156  *
53157  * Fork - LGPL
53158  * <script type="text/javascript">
53159  */
53160
53161 // private
53162 // This is a support class used internally by the Grid components
53163 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
53164     this.grid = grid;
53165     this.view = grid.getView();
53166     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53167     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
53168     if(hd2){
53169         this.setHandleElId(Roo.id(hd));
53170         this.setOuterHandleElId(Roo.id(hd2));
53171     }
53172     this.scroll = false;
53173 };
53174 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
53175     maxDragWidth: 120,
53176     getDragData : function(e){
53177         var t = Roo.lib.Event.getTarget(e);
53178         var h = this.view.findHeaderCell(t);
53179         if(h){
53180             return {ddel: h.firstChild, header:h};
53181         }
53182         return false;
53183     },
53184
53185     onInitDrag : function(e){
53186         this.view.headersDisabled = true;
53187         var clone = this.dragData.ddel.cloneNode(true);
53188         clone.id = Roo.id();
53189         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
53190         this.proxy.update(clone);
53191         return true;
53192     },
53193
53194     afterValidDrop : function(){
53195         var v = this.view;
53196         setTimeout(function(){
53197             v.headersDisabled = false;
53198         }, 50);
53199     },
53200
53201     afterInvalidDrop : function(){
53202         var v = this.view;
53203         setTimeout(function(){
53204             v.headersDisabled = false;
53205         }, 50);
53206     }
53207 });
53208 /*
53209  * Based on:
53210  * Ext JS Library 1.1.1
53211  * Copyright(c) 2006-2007, Ext JS, LLC.
53212  *
53213  * Originally Released Under LGPL - original licence link has changed is not relivant.
53214  *
53215  * Fork - LGPL
53216  * <script type="text/javascript">
53217  */
53218 // private
53219 // This is a support class used internally by the Grid components
53220 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
53221     this.grid = grid;
53222     this.view = grid.getView();
53223     // split the proxies so they don't interfere with mouse events
53224     this.proxyTop = Roo.DomHelper.append(document.body, {
53225         cls:"col-move-top", html:"&#160;"
53226     }, true);
53227     this.proxyBottom = Roo.DomHelper.append(document.body, {
53228         cls:"col-move-bottom", html:"&#160;"
53229     }, true);
53230     this.proxyTop.hide = this.proxyBottom.hide = function(){
53231         this.setLeftTop(-100,-100);
53232         this.setStyle("visibility", "hidden");
53233     };
53234     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53235     // temporarily disabled
53236     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
53237     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
53238 };
53239 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
53240     proxyOffsets : [-4, -9],
53241     fly: Roo.Element.fly,
53242
53243     getTargetFromEvent : function(e){
53244         var t = Roo.lib.Event.getTarget(e);
53245         var cindex = this.view.findCellIndex(t);
53246         if(cindex !== false){
53247             return this.view.getHeaderCell(cindex);
53248         }
53249         return null;
53250     },
53251
53252     nextVisible : function(h){
53253         var v = this.view, cm = this.grid.colModel;
53254         h = h.nextSibling;
53255         while(h){
53256             if(!cm.isHidden(v.getCellIndex(h))){
53257                 return h;
53258             }
53259             h = h.nextSibling;
53260         }
53261         return null;
53262     },
53263
53264     prevVisible : function(h){
53265         var v = this.view, cm = this.grid.colModel;
53266         h = h.prevSibling;
53267         while(h){
53268             if(!cm.isHidden(v.getCellIndex(h))){
53269                 return h;
53270             }
53271             h = h.prevSibling;
53272         }
53273         return null;
53274     },
53275
53276     positionIndicator : function(h, n, e){
53277         var x = Roo.lib.Event.getPageX(e);
53278         var r = Roo.lib.Dom.getRegion(n.firstChild);
53279         var px, pt, py = r.top + this.proxyOffsets[1];
53280         if((r.right - x) <= (r.right-r.left)/2){
53281             px = r.right+this.view.borderWidth;
53282             pt = "after";
53283         }else{
53284             px = r.left;
53285             pt = "before";
53286         }
53287         var oldIndex = this.view.getCellIndex(h);
53288         var newIndex = this.view.getCellIndex(n);
53289
53290         if(this.grid.colModel.isFixed(newIndex)){
53291             return false;
53292         }
53293
53294         var locked = this.grid.colModel.isLocked(newIndex);
53295
53296         if(pt == "after"){
53297             newIndex++;
53298         }
53299         if(oldIndex < newIndex){
53300             newIndex--;
53301         }
53302         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
53303             return false;
53304         }
53305         px +=  this.proxyOffsets[0];
53306         this.proxyTop.setLeftTop(px, py);
53307         this.proxyTop.show();
53308         if(!this.bottomOffset){
53309             this.bottomOffset = this.view.mainHd.getHeight();
53310         }
53311         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
53312         this.proxyBottom.show();
53313         return pt;
53314     },
53315
53316     onNodeEnter : function(n, dd, e, data){
53317         if(data.header != n){
53318             this.positionIndicator(data.header, n, e);
53319         }
53320     },
53321
53322     onNodeOver : function(n, dd, e, data){
53323         var result = false;
53324         if(data.header != n){
53325             result = this.positionIndicator(data.header, n, e);
53326         }
53327         if(!result){
53328             this.proxyTop.hide();
53329             this.proxyBottom.hide();
53330         }
53331         return result ? this.dropAllowed : this.dropNotAllowed;
53332     },
53333
53334     onNodeOut : function(n, dd, e, data){
53335         this.proxyTop.hide();
53336         this.proxyBottom.hide();
53337     },
53338
53339     onNodeDrop : function(n, dd, e, data){
53340         var h = data.header;
53341         if(h != n){
53342             var cm = this.grid.colModel;
53343             var x = Roo.lib.Event.getPageX(e);
53344             var r = Roo.lib.Dom.getRegion(n.firstChild);
53345             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
53346             var oldIndex = this.view.getCellIndex(h);
53347             var newIndex = this.view.getCellIndex(n);
53348             var locked = cm.isLocked(newIndex);
53349             if(pt == "after"){
53350                 newIndex++;
53351             }
53352             if(oldIndex < newIndex){
53353                 newIndex--;
53354             }
53355             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
53356                 return false;
53357             }
53358             cm.setLocked(oldIndex, locked, true);
53359             cm.moveColumn(oldIndex, newIndex);
53360             this.grid.fireEvent("columnmove", oldIndex, newIndex);
53361             return true;
53362         }
53363         return false;
53364     }
53365 });
53366 /*
53367  * Based on:
53368  * Ext JS Library 1.1.1
53369  * Copyright(c) 2006-2007, Ext JS, LLC.
53370  *
53371  * Originally Released Under LGPL - original licence link has changed is not relivant.
53372  *
53373  * Fork - LGPL
53374  * <script type="text/javascript">
53375  */
53376   
53377 /**
53378  * @class Roo.grid.GridView
53379  * @extends Roo.util.Observable
53380  *
53381  * @constructor
53382  * @param {Object} config
53383  */
53384 Roo.grid.GridView = function(config){
53385     Roo.grid.GridView.superclass.constructor.call(this);
53386     this.el = null;
53387
53388     Roo.apply(this, config);
53389 };
53390
53391 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
53392
53393     unselectable :  'unselectable="on"',
53394     unselectableCls :  'x-unselectable',
53395     
53396     
53397     rowClass : "x-grid-row",
53398
53399     cellClass : "x-grid-col",
53400
53401     tdClass : "x-grid-td",
53402
53403     hdClass : "x-grid-hd",
53404
53405     splitClass : "x-grid-split",
53406
53407     sortClasses : ["sort-asc", "sort-desc"],
53408
53409     enableMoveAnim : false,
53410
53411     hlColor: "C3DAF9",
53412
53413     dh : Roo.DomHelper,
53414
53415     fly : Roo.Element.fly,
53416
53417     css : Roo.util.CSS,
53418
53419     borderWidth: 1,
53420
53421     splitOffset: 3,
53422
53423     scrollIncrement : 22,
53424
53425     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
53426
53427     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
53428
53429     bind : function(ds, cm){
53430         if(this.ds){
53431             this.ds.un("load", this.onLoad, this);
53432             this.ds.un("datachanged", this.onDataChange, this);
53433             this.ds.un("add", this.onAdd, this);
53434             this.ds.un("remove", this.onRemove, this);
53435             this.ds.un("update", this.onUpdate, this);
53436             this.ds.un("clear", this.onClear, this);
53437         }
53438         if(ds){
53439             ds.on("load", this.onLoad, this);
53440             ds.on("datachanged", this.onDataChange, this);
53441             ds.on("add", this.onAdd, this);
53442             ds.on("remove", this.onRemove, this);
53443             ds.on("update", this.onUpdate, this);
53444             ds.on("clear", this.onClear, this);
53445         }
53446         this.ds = ds;
53447
53448         if(this.cm){
53449             this.cm.un("widthchange", this.onColWidthChange, this);
53450             this.cm.un("headerchange", this.onHeaderChange, this);
53451             this.cm.un("hiddenchange", this.onHiddenChange, this);
53452             this.cm.un("columnmoved", this.onColumnMove, this);
53453             this.cm.un("columnlockchange", this.onColumnLock, this);
53454         }
53455         if(cm){
53456             this.generateRules(cm);
53457             cm.on("widthchange", this.onColWidthChange, this);
53458             cm.on("headerchange", this.onHeaderChange, this);
53459             cm.on("hiddenchange", this.onHiddenChange, this);
53460             cm.on("columnmoved", this.onColumnMove, this);
53461             cm.on("columnlockchange", this.onColumnLock, this);
53462         }
53463         this.cm = cm;
53464     },
53465
53466     init: function(grid){
53467         Roo.grid.GridView.superclass.init.call(this, grid);
53468
53469         this.bind(grid.dataSource, grid.colModel);
53470
53471         grid.on("headerclick", this.handleHeaderClick, this);
53472
53473         if(grid.trackMouseOver){
53474             grid.on("mouseover", this.onRowOver, this);
53475             grid.on("mouseout", this.onRowOut, this);
53476         }
53477         grid.cancelTextSelection = function(){};
53478         this.gridId = grid.id;
53479
53480         var tpls = this.templates || {};
53481
53482         if(!tpls.master){
53483             tpls.master = new Roo.Template(
53484                '<div class="x-grid" hidefocus="true">',
53485                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
53486                   '<div class="x-grid-topbar"></div>',
53487                   '<div class="x-grid-scroller"><div></div></div>',
53488                   '<div class="x-grid-locked">',
53489                       '<div class="x-grid-header">{lockedHeader}</div>',
53490                       '<div class="x-grid-body">{lockedBody}</div>',
53491                   "</div>",
53492                   '<div class="x-grid-viewport">',
53493                       '<div class="x-grid-header">{header}</div>',
53494                       '<div class="x-grid-body">{body}</div>',
53495                   "</div>",
53496                   '<div class="x-grid-bottombar"></div>',
53497                  
53498                   '<div class="x-grid-resize-proxy">&#160;</div>',
53499                "</div>"
53500             );
53501             tpls.master.disableformats = true;
53502         }
53503
53504         if(!tpls.header){
53505             tpls.header = new Roo.Template(
53506                '<table border="0" cellspacing="0" cellpadding="0">',
53507                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
53508                "</table>{splits}"
53509             );
53510             tpls.header.disableformats = true;
53511         }
53512         tpls.header.compile();
53513
53514         if(!tpls.hcell){
53515             tpls.hcell = new Roo.Template(
53516                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
53517                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
53518                 "</div></td>"
53519              );
53520              tpls.hcell.disableFormats = true;
53521         }
53522         tpls.hcell.compile();
53523
53524         if(!tpls.hsplit){
53525             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
53526                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
53527             tpls.hsplit.disableFormats = true;
53528         }
53529         tpls.hsplit.compile();
53530
53531         if(!tpls.body){
53532             tpls.body = new Roo.Template(
53533                '<table border="0" cellspacing="0" cellpadding="0">',
53534                "<tbody>{rows}</tbody>",
53535                "</table>"
53536             );
53537             tpls.body.disableFormats = true;
53538         }
53539         tpls.body.compile();
53540
53541         if(!tpls.row){
53542             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
53543             tpls.row.disableFormats = true;
53544         }
53545         tpls.row.compile();
53546
53547         if(!tpls.cell){
53548             tpls.cell = new Roo.Template(
53549                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
53550                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
53551                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
53552                 "</td>"
53553             );
53554             tpls.cell.disableFormats = true;
53555         }
53556         tpls.cell.compile();
53557
53558         this.templates = tpls;
53559     },
53560
53561     // remap these for backwards compat
53562     onColWidthChange : function(){
53563         this.updateColumns.apply(this, arguments);
53564     },
53565     onHeaderChange : function(){
53566         this.updateHeaders.apply(this, arguments);
53567     }, 
53568     onHiddenChange : function(){
53569         this.handleHiddenChange.apply(this, arguments);
53570     },
53571     onColumnMove : function(){
53572         this.handleColumnMove.apply(this, arguments);
53573     },
53574     onColumnLock : function(){
53575         this.handleLockChange.apply(this, arguments);
53576     },
53577
53578     onDataChange : function(){
53579         this.refresh();
53580         this.updateHeaderSortState();
53581     },
53582
53583     onClear : function(){
53584         this.refresh();
53585     },
53586
53587     onUpdate : function(ds, record){
53588         this.refreshRow(record);
53589     },
53590
53591     refreshRow : function(record){
53592         var ds = this.ds, index;
53593         if(typeof record == 'number'){
53594             index = record;
53595             record = ds.getAt(index);
53596         }else{
53597             index = ds.indexOf(record);
53598         }
53599         this.insertRows(ds, index, index, true);
53600         this.onRemove(ds, record, index+1, true);
53601         this.syncRowHeights(index, index);
53602         this.layout();
53603         this.fireEvent("rowupdated", this, index, record);
53604     },
53605
53606     onAdd : function(ds, records, index){
53607         this.insertRows(ds, index, index + (records.length-1));
53608     },
53609
53610     onRemove : function(ds, record, index, isUpdate){
53611         if(isUpdate !== true){
53612             this.fireEvent("beforerowremoved", this, index, record);
53613         }
53614         var bt = this.getBodyTable(), lt = this.getLockedTable();
53615         if(bt.rows[index]){
53616             bt.firstChild.removeChild(bt.rows[index]);
53617         }
53618         if(lt.rows[index]){
53619             lt.firstChild.removeChild(lt.rows[index]);
53620         }
53621         if(isUpdate !== true){
53622             this.stripeRows(index);
53623             this.syncRowHeights(index, index);
53624             this.layout();
53625             this.fireEvent("rowremoved", this, index, record);
53626         }
53627     },
53628
53629     onLoad : function(){
53630         this.scrollToTop();
53631     },
53632
53633     /**
53634      * Scrolls the grid to the top
53635      */
53636     scrollToTop : function(){
53637         if(this.scroller){
53638             this.scroller.dom.scrollTop = 0;
53639             this.syncScroll();
53640         }
53641     },
53642
53643     /**
53644      * Gets a panel in the header of the grid that can be used for toolbars etc.
53645      * After modifying the contents of this panel a call to grid.autoSize() may be
53646      * required to register any changes in size.
53647      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53648      * @return Roo.Element
53649      */
53650     getHeaderPanel : function(doShow){
53651         if(doShow){
53652             this.headerPanel.show();
53653         }
53654         return this.headerPanel;
53655     },
53656
53657     /**
53658      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53659      * After modifying the contents of this panel a call to grid.autoSize() may be
53660      * required to register any changes in size.
53661      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53662      * @return Roo.Element
53663      */
53664     getFooterPanel : function(doShow){
53665         if(doShow){
53666             this.footerPanel.show();
53667         }
53668         return this.footerPanel;
53669     },
53670
53671     initElements : function(){
53672         var E = Roo.Element;
53673         var el = this.grid.getGridEl().dom.firstChild;
53674         var cs = el.childNodes;
53675
53676         this.el = new E(el);
53677         
53678          this.focusEl = new E(el.firstChild);
53679         this.focusEl.swallowEvent("click", true);
53680         
53681         this.headerPanel = new E(cs[1]);
53682         this.headerPanel.enableDisplayMode("block");
53683
53684         this.scroller = new E(cs[2]);
53685         this.scrollSizer = new E(this.scroller.dom.firstChild);
53686
53687         this.lockedWrap = new E(cs[3]);
53688         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53689         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53690
53691         this.mainWrap = new E(cs[4]);
53692         this.mainHd = new E(this.mainWrap.dom.firstChild);
53693         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53694
53695         this.footerPanel = new E(cs[5]);
53696         this.footerPanel.enableDisplayMode("block");
53697
53698         this.resizeProxy = new E(cs[6]);
53699
53700         this.headerSelector = String.format(
53701            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53702            this.lockedHd.id, this.mainHd.id
53703         );
53704
53705         this.splitterSelector = String.format(
53706            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53707            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53708         );
53709     },
53710     idToCssName : function(s)
53711     {
53712         return s.replace(/[^a-z0-9]+/ig, '-');
53713     },
53714
53715     getHeaderCell : function(index){
53716         return Roo.DomQuery.select(this.headerSelector)[index];
53717     },
53718
53719     getHeaderCellMeasure : function(index){
53720         return this.getHeaderCell(index).firstChild;
53721     },
53722
53723     getHeaderCellText : function(index){
53724         return this.getHeaderCell(index).firstChild.firstChild;
53725     },
53726
53727     getLockedTable : function(){
53728         return this.lockedBody.dom.firstChild;
53729     },
53730
53731     getBodyTable : function(){
53732         return this.mainBody.dom.firstChild;
53733     },
53734
53735     getLockedRow : function(index){
53736         return this.getLockedTable().rows[index];
53737     },
53738
53739     getRow : function(index){
53740         return this.getBodyTable().rows[index];
53741     },
53742
53743     getRowComposite : function(index){
53744         if(!this.rowEl){
53745             this.rowEl = new Roo.CompositeElementLite();
53746         }
53747         var els = [], lrow, mrow;
53748         if(lrow = this.getLockedRow(index)){
53749             els.push(lrow);
53750         }
53751         if(mrow = this.getRow(index)){
53752             els.push(mrow);
53753         }
53754         this.rowEl.elements = els;
53755         return this.rowEl;
53756     },
53757     /**
53758      * Gets the 'td' of the cell
53759      * 
53760      * @param {Integer} rowIndex row to select
53761      * @param {Integer} colIndex column to select
53762      * 
53763      * @return {Object} 
53764      */
53765     getCell : function(rowIndex, colIndex){
53766         var locked = this.cm.getLockedCount();
53767         var source;
53768         if(colIndex < locked){
53769             source = this.lockedBody.dom.firstChild;
53770         }else{
53771             source = this.mainBody.dom.firstChild;
53772             colIndex -= locked;
53773         }
53774         return source.rows[rowIndex].childNodes[colIndex];
53775     },
53776
53777     getCellText : function(rowIndex, colIndex){
53778         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53779     },
53780
53781     getCellBox : function(cell){
53782         var b = this.fly(cell).getBox();
53783         if(Roo.isOpera){ // opera fails to report the Y
53784             b.y = cell.offsetTop + this.mainBody.getY();
53785         }
53786         return b;
53787     },
53788
53789     getCellIndex : function(cell){
53790         var id = String(cell.className).match(this.cellRE);
53791         if(id){
53792             return parseInt(id[1], 10);
53793         }
53794         return 0;
53795     },
53796
53797     findHeaderIndex : function(n){
53798         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53799         return r ? this.getCellIndex(r) : false;
53800     },
53801
53802     findHeaderCell : function(n){
53803         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53804         return r ? r : false;
53805     },
53806
53807     findRowIndex : function(n){
53808         if(!n){
53809             return false;
53810         }
53811         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53812         return r ? r.rowIndex : false;
53813     },
53814
53815     findCellIndex : function(node){
53816         var stop = this.el.dom;
53817         while(node && node != stop){
53818             if(this.findRE.test(node.className)){
53819                 return this.getCellIndex(node);
53820             }
53821             node = node.parentNode;
53822         }
53823         return false;
53824     },
53825
53826     getColumnId : function(index){
53827         return this.cm.getColumnId(index);
53828     },
53829
53830     getSplitters : function()
53831     {
53832         if(this.splitterSelector){
53833            return Roo.DomQuery.select(this.splitterSelector);
53834         }else{
53835             return null;
53836       }
53837     },
53838
53839     getSplitter : function(index){
53840         return this.getSplitters()[index];
53841     },
53842
53843     onRowOver : function(e, t){
53844         var row;
53845         if((row = this.findRowIndex(t)) !== false){
53846             this.getRowComposite(row).addClass("x-grid-row-over");
53847         }
53848     },
53849
53850     onRowOut : function(e, t){
53851         var row;
53852         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53853             this.getRowComposite(row).removeClass("x-grid-row-over");
53854         }
53855     },
53856
53857     renderHeaders : function(){
53858         var cm = this.cm;
53859         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53860         var cb = [], lb = [], sb = [], lsb = [], p = {};
53861         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53862             p.cellId = "x-grid-hd-0-" + i;
53863             p.splitId = "x-grid-csplit-0-" + i;
53864             p.id = cm.getColumnId(i);
53865             p.title = cm.getColumnTooltip(i) || "";
53866             p.value = cm.getColumnHeader(i) || "";
53867             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53868             if(!cm.isLocked(i)){
53869                 cb[cb.length] = ct.apply(p);
53870                 sb[sb.length] = st.apply(p);
53871             }else{
53872                 lb[lb.length] = ct.apply(p);
53873                 lsb[lsb.length] = st.apply(p);
53874             }
53875         }
53876         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53877                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53878     },
53879
53880     updateHeaders : function(){
53881         var html = this.renderHeaders();
53882         this.lockedHd.update(html[0]);
53883         this.mainHd.update(html[1]);
53884     },
53885
53886     /**
53887      * Focuses the specified row.
53888      * @param {Number} row The row index
53889      */
53890     focusRow : function(row)
53891     {
53892         //Roo.log('GridView.focusRow');
53893         var x = this.scroller.dom.scrollLeft;
53894         this.focusCell(row, 0, false);
53895         this.scroller.dom.scrollLeft = x;
53896     },
53897
53898     /**
53899      * Focuses the specified cell.
53900      * @param {Number} row The row index
53901      * @param {Number} col The column index
53902      * @param {Boolean} hscroll false to disable horizontal scrolling
53903      */
53904     focusCell : function(row, col, hscroll)
53905     {
53906         //Roo.log('GridView.focusCell');
53907         var el = this.ensureVisible(row, col, hscroll);
53908         this.focusEl.alignTo(el, "tl-tl");
53909         if(Roo.isGecko){
53910             this.focusEl.focus();
53911         }else{
53912             this.focusEl.focus.defer(1, this.focusEl);
53913         }
53914     },
53915
53916     /**
53917      * Scrolls the specified cell into view
53918      * @param {Number} row The row index
53919      * @param {Number} col The column index
53920      * @param {Boolean} hscroll false to disable horizontal scrolling
53921      */
53922     ensureVisible : function(row, col, hscroll)
53923     {
53924         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
53925         //return null; //disable for testing.
53926         if(typeof row != "number"){
53927             row = row.rowIndex;
53928         }
53929         if(row < 0 && row >= this.ds.getCount()){
53930             return  null;
53931         }
53932         col = (col !== undefined ? col : 0);
53933         var cm = this.grid.colModel;
53934         while(cm.isHidden(col)){
53935             col++;
53936         }
53937
53938         var el = this.getCell(row, col);
53939         if(!el){
53940             return null;
53941         }
53942         var c = this.scroller.dom;
53943
53944         var ctop = parseInt(el.offsetTop, 10);
53945         var cleft = parseInt(el.offsetLeft, 10);
53946         var cbot = ctop + el.offsetHeight;
53947         var cright = cleft + el.offsetWidth;
53948         
53949         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
53950         var stop = parseInt(c.scrollTop, 10);
53951         var sleft = parseInt(c.scrollLeft, 10);
53952         var sbot = stop + ch;
53953         var sright = sleft + c.clientWidth;
53954         /*
53955         Roo.log('GridView.ensureVisible:' +
53956                 ' ctop:' + ctop +
53957                 ' c.clientHeight:' + c.clientHeight +
53958                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
53959                 ' stop:' + stop +
53960                 ' cbot:' + cbot +
53961                 ' sbot:' + sbot +
53962                 ' ch:' + ch  
53963                 );
53964         */
53965         if(ctop < stop){
53966              c.scrollTop = ctop;
53967             //Roo.log("set scrolltop to ctop DISABLE?");
53968         }else if(cbot > sbot){
53969             //Roo.log("set scrolltop to cbot-ch");
53970             c.scrollTop = cbot-ch;
53971         }
53972         
53973         if(hscroll !== false){
53974             if(cleft < sleft){
53975                 c.scrollLeft = cleft;
53976             }else if(cright > sright){
53977                 c.scrollLeft = cright-c.clientWidth;
53978             }
53979         }
53980          
53981         return el;
53982     },
53983
53984     updateColumns : function(){
53985         this.grid.stopEditing();
53986         var cm = this.grid.colModel, colIds = this.getColumnIds();
53987         //var totalWidth = cm.getTotalWidth();
53988         var pos = 0;
53989         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53990             //if(cm.isHidden(i)) continue;
53991             var w = cm.getColumnWidth(i);
53992             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53993             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53994         }
53995         this.updateSplitters();
53996     },
53997
53998     generateRules : function(cm){
53999         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
54000         Roo.util.CSS.removeStyleSheet(rulesId);
54001         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54002             var cid = cm.getColumnId(i);
54003             var align = '';
54004             if(cm.config[i].align){
54005                 align = 'text-align:'+cm.config[i].align+';';
54006             }
54007             var hidden = '';
54008             if(cm.isHidden(i)){
54009                 hidden = 'display:none;';
54010             }
54011             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
54012             ruleBuf.push(
54013                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
54014                     this.hdSelector, cid, " {\n", align, width, "}\n",
54015                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
54016                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
54017         }
54018         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
54019     },
54020
54021     updateSplitters : function(){
54022         var cm = this.cm, s = this.getSplitters();
54023         if(s){ // splitters not created yet
54024             var pos = 0, locked = true;
54025             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54026                 if(cm.isHidden(i)) continue;
54027                 var w = cm.getColumnWidth(i); // make sure it's a number
54028                 if(!cm.isLocked(i) && locked){
54029                     pos = 0;
54030                     locked = false;
54031                 }
54032                 pos += w;
54033                 s[i].style.left = (pos-this.splitOffset) + "px";
54034             }
54035         }
54036     },
54037
54038     handleHiddenChange : function(colModel, colIndex, hidden){
54039         if(hidden){
54040             this.hideColumn(colIndex);
54041         }else{
54042             this.unhideColumn(colIndex);
54043         }
54044     },
54045
54046     hideColumn : function(colIndex){
54047         var cid = this.getColumnId(colIndex);
54048         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
54049         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
54050         if(Roo.isSafari){
54051             this.updateHeaders();
54052         }
54053         this.updateSplitters();
54054         this.layout();
54055     },
54056
54057     unhideColumn : function(colIndex){
54058         var cid = this.getColumnId(colIndex);
54059         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
54060         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
54061
54062         if(Roo.isSafari){
54063             this.updateHeaders();
54064         }
54065         this.updateSplitters();
54066         this.layout();
54067     },
54068
54069     insertRows : function(dm, firstRow, lastRow, isUpdate){
54070         if(firstRow == 0 && lastRow == dm.getCount()-1){
54071             this.refresh();
54072         }else{
54073             if(!isUpdate){
54074                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
54075             }
54076             var s = this.getScrollState();
54077             var markup = this.renderRows(firstRow, lastRow);
54078             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
54079             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
54080             this.restoreScroll(s);
54081             if(!isUpdate){
54082                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
54083                 this.syncRowHeights(firstRow, lastRow);
54084                 this.stripeRows(firstRow);
54085                 this.layout();
54086             }
54087         }
54088     },
54089
54090     bufferRows : function(markup, target, index){
54091         var before = null, trows = target.rows, tbody = target.tBodies[0];
54092         if(index < trows.length){
54093             before = trows[index];
54094         }
54095         var b = document.createElement("div");
54096         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
54097         var rows = b.firstChild.rows;
54098         for(var i = 0, len = rows.length; i < len; i++){
54099             if(before){
54100                 tbody.insertBefore(rows[0], before);
54101             }else{
54102                 tbody.appendChild(rows[0]);
54103             }
54104         }
54105         b.innerHTML = "";
54106         b = null;
54107     },
54108
54109     deleteRows : function(dm, firstRow, lastRow){
54110         if(dm.getRowCount()<1){
54111             this.fireEvent("beforerefresh", this);
54112             this.mainBody.update("");
54113             this.lockedBody.update("");
54114             this.fireEvent("refresh", this);
54115         }else{
54116             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
54117             var bt = this.getBodyTable();
54118             var tbody = bt.firstChild;
54119             var rows = bt.rows;
54120             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
54121                 tbody.removeChild(rows[firstRow]);
54122             }
54123             this.stripeRows(firstRow);
54124             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
54125         }
54126     },
54127
54128     updateRows : function(dataSource, firstRow, lastRow){
54129         var s = this.getScrollState();
54130         this.refresh();
54131         this.restoreScroll(s);
54132     },
54133
54134     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
54135         if(!noRefresh){
54136            this.refresh();
54137         }
54138         this.updateHeaderSortState();
54139     },
54140
54141     getScrollState : function(){
54142         
54143         var sb = this.scroller.dom;
54144         return {left: sb.scrollLeft, top: sb.scrollTop};
54145     },
54146
54147     stripeRows : function(startRow){
54148         if(!this.grid.stripeRows || this.ds.getCount() < 1){
54149             return;
54150         }
54151         startRow = startRow || 0;
54152         var rows = this.getBodyTable().rows;
54153         var lrows = this.getLockedTable().rows;
54154         var cls = ' x-grid-row-alt ';
54155         for(var i = startRow, len = rows.length; i < len; i++){
54156             var row = rows[i], lrow = lrows[i];
54157             var isAlt = ((i+1) % 2 == 0);
54158             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
54159             if(isAlt == hasAlt){
54160                 continue;
54161             }
54162             if(isAlt){
54163                 row.className += " x-grid-row-alt";
54164             }else{
54165                 row.className = row.className.replace("x-grid-row-alt", "");
54166             }
54167             if(lrow){
54168                 lrow.className = row.className;
54169             }
54170         }
54171     },
54172
54173     restoreScroll : function(state){
54174         //Roo.log('GridView.restoreScroll');
54175         var sb = this.scroller.dom;
54176         sb.scrollLeft = state.left;
54177         sb.scrollTop = state.top;
54178         this.syncScroll();
54179     },
54180
54181     syncScroll : function(){
54182         //Roo.log('GridView.syncScroll');
54183         var sb = this.scroller.dom;
54184         var sh = this.mainHd.dom;
54185         var bs = this.mainBody.dom;
54186         var lv = this.lockedBody.dom;
54187         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
54188         lv.scrollTop = bs.scrollTop = sb.scrollTop;
54189     },
54190
54191     handleScroll : function(e){
54192         this.syncScroll();
54193         var sb = this.scroller.dom;
54194         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
54195         e.stopEvent();
54196     },
54197
54198     handleWheel : function(e){
54199         var d = e.getWheelDelta();
54200         this.scroller.dom.scrollTop -= d*22;
54201         // set this here to prevent jumpy scrolling on large tables
54202         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
54203         e.stopEvent();
54204     },
54205
54206     renderRows : function(startRow, endRow){
54207         // pull in all the crap needed to render rows
54208         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
54209         var colCount = cm.getColumnCount();
54210
54211         if(ds.getCount() < 1){
54212             return ["", ""];
54213         }
54214
54215         // build a map for all the columns
54216         var cs = [];
54217         for(var i = 0; i < colCount; i++){
54218             var name = cm.getDataIndex(i);
54219             cs[i] = {
54220                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
54221                 renderer : cm.getRenderer(i),
54222                 id : cm.getColumnId(i),
54223                 locked : cm.isLocked(i)
54224             };
54225         }
54226
54227         startRow = startRow || 0;
54228         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
54229
54230         // records to render
54231         var rs = ds.getRange(startRow, endRow);
54232
54233         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
54234     },
54235
54236     // As much as I hate to duplicate code, this was branched because FireFox really hates
54237     // [].join("") on strings. The performance difference was substantial enough to
54238     // branch this function
54239     doRender : Roo.isGecko ?
54240             function(cs, rs, ds, startRow, colCount, stripe){
54241                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54242                 // buffers
54243                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54244                 
54245                 var hasListener = this.grid.hasListener('rowclass');
54246                 var rowcfg = {};
54247                 for(var j = 0, len = rs.length; j < len; j++){
54248                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
54249                     for(var i = 0; i < colCount; i++){
54250                         c = cs[i];
54251                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54252                         p.id = c.id;
54253                         p.css = p.attr = "";
54254                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54255                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54256                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54257                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54258                         }
54259                         var markup = ct.apply(p);
54260                         if(!c.locked){
54261                             cb+= markup;
54262                         }else{
54263                             lcb+= markup;
54264                         }
54265                     }
54266                     var alt = [];
54267                     if(stripe && ((rowIndex+1) % 2 == 0)){
54268                         alt.push("x-grid-row-alt")
54269                     }
54270                     if(r.dirty){
54271                         alt.push(  " x-grid-dirty-row");
54272                     }
54273                     rp.cells = lcb;
54274                     if(this.getRowClass){
54275                         alt.push(this.getRowClass(r, rowIndex));
54276                     }
54277                     if (hasListener) {
54278                         rowcfg = {
54279                              
54280                             record: r,
54281                             rowIndex : rowIndex,
54282                             rowClass : ''
54283                         }
54284                         this.grid.fireEvent('rowclass', this, rowcfg);
54285                         alt.push(rowcfg.rowClass);
54286                     }
54287                     rp.alt = alt.join(" ");
54288                     lbuf+= rt.apply(rp);
54289                     rp.cells = cb;
54290                     buf+=  rt.apply(rp);
54291                 }
54292                 return [lbuf, buf];
54293             } :
54294             function(cs, rs, ds, startRow, colCount, stripe){
54295                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54296                 // buffers
54297                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54298                 var hasListener = this.grid.hasListener('rowclass');
54299  
54300                 var rowcfg = {};
54301                 for(var j = 0, len = rs.length; j < len; j++){
54302                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
54303                     for(var i = 0; i < colCount; i++){
54304                         c = cs[i];
54305                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54306                         p.id = c.id;
54307                         p.css = p.attr = "";
54308                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54309                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54310                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54311                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54312                         }
54313                         
54314                         var markup = ct.apply(p);
54315                         if(!c.locked){
54316                             cb[cb.length] = markup;
54317                         }else{
54318                             lcb[lcb.length] = markup;
54319                         }
54320                     }
54321                     var alt = [];
54322                     if(stripe && ((rowIndex+1) % 2 == 0)){
54323                         alt.push( "x-grid-row-alt");
54324                     }
54325                     if(r.dirty){
54326                         alt.push(" x-grid-dirty-row");
54327                     }
54328                     rp.cells = lcb;
54329                     if(this.getRowClass){
54330                         alt.push( this.getRowClass(r, rowIndex));
54331                     }
54332                     if (hasListener) {
54333                         rowcfg = {
54334                              
54335                             record: r,
54336                             rowIndex : rowIndex,
54337                             rowClass : ''
54338                         }
54339                         this.grid.fireEvent('rowclass', this, rowcfg);
54340                         alt.push(rowcfg.rowClass);
54341                     }
54342                     rp.alt = alt.join(" ");
54343                     rp.cells = lcb.join("");
54344                     lbuf[lbuf.length] = rt.apply(rp);
54345                     rp.cells = cb.join("");
54346                     buf[buf.length] =  rt.apply(rp);
54347                 }
54348                 return [lbuf.join(""), buf.join("")];
54349             },
54350
54351     renderBody : function(){
54352         var markup = this.renderRows();
54353         var bt = this.templates.body;
54354         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
54355     },
54356
54357     /**
54358      * Refreshes the grid
54359      * @param {Boolean} headersToo
54360      */
54361     refresh : function(headersToo){
54362         this.fireEvent("beforerefresh", this);
54363         this.grid.stopEditing();
54364         var result = this.renderBody();
54365         this.lockedBody.update(result[0]);
54366         this.mainBody.update(result[1]);
54367         if(headersToo === true){
54368             this.updateHeaders();
54369             this.updateColumns();
54370             this.updateSplitters();
54371             this.updateHeaderSortState();
54372         }
54373         this.syncRowHeights();
54374         this.layout();
54375         this.fireEvent("refresh", this);
54376     },
54377
54378     handleColumnMove : function(cm, oldIndex, newIndex){
54379         this.indexMap = null;
54380         var s = this.getScrollState();
54381         this.refresh(true);
54382         this.restoreScroll(s);
54383         this.afterMove(newIndex);
54384     },
54385
54386     afterMove : function(colIndex){
54387         if(this.enableMoveAnim && Roo.enableFx){
54388             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
54389         }
54390         // if multisort - fix sortOrder, and reload..
54391         if (this.grid.dataSource.multiSort) {
54392             // the we can call sort again..
54393             var dm = this.grid.dataSource;
54394             var cm = this.grid.colModel;
54395             var so = [];
54396             for(var i = 0; i < cm.config.length; i++ ) {
54397                 
54398                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
54399                     continue; // dont' bother, it's not in sort list or being set.
54400                 }
54401                 
54402                 so.push(cm.config[i].dataIndex);
54403             };
54404             dm.sortOrder = so;
54405             dm.load(dm.lastOptions);
54406             
54407             
54408         }
54409         
54410     },
54411
54412     updateCell : function(dm, rowIndex, dataIndex){
54413         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
54414         if(typeof colIndex == "undefined"){ // not present in grid
54415             return;
54416         }
54417         var cm = this.grid.colModel;
54418         var cell = this.getCell(rowIndex, colIndex);
54419         var cellText = this.getCellText(rowIndex, colIndex);
54420
54421         var p = {
54422             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
54423             id : cm.getColumnId(colIndex),
54424             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
54425         };
54426         var renderer = cm.getRenderer(colIndex);
54427         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
54428         if(typeof val == "undefined" || val === "") val = "&#160;";
54429         cellText.innerHTML = val;
54430         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
54431         this.syncRowHeights(rowIndex, rowIndex);
54432     },
54433
54434     calcColumnWidth : function(colIndex, maxRowsToMeasure){
54435         var maxWidth = 0;
54436         if(this.grid.autoSizeHeaders){
54437             var h = this.getHeaderCellMeasure(colIndex);
54438             maxWidth = Math.max(maxWidth, h.scrollWidth);
54439         }
54440         var tb, index;
54441         if(this.cm.isLocked(colIndex)){
54442             tb = this.getLockedTable();
54443             index = colIndex;
54444         }else{
54445             tb = this.getBodyTable();
54446             index = colIndex - this.cm.getLockedCount();
54447         }
54448         if(tb && tb.rows){
54449             var rows = tb.rows;
54450             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
54451             for(var i = 0; i < stopIndex; i++){
54452                 var cell = rows[i].childNodes[index].firstChild;
54453                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
54454             }
54455         }
54456         return maxWidth + /*margin for error in IE*/ 5;
54457     },
54458     /**
54459      * Autofit a column to its content.
54460      * @param {Number} colIndex
54461      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
54462      */
54463      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
54464          if(this.cm.isHidden(colIndex)){
54465              return; // can't calc a hidden column
54466          }
54467         if(forceMinSize){
54468             var cid = this.cm.getColumnId(colIndex);
54469             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
54470            if(this.grid.autoSizeHeaders){
54471                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
54472            }
54473         }
54474         var newWidth = this.calcColumnWidth(colIndex);
54475         this.cm.setColumnWidth(colIndex,
54476             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
54477         if(!suppressEvent){
54478             this.grid.fireEvent("columnresize", colIndex, newWidth);
54479         }
54480     },
54481
54482     /**
54483      * Autofits all columns to their content and then expands to fit any extra space in the grid
54484      */
54485      autoSizeColumns : function(){
54486         var cm = this.grid.colModel;
54487         var colCount = cm.getColumnCount();
54488         for(var i = 0; i < colCount; i++){
54489             this.autoSizeColumn(i, true, true);
54490         }
54491         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
54492             this.fitColumns();
54493         }else{
54494             this.updateColumns();
54495             this.layout();
54496         }
54497     },
54498
54499     /**
54500      * Autofits all columns to the grid's width proportionate with their current size
54501      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
54502      */
54503     fitColumns : function(reserveScrollSpace){
54504         var cm = this.grid.colModel;
54505         var colCount = cm.getColumnCount();
54506         var cols = [];
54507         var width = 0;
54508         var i, w;
54509         for (i = 0; i < colCount; i++){
54510             if(!cm.isHidden(i) && !cm.isFixed(i)){
54511                 w = cm.getColumnWidth(i);
54512                 cols.push(i);
54513                 cols.push(w);
54514                 width += w;
54515             }
54516         }
54517         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
54518         if(reserveScrollSpace){
54519             avail -= 17;
54520         }
54521         var frac = (avail - cm.getTotalWidth())/width;
54522         while (cols.length){
54523             w = cols.pop();
54524             i = cols.pop();
54525             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
54526         }
54527         this.updateColumns();
54528         this.layout();
54529     },
54530
54531     onRowSelect : function(rowIndex){
54532         var row = this.getRowComposite(rowIndex);
54533         row.addClass("x-grid-row-selected");
54534     },
54535
54536     onRowDeselect : function(rowIndex){
54537         var row = this.getRowComposite(rowIndex);
54538         row.removeClass("x-grid-row-selected");
54539     },
54540
54541     onCellSelect : function(row, col){
54542         var cell = this.getCell(row, col);
54543         if(cell){
54544             Roo.fly(cell).addClass("x-grid-cell-selected");
54545         }
54546     },
54547
54548     onCellDeselect : function(row, col){
54549         var cell = this.getCell(row, col);
54550         if(cell){
54551             Roo.fly(cell).removeClass("x-grid-cell-selected");
54552         }
54553     },
54554
54555     updateHeaderSortState : function(){
54556         
54557         // sort state can be single { field: xxx, direction : yyy}
54558         // or   { xxx=>ASC , yyy : DESC ..... }
54559         
54560         var mstate = {};
54561         if (!this.ds.multiSort) { 
54562             var state = this.ds.getSortState();
54563             if(!state){
54564                 return;
54565             }
54566             mstate[state.field] = state.direction;
54567             // FIXME... - this is not used here.. but might be elsewhere..
54568             this.sortState = state;
54569             
54570         } else {
54571             mstate = this.ds.sortToggle;
54572         }
54573         //remove existing sort classes..
54574         
54575         var sc = this.sortClasses;
54576         var hds = this.el.select(this.headerSelector).removeClass(sc);
54577         
54578         for(var f in mstate) {
54579         
54580             var sortColumn = this.cm.findColumnIndex(f);
54581             
54582             if(sortColumn != -1){
54583                 var sortDir = mstate[f];        
54584                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
54585             }
54586         }
54587         
54588          
54589         
54590     },
54591
54592
54593     handleHeaderClick : function(g, index,e){
54594         
54595         Roo.log("header click");
54596         
54597         if (Roo.isTouch) {
54598             // touch events on header are handled by context
54599             this.handleHdCtx(g,index,e);
54600             return;
54601         }
54602         
54603         
54604         if(this.headersDisabled){
54605             return;
54606         }
54607         var dm = g.dataSource, cm = g.colModel;
54608         if(!cm.isSortable(index)){
54609             return;
54610         }
54611         g.stopEditing();
54612         
54613         if (dm.multiSort) {
54614             // update the sortOrder
54615             var so = [];
54616             for(var i = 0; i < cm.config.length; i++ ) {
54617                 
54618                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
54619                     continue; // dont' bother, it's not in sort list or being set.
54620                 }
54621                 
54622                 so.push(cm.config[i].dataIndex);
54623             };
54624             dm.sortOrder = so;
54625         }
54626         
54627         
54628         dm.sort(cm.getDataIndex(index));
54629     },
54630
54631
54632     destroy : function(){
54633         if(this.colMenu){
54634             this.colMenu.removeAll();
54635             Roo.menu.MenuMgr.unregister(this.colMenu);
54636             this.colMenu.getEl().remove();
54637             delete this.colMenu;
54638         }
54639         if(this.hmenu){
54640             this.hmenu.removeAll();
54641             Roo.menu.MenuMgr.unregister(this.hmenu);
54642             this.hmenu.getEl().remove();
54643             delete this.hmenu;
54644         }
54645         if(this.grid.enableColumnMove){
54646             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54647             if(dds){
54648                 for(var dd in dds){
54649                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54650                         var elid = dds[dd].dragElId;
54651                         dds[dd].unreg();
54652                         Roo.get(elid).remove();
54653                     } else if(dds[dd].config.isTarget){
54654                         dds[dd].proxyTop.remove();
54655                         dds[dd].proxyBottom.remove();
54656                         dds[dd].unreg();
54657                     }
54658                     if(Roo.dd.DDM.locationCache[dd]){
54659                         delete Roo.dd.DDM.locationCache[dd];
54660                     }
54661                 }
54662                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54663             }
54664         }
54665         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54666         this.bind(null, null);
54667         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54668     },
54669
54670     handleLockChange : function(){
54671         this.refresh(true);
54672     },
54673
54674     onDenyColumnLock : function(){
54675
54676     },
54677
54678     onDenyColumnHide : function(){
54679
54680     },
54681
54682     handleHdMenuClick : function(item){
54683         var index = this.hdCtxIndex;
54684         var cm = this.cm, ds = this.ds;
54685         switch(item.id){
54686             case "asc":
54687                 ds.sort(cm.getDataIndex(index), "ASC");
54688                 break;
54689             case "desc":
54690                 ds.sort(cm.getDataIndex(index), "DESC");
54691                 break;
54692             case "lock":
54693                 var lc = cm.getLockedCount();
54694                 if(cm.getColumnCount(true) <= lc+1){
54695                     this.onDenyColumnLock();
54696                     return;
54697                 }
54698                 if(lc != index){
54699                     cm.setLocked(index, true, true);
54700                     cm.moveColumn(index, lc);
54701                     this.grid.fireEvent("columnmove", index, lc);
54702                 }else{
54703                     cm.setLocked(index, true);
54704                 }
54705             break;
54706             case "unlock":
54707                 var lc = cm.getLockedCount();
54708                 if((lc-1) != index){
54709                     cm.setLocked(index, false, true);
54710                     cm.moveColumn(index, lc-1);
54711                     this.grid.fireEvent("columnmove", index, lc-1);
54712                 }else{
54713                     cm.setLocked(index, false);
54714                 }
54715             break;
54716             case 'wider': // used to expand cols on touch..
54717             case 'narrow':
54718                 var cw = cm.getColumnWidth(index);
54719                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54720                 cw = Math.max(0, cw);
54721                 cw = Math.min(cw,4000);
54722                 cm.setColumnWidth(index, cw);
54723                 break;
54724                 
54725             default:
54726                 index = cm.getIndexById(item.id.substr(4));
54727                 if(index != -1){
54728                     if(item.checked && cm.getColumnCount(true) <= 1){
54729                         this.onDenyColumnHide();
54730                         return false;
54731                     }
54732                     cm.setHidden(index, item.checked);
54733                 }
54734         }
54735         return true;
54736     },
54737
54738     beforeColMenuShow : function(){
54739         var cm = this.cm,  colCount = cm.getColumnCount();
54740         this.colMenu.removeAll();
54741         for(var i = 0; i < colCount; i++){
54742             this.colMenu.add(new Roo.menu.CheckItem({
54743                 id: "col-"+cm.getColumnId(i),
54744                 text: cm.getColumnHeader(i),
54745                 checked: !cm.isHidden(i),
54746                 hideOnClick:false
54747             }));
54748         }
54749     },
54750
54751     handleHdCtx : function(g, index, e){
54752         e.stopEvent();
54753         var hd = this.getHeaderCell(index);
54754         this.hdCtxIndex = index;
54755         var ms = this.hmenu.items, cm = this.cm;
54756         ms.get("asc").setDisabled(!cm.isSortable(index));
54757         ms.get("desc").setDisabled(!cm.isSortable(index));
54758         if(this.grid.enableColLock !== false){
54759             ms.get("lock").setDisabled(cm.isLocked(index));
54760             ms.get("unlock").setDisabled(!cm.isLocked(index));
54761         }
54762         this.hmenu.show(hd, "tl-bl");
54763     },
54764
54765     handleHdOver : function(e){
54766         var hd = this.findHeaderCell(e.getTarget());
54767         if(hd && !this.headersDisabled){
54768             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54769                this.fly(hd).addClass("x-grid-hd-over");
54770             }
54771         }
54772     },
54773
54774     handleHdOut : function(e){
54775         var hd = this.findHeaderCell(e.getTarget());
54776         if(hd){
54777             this.fly(hd).removeClass("x-grid-hd-over");
54778         }
54779     },
54780
54781     handleSplitDblClick : function(e, t){
54782         var i = this.getCellIndex(t);
54783         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54784             this.autoSizeColumn(i, true);
54785             this.layout();
54786         }
54787     },
54788
54789     render : function(){
54790
54791         var cm = this.cm;
54792         var colCount = cm.getColumnCount();
54793
54794         if(this.grid.monitorWindowResize === true){
54795             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54796         }
54797         var header = this.renderHeaders();
54798         var body = this.templates.body.apply({rows:""});
54799         var html = this.templates.master.apply({
54800             lockedBody: body,
54801             body: body,
54802             lockedHeader: header[0],
54803             header: header[1]
54804         });
54805
54806         //this.updateColumns();
54807
54808         this.grid.getGridEl().dom.innerHTML = html;
54809
54810         this.initElements();
54811         
54812         // a kludge to fix the random scolling effect in webkit
54813         this.el.on("scroll", function() {
54814             this.el.dom.scrollTop=0; // hopefully not recursive..
54815         },this);
54816
54817         this.scroller.on("scroll", this.handleScroll, this);
54818         this.lockedBody.on("mousewheel", this.handleWheel, this);
54819         this.mainBody.on("mousewheel", this.handleWheel, this);
54820
54821         this.mainHd.on("mouseover", this.handleHdOver, this);
54822         this.mainHd.on("mouseout", this.handleHdOut, this);
54823         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54824                 {delegate: "."+this.splitClass});
54825
54826         this.lockedHd.on("mouseover", this.handleHdOver, this);
54827         this.lockedHd.on("mouseout", this.handleHdOut, this);
54828         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54829                 {delegate: "."+this.splitClass});
54830
54831         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54832             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54833         }
54834
54835         this.updateSplitters();
54836
54837         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54838             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54839             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54840         }
54841
54842         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54843             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54844             this.hmenu.add(
54845                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54846                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54847             );
54848             if(this.grid.enableColLock !== false){
54849                 this.hmenu.add('-',
54850                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54851                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54852                 );
54853             }
54854             if (Roo.isTouch) {
54855                  this.hmenu.add('-',
54856                     {id:"wider", text: this.columnsWiderText},
54857                     {id:"narrow", text: this.columnsNarrowText }
54858                 );
54859                 
54860                  
54861             }
54862             
54863             if(this.grid.enableColumnHide !== false){
54864
54865                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54866                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54867                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54868
54869                 this.hmenu.add('-',
54870                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54871                 );
54872             }
54873             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54874
54875             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54876         }
54877
54878         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54879             this.dd = new Roo.grid.GridDragZone(this.grid, {
54880                 ddGroup : this.grid.ddGroup || 'GridDD'
54881             });
54882             
54883         }
54884
54885         /*
54886         for(var i = 0; i < colCount; i++){
54887             if(cm.isHidden(i)){
54888                 this.hideColumn(i);
54889             }
54890             if(cm.config[i].align){
54891                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54892                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54893             }
54894         }*/
54895         
54896         this.updateHeaderSortState();
54897
54898         this.beforeInitialResize();
54899         this.layout(true);
54900
54901         // two part rendering gives faster view to the user
54902         this.renderPhase2.defer(1, this);
54903     },
54904
54905     renderPhase2 : function(){
54906         // render the rows now
54907         this.refresh();
54908         if(this.grid.autoSizeColumns){
54909             this.autoSizeColumns();
54910         }
54911     },
54912
54913     beforeInitialResize : function(){
54914
54915     },
54916
54917     onColumnSplitterMoved : function(i, w){
54918         this.userResized = true;
54919         var cm = this.grid.colModel;
54920         cm.setColumnWidth(i, w, true);
54921         var cid = cm.getColumnId(i);
54922         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54923         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54924         this.updateSplitters();
54925         this.layout();
54926         this.grid.fireEvent("columnresize", i, w);
54927     },
54928
54929     syncRowHeights : function(startIndex, endIndex){
54930         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
54931             startIndex = startIndex || 0;
54932             var mrows = this.getBodyTable().rows;
54933             var lrows = this.getLockedTable().rows;
54934             var len = mrows.length-1;
54935             endIndex = Math.min(endIndex || len, len);
54936             for(var i = startIndex; i <= endIndex; i++){
54937                 var m = mrows[i], l = lrows[i];
54938                 var h = Math.max(m.offsetHeight, l.offsetHeight);
54939                 m.style.height = l.style.height = h + "px";
54940             }
54941         }
54942     },
54943
54944     layout : function(initialRender, is2ndPass){
54945         var g = this.grid;
54946         var auto = g.autoHeight;
54947         var scrollOffset = 16;
54948         var c = g.getGridEl(), cm = this.cm,
54949                 expandCol = g.autoExpandColumn,
54950                 gv = this;
54951         //c.beginMeasure();
54952
54953         if(!c.dom.offsetWidth){ // display:none?
54954             if(initialRender){
54955                 this.lockedWrap.show();
54956                 this.mainWrap.show();
54957             }
54958             return;
54959         }
54960
54961         var hasLock = this.cm.isLocked(0);
54962
54963         var tbh = this.headerPanel.getHeight();
54964         var bbh = this.footerPanel.getHeight();
54965
54966         if(auto){
54967             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
54968             var newHeight = ch + c.getBorderWidth("tb");
54969             if(g.maxHeight){
54970                 newHeight = Math.min(g.maxHeight, newHeight);
54971             }
54972             c.setHeight(newHeight);
54973         }
54974
54975         if(g.autoWidth){
54976             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54977         }
54978
54979         var s = this.scroller;
54980
54981         var csize = c.getSize(true);
54982
54983         this.el.setSize(csize.width, csize.height);
54984
54985         this.headerPanel.setWidth(csize.width);
54986         this.footerPanel.setWidth(csize.width);
54987
54988         var hdHeight = this.mainHd.getHeight();
54989         var vw = csize.width;
54990         var vh = csize.height - (tbh + bbh);
54991
54992         s.setSize(vw, vh);
54993
54994         var bt = this.getBodyTable();
54995         var ltWidth = hasLock ?
54996                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54997
54998         var scrollHeight = bt.offsetHeight;
54999         var scrollWidth = ltWidth + bt.offsetWidth;
55000         var vscroll = false, hscroll = false;
55001
55002         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
55003
55004         var lw = this.lockedWrap, mw = this.mainWrap;
55005         var lb = this.lockedBody, mb = this.mainBody;
55006
55007         setTimeout(function(){
55008             var t = s.dom.offsetTop;
55009             var w = s.dom.clientWidth,
55010                 h = s.dom.clientHeight;
55011
55012             lw.setTop(t);
55013             lw.setSize(ltWidth, h);
55014
55015             mw.setLeftTop(ltWidth, t);
55016             mw.setSize(w-ltWidth, h);
55017
55018             lb.setHeight(h-hdHeight);
55019             mb.setHeight(h-hdHeight);
55020
55021             if(is2ndPass !== true && !gv.userResized && expandCol){
55022                 // high speed resize without full column calculation
55023                 
55024                 var ci = cm.getIndexById(expandCol);
55025                 if (ci < 0) {
55026                     ci = cm.findColumnIndex(expandCol);
55027                 }
55028                 ci = Math.max(0, ci); // make sure it's got at least the first col.
55029                 var expandId = cm.getColumnId(ci);
55030                 var  tw = cm.getTotalWidth(false);
55031                 var currentWidth = cm.getColumnWidth(ci);
55032                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
55033                 if(currentWidth != cw){
55034                     cm.setColumnWidth(ci, cw, true);
55035                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
55036                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
55037                     gv.updateSplitters();
55038                     gv.layout(false, true);
55039                 }
55040             }
55041
55042             if(initialRender){
55043                 lw.show();
55044                 mw.show();
55045             }
55046             //c.endMeasure();
55047         }, 10);
55048     },
55049
55050     onWindowResize : function(){
55051         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
55052             return;
55053         }
55054         this.layout();
55055     },
55056
55057     appendFooter : function(parentEl){
55058         return null;
55059     },
55060
55061     sortAscText : "Sort Ascending",
55062     sortDescText : "Sort Descending",
55063     lockText : "Lock Column",
55064     unlockText : "Unlock Column",
55065     columnsText : "Columns",
55066  
55067     columnsWiderText : "Wider",
55068     columnsNarrowText : "Thinner"
55069 });
55070
55071
55072 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
55073     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
55074     this.proxy.el.addClass('x-grid3-col-dd');
55075 };
55076
55077 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
55078     handleMouseDown : function(e){
55079
55080     },
55081
55082     callHandleMouseDown : function(e){
55083         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
55084     }
55085 });
55086 /*
55087  * Based on:
55088  * Ext JS Library 1.1.1
55089  * Copyright(c) 2006-2007, Ext JS, LLC.
55090  *
55091  * Originally Released Under LGPL - original licence link has changed is not relivant.
55092  *
55093  * Fork - LGPL
55094  * <script type="text/javascript">
55095  */
55096  
55097 // private
55098 // This is a support class used internally by the Grid components
55099 Roo.grid.SplitDragZone = function(grid, hd, hd2){
55100     this.grid = grid;
55101     this.view = grid.getView();
55102     this.proxy = this.view.resizeProxy;
55103     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
55104         "gridSplitters" + this.grid.getGridEl().id, {
55105         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
55106     });
55107     this.setHandleElId(Roo.id(hd));
55108     this.setOuterHandleElId(Roo.id(hd2));
55109     this.scroll = false;
55110 };
55111 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
55112     fly: Roo.Element.fly,
55113
55114     b4StartDrag : function(x, y){
55115         this.view.headersDisabled = true;
55116         this.proxy.setHeight(this.view.mainWrap.getHeight());
55117         var w = this.cm.getColumnWidth(this.cellIndex);
55118         var minw = Math.max(w-this.grid.minColumnWidth, 0);
55119         this.resetConstraints();
55120         this.setXConstraint(minw, 1000);
55121         this.setYConstraint(0, 0);
55122         this.minX = x - minw;
55123         this.maxX = x + 1000;
55124         this.startPos = x;
55125         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
55126     },
55127
55128
55129     handleMouseDown : function(e){
55130         ev = Roo.EventObject.setEvent(e);
55131         var t = this.fly(ev.getTarget());
55132         if(t.hasClass("x-grid-split")){
55133             this.cellIndex = this.view.getCellIndex(t.dom);
55134             this.split = t.dom;
55135             this.cm = this.grid.colModel;
55136             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
55137                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
55138             }
55139         }
55140     },
55141
55142     endDrag : function(e){
55143         this.view.headersDisabled = false;
55144         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
55145         var diff = endX - this.startPos;
55146         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
55147     },
55148
55149     autoOffset : function(){
55150         this.setDelta(0,0);
55151     }
55152 });/*
55153  * Based on:
55154  * Ext JS Library 1.1.1
55155  * Copyright(c) 2006-2007, Ext JS, LLC.
55156  *
55157  * Originally Released Under LGPL - original licence link has changed is not relivant.
55158  *
55159  * Fork - LGPL
55160  * <script type="text/javascript">
55161  */
55162  
55163 // private
55164 // This is a support class used internally by the Grid components
55165 Roo.grid.GridDragZone = function(grid, config){
55166     this.view = grid.getView();
55167     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
55168     if(this.view.lockedBody){
55169         this.setHandleElId(Roo.id(this.view.mainBody.dom));
55170         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
55171     }
55172     this.scroll = false;
55173     this.grid = grid;
55174     this.ddel = document.createElement('div');
55175     this.ddel.className = 'x-grid-dd-wrap';
55176 };
55177
55178 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
55179     ddGroup : "GridDD",
55180
55181     getDragData : function(e){
55182         var t = Roo.lib.Event.getTarget(e);
55183         var rowIndex = this.view.findRowIndex(t);
55184         var sm = this.grid.selModel;
55185             
55186         //Roo.log(rowIndex);
55187         
55188         if (sm.getSelectedCell) {
55189             // cell selection..
55190             if (!sm.getSelectedCell()) {
55191                 return false;
55192             }
55193             if (rowIndex != sm.getSelectedCell()[0]) {
55194                 return false;
55195             }
55196         
55197         }
55198         
55199         if(rowIndex !== false){
55200             
55201             // if editorgrid.. 
55202             
55203             
55204             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
55205                
55206             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
55207               //  
55208             //}
55209             if (e.hasModifier()){
55210                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
55211             }
55212             
55213             Roo.log("getDragData");
55214             
55215             return {
55216                 grid: this.grid,
55217                 ddel: this.ddel,
55218                 rowIndex: rowIndex,
55219                 selections:sm.getSelections ? sm.getSelections() : (
55220                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
55221                 )
55222             };
55223         }
55224         return false;
55225     },
55226
55227     onInitDrag : function(e){
55228         var data = this.dragData;
55229         this.ddel.innerHTML = this.grid.getDragDropText();
55230         this.proxy.update(this.ddel);
55231         // fire start drag?
55232     },
55233
55234     afterRepair : function(){
55235         this.dragging = false;
55236     },
55237
55238     getRepairXY : function(e, data){
55239         return false;
55240     },
55241
55242     onEndDrag : function(data, e){
55243         // fire end drag?
55244     },
55245
55246     onValidDrop : function(dd, e, id){
55247         // fire drag drop?
55248         this.hideProxy();
55249     },
55250
55251     beforeInvalidDrop : function(e, id){
55252
55253     }
55254 });/*
55255  * Based on:
55256  * Ext JS Library 1.1.1
55257  * Copyright(c) 2006-2007, Ext JS, LLC.
55258  *
55259  * Originally Released Under LGPL - original licence link has changed is not relivant.
55260  *
55261  * Fork - LGPL
55262  * <script type="text/javascript">
55263  */
55264  
55265
55266 /**
55267  * @class Roo.grid.ColumnModel
55268  * @extends Roo.util.Observable
55269  * This is the default implementation of a ColumnModel used by the Grid. It defines
55270  * the columns in the grid.
55271  * <br>Usage:<br>
55272  <pre><code>
55273  var colModel = new Roo.grid.ColumnModel([
55274         {header: "Ticker", width: 60, sortable: true, locked: true},
55275         {header: "Company Name", width: 150, sortable: true},
55276         {header: "Market Cap.", width: 100, sortable: true},
55277         {header: "$ Sales", width: 100, sortable: true, renderer: money},
55278         {header: "Employees", width: 100, sortable: true, resizable: false}
55279  ]);
55280  </code></pre>
55281  * <p>
55282  
55283  * The config options listed for this class are options which may appear in each
55284  * individual column definition.
55285  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
55286  * @constructor
55287  * @param {Object} config An Array of column config objects. See this class's
55288  * config objects for details.
55289 */
55290 Roo.grid.ColumnModel = function(config){
55291         /**
55292      * The config passed into the constructor
55293      */
55294     this.config = config;
55295     this.lookup = {};
55296
55297     // if no id, create one
55298     // if the column does not have a dataIndex mapping,
55299     // map it to the order it is in the config
55300     for(var i = 0, len = config.length; i < len; i++){
55301         var c = config[i];
55302         if(typeof c.dataIndex == "undefined"){
55303             c.dataIndex = i;
55304         }
55305         if(typeof c.renderer == "string"){
55306             c.renderer = Roo.util.Format[c.renderer];
55307         }
55308         if(typeof c.id == "undefined"){
55309             c.id = Roo.id();
55310         }
55311         if(c.editor && c.editor.xtype){
55312             c.editor  = Roo.factory(c.editor, Roo.grid);
55313         }
55314         if(c.editor && c.editor.isFormField){
55315             c.editor = new Roo.grid.GridEditor(c.editor);
55316         }
55317         this.lookup[c.id] = c;
55318     }
55319
55320     /**
55321      * The width of columns which have no width specified (defaults to 100)
55322      * @type Number
55323      */
55324     this.defaultWidth = 100;
55325
55326     /**
55327      * Default sortable of columns which have no sortable specified (defaults to false)
55328      * @type Boolean
55329      */
55330     this.defaultSortable = false;
55331
55332     this.addEvents({
55333         /**
55334              * @event widthchange
55335              * Fires when the width of a column changes.
55336              * @param {ColumnModel} this
55337              * @param {Number} columnIndex The column index
55338              * @param {Number} newWidth The new width
55339              */
55340             "widthchange": true,
55341         /**
55342              * @event headerchange
55343              * Fires when the text of a header changes.
55344              * @param {ColumnModel} this
55345              * @param {Number} columnIndex The column index
55346              * @param {Number} newText The new header text
55347              */
55348             "headerchange": true,
55349         /**
55350              * @event hiddenchange
55351              * Fires when a column is hidden or "unhidden".
55352              * @param {ColumnModel} this
55353              * @param {Number} columnIndex The column index
55354              * @param {Boolean} hidden true if hidden, false otherwise
55355              */
55356             "hiddenchange": true,
55357             /**
55358          * @event columnmoved
55359          * Fires when a column is moved.
55360          * @param {ColumnModel} this
55361          * @param {Number} oldIndex
55362          * @param {Number} newIndex
55363          */
55364         "columnmoved" : true,
55365         /**
55366          * @event columlockchange
55367          * Fires when a column's locked state is changed
55368          * @param {ColumnModel} this
55369          * @param {Number} colIndex
55370          * @param {Boolean} locked true if locked
55371          */
55372         "columnlockchange" : true
55373     });
55374     Roo.grid.ColumnModel.superclass.constructor.call(this);
55375 };
55376 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
55377     /**
55378      * @cfg {String} header The header text to display in the Grid view.
55379      */
55380     /**
55381      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
55382      * {@link Roo.data.Record} definition from which to draw the column's value. If not
55383      * specified, the column's index is used as an index into the Record's data Array.
55384      */
55385     /**
55386      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
55387      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
55388      */
55389     /**
55390      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
55391      * Defaults to the value of the {@link #defaultSortable} property.
55392      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
55393      */
55394     /**
55395      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
55396      */
55397     /**
55398      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
55399      */
55400     /**
55401      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
55402      */
55403     /**
55404      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
55405      */
55406     /**
55407      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
55408      * given the cell's data value. See {@link #setRenderer}. If not specified, the
55409      * default renderer uses the raw data value. If an object is returned (bootstrap only)
55410      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
55411      */
55412        /**
55413      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
55414      */
55415     /**
55416      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
55417      */
55418     /**
55419      * @cfg {String} cursor (Optional)
55420      */
55421     /**
55422      * @cfg {String} tooltip (Optional)
55423      */
55424     /**
55425      * Returns the id of the column at the specified index.
55426      * @param {Number} index The column index
55427      * @return {String} the id
55428      */
55429     getColumnId : function(index){
55430         return this.config[index].id;
55431     },
55432
55433     /**
55434      * Returns the column for a specified id.
55435      * @param {String} id The column id
55436      * @return {Object} the column
55437      */
55438     getColumnById : function(id){
55439         return this.lookup[id];
55440     },
55441
55442     
55443     /**
55444      * Returns the column for a specified dataIndex.
55445      * @param {String} dataIndex The column dataIndex
55446      * @return {Object|Boolean} the column or false if not found
55447      */
55448     getColumnByDataIndex: function(dataIndex){
55449         var index = this.findColumnIndex(dataIndex);
55450         return index > -1 ? this.config[index] : false;
55451     },
55452     
55453     /**
55454      * Returns the index for a specified column id.
55455      * @param {String} id The column id
55456      * @return {Number} the index, or -1 if not found
55457      */
55458     getIndexById : function(id){
55459         for(var i = 0, len = this.config.length; i < len; i++){
55460             if(this.config[i].id == id){
55461                 return i;
55462             }
55463         }
55464         return -1;
55465     },
55466     
55467     /**
55468      * Returns the index for a specified column dataIndex.
55469      * @param {String} dataIndex The column dataIndex
55470      * @return {Number} the index, or -1 if not found
55471      */
55472     
55473     findColumnIndex : function(dataIndex){
55474         for(var i = 0, len = this.config.length; i < len; i++){
55475             if(this.config[i].dataIndex == dataIndex){
55476                 return i;
55477             }
55478         }
55479         return -1;
55480     },
55481     
55482     
55483     moveColumn : function(oldIndex, newIndex){
55484         var c = this.config[oldIndex];
55485         this.config.splice(oldIndex, 1);
55486         this.config.splice(newIndex, 0, c);
55487         this.dataMap = null;
55488         this.fireEvent("columnmoved", this, oldIndex, newIndex);
55489     },
55490
55491     isLocked : function(colIndex){
55492         return this.config[colIndex].locked === true;
55493     },
55494
55495     setLocked : function(colIndex, value, suppressEvent){
55496         if(this.isLocked(colIndex) == value){
55497             return;
55498         }
55499         this.config[colIndex].locked = value;
55500         if(!suppressEvent){
55501             this.fireEvent("columnlockchange", this, colIndex, value);
55502         }
55503     },
55504
55505     getTotalLockedWidth : function(){
55506         var totalWidth = 0;
55507         for(var i = 0; i < this.config.length; i++){
55508             if(this.isLocked(i) && !this.isHidden(i)){
55509                 this.totalWidth += this.getColumnWidth(i);
55510             }
55511         }
55512         return totalWidth;
55513     },
55514
55515     getLockedCount : function(){
55516         for(var i = 0, len = this.config.length; i < len; i++){
55517             if(!this.isLocked(i)){
55518                 return i;
55519             }
55520         }
55521     },
55522
55523     /**
55524      * Returns the number of columns.
55525      * @return {Number}
55526      */
55527     getColumnCount : function(visibleOnly){
55528         if(visibleOnly === true){
55529             var c = 0;
55530             for(var i = 0, len = this.config.length; i < len; i++){
55531                 if(!this.isHidden(i)){
55532                     c++;
55533                 }
55534             }
55535             return c;
55536         }
55537         return this.config.length;
55538     },
55539
55540     /**
55541      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
55542      * @param {Function} fn
55543      * @param {Object} scope (optional)
55544      * @return {Array} result
55545      */
55546     getColumnsBy : function(fn, scope){
55547         var r = [];
55548         for(var i = 0, len = this.config.length; i < len; i++){
55549             var c = this.config[i];
55550             if(fn.call(scope||this, c, i) === true){
55551                 r[r.length] = c;
55552             }
55553         }
55554         return r;
55555     },
55556
55557     /**
55558      * Returns true if the specified column is sortable.
55559      * @param {Number} col The column index
55560      * @return {Boolean}
55561      */
55562     isSortable : function(col){
55563         if(typeof this.config[col].sortable == "undefined"){
55564             return this.defaultSortable;
55565         }
55566         return this.config[col].sortable;
55567     },
55568
55569     /**
55570      * Returns the rendering (formatting) function defined for the column.
55571      * @param {Number} col The column index.
55572      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
55573      */
55574     getRenderer : function(col){
55575         if(!this.config[col].renderer){
55576             return Roo.grid.ColumnModel.defaultRenderer;
55577         }
55578         return this.config[col].renderer;
55579     },
55580
55581     /**
55582      * Sets the rendering (formatting) function for a column.
55583      * @param {Number} col The column index
55584      * @param {Function} fn The function to use to process the cell's raw data
55585      * to return HTML markup for the grid view. The render function is called with
55586      * the following parameters:<ul>
55587      * <li>Data value.</li>
55588      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
55589      * <li>css A CSS style string to apply to the table cell.</li>
55590      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
55591      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
55592      * <li>Row index</li>
55593      * <li>Column index</li>
55594      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
55595      */
55596     setRenderer : function(col, fn){
55597         this.config[col].renderer = fn;
55598     },
55599
55600     /**
55601      * Returns the width for the specified column.
55602      * @param {Number} col The column index
55603      * @return {Number}
55604      */
55605     getColumnWidth : function(col){
55606         return this.config[col].width * 1 || this.defaultWidth;
55607     },
55608
55609     /**
55610      * Sets the width for a column.
55611      * @param {Number} col The column index
55612      * @param {Number} width The new width
55613      */
55614     setColumnWidth : function(col, width, suppressEvent){
55615         this.config[col].width = width;
55616         this.totalWidth = null;
55617         if(!suppressEvent){
55618              this.fireEvent("widthchange", this, col, width);
55619         }
55620     },
55621
55622     /**
55623      * Returns the total width of all columns.
55624      * @param {Boolean} includeHidden True to include hidden column widths
55625      * @return {Number}
55626      */
55627     getTotalWidth : function(includeHidden){
55628         if(!this.totalWidth){
55629             this.totalWidth = 0;
55630             for(var i = 0, len = this.config.length; i < len; i++){
55631                 if(includeHidden || !this.isHidden(i)){
55632                     this.totalWidth += this.getColumnWidth(i);
55633                 }
55634             }
55635         }
55636         return this.totalWidth;
55637     },
55638
55639     /**
55640      * Returns the header for the specified column.
55641      * @param {Number} col The column index
55642      * @return {String}
55643      */
55644     getColumnHeader : function(col){
55645         return this.config[col].header;
55646     },
55647
55648     /**
55649      * Sets the header for a column.
55650      * @param {Number} col The column index
55651      * @param {String} header The new header
55652      */
55653     setColumnHeader : function(col, header){
55654         this.config[col].header = header;
55655         this.fireEvent("headerchange", this, col, header);
55656     },
55657
55658     /**
55659      * Returns the tooltip for the specified column.
55660      * @param {Number} col The column index
55661      * @return {String}
55662      */
55663     getColumnTooltip : function(col){
55664             return this.config[col].tooltip;
55665     },
55666     /**
55667      * Sets the tooltip for a column.
55668      * @param {Number} col The column index
55669      * @param {String} tooltip The new tooltip
55670      */
55671     setColumnTooltip : function(col, tooltip){
55672             this.config[col].tooltip = tooltip;
55673     },
55674
55675     /**
55676      * Returns the dataIndex for the specified column.
55677      * @param {Number} col The column index
55678      * @return {Number}
55679      */
55680     getDataIndex : function(col){
55681         return this.config[col].dataIndex;
55682     },
55683
55684     /**
55685      * Sets the dataIndex for a column.
55686      * @param {Number} col The column index
55687      * @param {Number} dataIndex The new dataIndex
55688      */
55689     setDataIndex : function(col, dataIndex){
55690         this.config[col].dataIndex = dataIndex;
55691     },
55692
55693     
55694     
55695     /**
55696      * Returns true if the cell is editable.
55697      * @param {Number} colIndex The column index
55698      * @param {Number} rowIndex The row index
55699      * @return {Boolean}
55700      */
55701     isCellEditable : function(colIndex, rowIndex){
55702         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55703     },
55704
55705     /**
55706      * Returns the editor defined for the cell/column.
55707      * return false or null to disable editing.
55708      * @param {Number} colIndex The column index
55709      * @param {Number} rowIndex The row index
55710      * @return {Object}
55711      */
55712     getCellEditor : function(colIndex, rowIndex){
55713         return this.config[colIndex].editor;
55714     },
55715
55716     /**
55717      * Sets if a column is editable.
55718      * @param {Number} col The column index
55719      * @param {Boolean} editable True if the column is editable
55720      */
55721     setEditable : function(col, editable){
55722         this.config[col].editable = editable;
55723     },
55724
55725
55726     /**
55727      * Returns true if the column is hidden.
55728      * @param {Number} colIndex The column index
55729      * @return {Boolean}
55730      */
55731     isHidden : function(colIndex){
55732         return this.config[colIndex].hidden;
55733     },
55734
55735
55736     /**
55737      * Returns true if the column width cannot be changed
55738      */
55739     isFixed : function(colIndex){
55740         return this.config[colIndex].fixed;
55741     },
55742
55743     /**
55744      * Returns true if the column can be resized
55745      * @return {Boolean}
55746      */
55747     isResizable : function(colIndex){
55748         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55749     },
55750     /**
55751      * Sets if a column is hidden.
55752      * @param {Number} colIndex The column index
55753      * @param {Boolean} hidden True if the column is hidden
55754      */
55755     setHidden : function(colIndex, hidden){
55756         this.config[colIndex].hidden = hidden;
55757         this.totalWidth = null;
55758         this.fireEvent("hiddenchange", this, colIndex, hidden);
55759     },
55760
55761     /**
55762      * Sets the editor for a column.
55763      * @param {Number} col The column index
55764      * @param {Object} editor The editor object
55765      */
55766     setEditor : function(col, editor){
55767         this.config[col].editor = editor;
55768     }
55769 });
55770
55771 Roo.grid.ColumnModel.defaultRenderer = function(value){
55772         if(typeof value == "string" && value.length < 1){
55773             return "&#160;";
55774         }
55775         return value;
55776 };
55777
55778 // Alias for backwards compatibility
55779 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55780 /*
55781  * Based on:
55782  * Ext JS Library 1.1.1
55783  * Copyright(c) 2006-2007, Ext JS, LLC.
55784  *
55785  * Originally Released Under LGPL - original licence link has changed is not relivant.
55786  *
55787  * Fork - LGPL
55788  * <script type="text/javascript">
55789  */
55790
55791 /**
55792  * @class Roo.grid.AbstractSelectionModel
55793  * @extends Roo.util.Observable
55794  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55795  * implemented by descendant classes.  This class should not be directly instantiated.
55796  * @constructor
55797  */
55798 Roo.grid.AbstractSelectionModel = function(){
55799     this.locked = false;
55800     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55801 };
55802
55803 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55804     /** @ignore Called by the grid automatically. Do not call directly. */
55805     init : function(grid){
55806         this.grid = grid;
55807         this.initEvents();
55808     },
55809
55810     /**
55811      * Locks the selections.
55812      */
55813     lock : function(){
55814         this.locked = true;
55815     },
55816
55817     /**
55818      * Unlocks the selections.
55819      */
55820     unlock : function(){
55821         this.locked = false;
55822     },
55823
55824     /**
55825      * Returns true if the selections are locked.
55826      * @return {Boolean}
55827      */
55828     isLocked : function(){
55829         return this.locked;
55830     }
55831 });/*
55832  * Based on:
55833  * Ext JS Library 1.1.1
55834  * Copyright(c) 2006-2007, Ext JS, LLC.
55835  *
55836  * Originally Released Under LGPL - original licence link has changed is not relivant.
55837  *
55838  * Fork - LGPL
55839  * <script type="text/javascript">
55840  */
55841 /**
55842  * @extends Roo.grid.AbstractSelectionModel
55843  * @class Roo.grid.RowSelectionModel
55844  * The default SelectionModel used by {@link Roo.grid.Grid}.
55845  * It supports multiple selections and keyboard selection/navigation. 
55846  * @constructor
55847  * @param {Object} config
55848  */
55849 Roo.grid.RowSelectionModel = function(config){
55850     Roo.apply(this, config);
55851     this.selections = new Roo.util.MixedCollection(false, function(o){
55852         return o.id;
55853     });
55854
55855     this.last = false;
55856     this.lastActive = false;
55857
55858     this.addEvents({
55859         /**
55860              * @event selectionchange
55861              * Fires when the selection changes
55862              * @param {SelectionModel} this
55863              */
55864             "selectionchange" : true,
55865         /**
55866              * @event afterselectionchange
55867              * Fires after the selection changes (eg. by key press or clicking)
55868              * @param {SelectionModel} this
55869              */
55870             "afterselectionchange" : true,
55871         /**
55872              * @event beforerowselect
55873              * Fires when a row is selected being selected, return false to cancel.
55874              * @param {SelectionModel} this
55875              * @param {Number} rowIndex The selected index
55876              * @param {Boolean} keepExisting False if other selections will be cleared
55877              */
55878             "beforerowselect" : true,
55879         /**
55880              * @event rowselect
55881              * Fires when a row is selected.
55882              * @param {SelectionModel} this
55883              * @param {Number} rowIndex The selected index
55884              * @param {Roo.data.Record} r The record
55885              */
55886             "rowselect" : true,
55887         /**
55888              * @event rowdeselect
55889              * Fires when a row is deselected.
55890              * @param {SelectionModel} this
55891              * @param {Number} rowIndex The selected index
55892              */
55893         "rowdeselect" : true
55894     });
55895     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55896     this.locked = false;
55897 };
55898
55899 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
55900     /**
55901      * @cfg {Boolean} singleSelect
55902      * True to allow selection of only one row at a time (defaults to false)
55903      */
55904     singleSelect : false,
55905
55906     // private
55907     initEvents : function(){
55908
55909         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
55910             this.grid.on("mousedown", this.handleMouseDown, this);
55911         }else{ // allow click to work like normal
55912             this.grid.on("rowclick", this.handleDragableRowClick, this);
55913         }
55914
55915         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
55916             "up" : function(e){
55917                 if(!e.shiftKey){
55918                     this.selectPrevious(e.shiftKey);
55919                 }else if(this.last !== false && this.lastActive !== false){
55920                     var last = this.last;
55921                     this.selectRange(this.last,  this.lastActive-1);
55922                     this.grid.getView().focusRow(this.lastActive);
55923                     if(last !== false){
55924                         this.last = last;
55925                     }
55926                 }else{
55927                     this.selectFirstRow();
55928                 }
55929                 this.fireEvent("afterselectionchange", this);
55930             },
55931             "down" : function(e){
55932                 if(!e.shiftKey){
55933                     this.selectNext(e.shiftKey);
55934                 }else if(this.last !== false && this.lastActive !== false){
55935                     var last = this.last;
55936                     this.selectRange(this.last,  this.lastActive+1);
55937                     this.grid.getView().focusRow(this.lastActive);
55938                     if(last !== false){
55939                         this.last = last;
55940                     }
55941                 }else{
55942                     this.selectFirstRow();
55943                 }
55944                 this.fireEvent("afterselectionchange", this);
55945             },
55946             scope: this
55947         });
55948
55949         var view = this.grid.view;
55950         view.on("refresh", this.onRefresh, this);
55951         view.on("rowupdated", this.onRowUpdated, this);
55952         view.on("rowremoved", this.onRemove, this);
55953     },
55954
55955     // private
55956     onRefresh : function(){
55957         var ds = this.grid.dataSource, i, v = this.grid.view;
55958         var s = this.selections;
55959         s.each(function(r){
55960             if((i = ds.indexOfId(r.id)) != -1){
55961                 v.onRowSelect(i);
55962                 s.add(ds.getAt(i)); // updating the selection relate data
55963             }else{
55964                 s.remove(r);
55965             }
55966         });
55967     },
55968
55969     // private
55970     onRemove : function(v, index, r){
55971         this.selections.remove(r);
55972     },
55973
55974     // private
55975     onRowUpdated : function(v, index, r){
55976         if(this.isSelected(r)){
55977             v.onRowSelect(index);
55978         }
55979     },
55980
55981     /**
55982      * Select records.
55983      * @param {Array} records The records to select
55984      * @param {Boolean} keepExisting (optional) True to keep existing selections
55985      */
55986     selectRecords : function(records, keepExisting){
55987         if(!keepExisting){
55988             this.clearSelections();
55989         }
55990         var ds = this.grid.dataSource;
55991         for(var i = 0, len = records.length; i < len; i++){
55992             this.selectRow(ds.indexOf(records[i]), true);
55993         }
55994     },
55995
55996     /**
55997      * Gets the number of selected rows.
55998      * @return {Number}
55999      */
56000     getCount : function(){
56001         return this.selections.length;
56002     },
56003
56004     /**
56005      * Selects the first row in the grid.
56006      */
56007     selectFirstRow : function(){
56008         this.selectRow(0);
56009     },
56010
56011     /**
56012      * Select the last row.
56013      * @param {Boolean} keepExisting (optional) True to keep existing selections
56014      */
56015     selectLastRow : function(keepExisting){
56016         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
56017     },
56018
56019     /**
56020      * Selects the row immediately following the last selected row.
56021      * @param {Boolean} keepExisting (optional) True to keep existing selections
56022      */
56023     selectNext : function(keepExisting){
56024         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
56025             this.selectRow(this.last+1, keepExisting);
56026             this.grid.getView().focusRow(this.last);
56027         }
56028     },
56029
56030     /**
56031      * Selects the row that precedes the last selected row.
56032      * @param {Boolean} keepExisting (optional) True to keep existing selections
56033      */
56034     selectPrevious : function(keepExisting){
56035         if(this.last){
56036             this.selectRow(this.last-1, keepExisting);
56037             this.grid.getView().focusRow(this.last);
56038         }
56039     },
56040
56041     /**
56042      * Returns the selected records
56043      * @return {Array} Array of selected records
56044      */
56045     getSelections : function(){
56046         return [].concat(this.selections.items);
56047     },
56048
56049     /**
56050      * Returns the first selected record.
56051      * @return {Record}
56052      */
56053     getSelected : function(){
56054         return this.selections.itemAt(0);
56055     },
56056
56057
56058     /**
56059      * Clears all selections.
56060      */
56061     clearSelections : function(fast){
56062         if(this.locked) return;
56063         if(fast !== true){
56064             var ds = this.grid.dataSource;
56065             var s = this.selections;
56066             s.each(function(r){
56067                 this.deselectRow(ds.indexOfId(r.id));
56068             }, this);
56069             s.clear();
56070         }else{
56071             this.selections.clear();
56072         }
56073         this.last = false;
56074     },
56075
56076
56077     /**
56078      * Selects all rows.
56079      */
56080     selectAll : function(){
56081         if(this.locked) return;
56082         this.selections.clear();
56083         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
56084             this.selectRow(i, true);
56085         }
56086     },
56087
56088     /**
56089      * Returns True if there is a selection.
56090      * @return {Boolean}
56091      */
56092     hasSelection : function(){
56093         return this.selections.length > 0;
56094     },
56095
56096     /**
56097      * Returns True if the specified row is selected.
56098      * @param {Number/Record} record The record or index of the record to check
56099      * @return {Boolean}
56100      */
56101     isSelected : function(index){
56102         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
56103         return (r && this.selections.key(r.id) ? true : false);
56104     },
56105
56106     /**
56107      * Returns True if the specified record id is selected.
56108      * @param {String} id The id of record to check
56109      * @return {Boolean}
56110      */
56111     isIdSelected : function(id){
56112         return (this.selections.key(id) ? true : false);
56113     },
56114
56115     // private
56116     handleMouseDown : function(e, t){
56117         var view = this.grid.getView(), rowIndex;
56118         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
56119             return;
56120         };
56121         if(e.shiftKey && this.last !== false){
56122             var last = this.last;
56123             this.selectRange(last, rowIndex, e.ctrlKey);
56124             this.last = last; // reset the last
56125             view.focusRow(rowIndex);
56126         }else{
56127             var isSelected = this.isSelected(rowIndex);
56128             if(e.button !== 0 && isSelected){
56129                 view.focusRow(rowIndex);
56130             }else if(e.ctrlKey && isSelected){
56131                 this.deselectRow(rowIndex);
56132             }else if(!isSelected){
56133                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
56134                 view.focusRow(rowIndex);
56135             }
56136         }
56137         this.fireEvent("afterselectionchange", this);
56138     },
56139     // private
56140     handleDragableRowClick :  function(grid, rowIndex, e) 
56141     {
56142         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
56143             this.selectRow(rowIndex, false);
56144             grid.view.focusRow(rowIndex);
56145              this.fireEvent("afterselectionchange", this);
56146         }
56147     },
56148     
56149     /**
56150      * Selects multiple rows.
56151      * @param {Array} rows Array of the indexes of the row to select
56152      * @param {Boolean} keepExisting (optional) True to keep existing selections
56153      */
56154     selectRows : function(rows, keepExisting){
56155         if(!keepExisting){
56156             this.clearSelections();
56157         }
56158         for(var i = 0, len = rows.length; i < len; i++){
56159             this.selectRow(rows[i], true);
56160         }
56161     },
56162
56163     /**
56164      * Selects a range of rows. All rows in between startRow and endRow are also selected.
56165      * @param {Number} startRow The index of the first row in the range
56166      * @param {Number} endRow The index of the last row in the range
56167      * @param {Boolean} keepExisting (optional) True to retain existing selections
56168      */
56169     selectRange : function(startRow, endRow, keepExisting){
56170         if(this.locked) return;
56171         if(!keepExisting){
56172             this.clearSelections();
56173         }
56174         if(startRow <= endRow){
56175             for(var i = startRow; i <= endRow; i++){
56176                 this.selectRow(i, true);
56177             }
56178         }else{
56179             for(var i = startRow; i >= endRow; i--){
56180                 this.selectRow(i, true);
56181             }
56182         }
56183     },
56184
56185     /**
56186      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
56187      * @param {Number} startRow The index of the first row in the range
56188      * @param {Number} endRow The index of the last row in the range
56189      */
56190     deselectRange : function(startRow, endRow, preventViewNotify){
56191         if(this.locked) return;
56192         for(var i = startRow; i <= endRow; i++){
56193             this.deselectRow(i, preventViewNotify);
56194         }
56195     },
56196
56197     /**
56198      * Selects a row.
56199      * @param {Number} row The index of the row to select
56200      * @param {Boolean} keepExisting (optional) True to keep existing selections
56201      */
56202     selectRow : function(index, keepExisting, preventViewNotify){
56203         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
56204         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
56205             if(!keepExisting || this.singleSelect){
56206                 this.clearSelections();
56207             }
56208             var r = this.grid.dataSource.getAt(index);
56209             this.selections.add(r);
56210             this.last = this.lastActive = index;
56211             if(!preventViewNotify){
56212                 this.grid.getView().onRowSelect(index);
56213             }
56214             this.fireEvent("rowselect", this, index, r);
56215             this.fireEvent("selectionchange", this);
56216         }
56217     },
56218
56219     /**
56220      * Deselects a row.
56221      * @param {Number} row The index of the row to deselect
56222      */
56223     deselectRow : function(index, preventViewNotify){
56224         if(this.locked) return;
56225         if(this.last == index){
56226             this.last = false;
56227         }
56228         if(this.lastActive == index){
56229             this.lastActive = false;
56230         }
56231         var r = this.grid.dataSource.getAt(index);
56232         this.selections.remove(r);
56233         if(!preventViewNotify){
56234             this.grid.getView().onRowDeselect(index);
56235         }
56236         this.fireEvent("rowdeselect", this, index);
56237         this.fireEvent("selectionchange", this);
56238     },
56239
56240     // private
56241     restoreLast : function(){
56242         if(this._last){
56243             this.last = this._last;
56244         }
56245     },
56246
56247     // private
56248     acceptsNav : function(row, col, cm){
56249         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56250     },
56251
56252     // private
56253     onEditorKey : function(field, e){
56254         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
56255         if(k == e.TAB){
56256             e.stopEvent();
56257             ed.completeEdit();
56258             if(e.shiftKey){
56259                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56260             }else{
56261                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56262             }
56263         }else if(k == e.ENTER && !e.ctrlKey){
56264             e.stopEvent();
56265             ed.completeEdit();
56266             if(e.shiftKey){
56267                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
56268             }else{
56269                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
56270             }
56271         }else if(k == e.ESC){
56272             ed.cancelEdit();
56273         }
56274         if(newCell){
56275             g.startEditing(newCell[0], newCell[1]);
56276         }
56277     }
56278 });/*
56279  * Based on:
56280  * Ext JS Library 1.1.1
56281  * Copyright(c) 2006-2007, Ext JS, LLC.
56282  *
56283  * Originally Released Under LGPL - original licence link has changed is not relivant.
56284  *
56285  * Fork - LGPL
56286  * <script type="text/javascript">
56287  */
56288 /**
56289  * @class Roo.grid.CellSelectionModel
56290  * @extends Roo.grid.AbstractSelectionModel
56291  * This class provides the basic implementation for cell selection in a grid.
56292  * @constructor
56293  * @param {Object} config The object containing the configuration of this model.
56294  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
56295  */
56296 Roo.grid.CellSelectionModel = function(config){
56297     Roo.apply(this, config);
56298
56299     this.selection = null;
56300
56301     this.addEvents({
56302         /**
56303              * @event beforerowselect
56304              * Fires before a cell is selected.
56305              * @param {SelectionModel} this
56306              * @param {Number} rowIndex The selected row index
56307              * @param {Number} colIndex The selected cell index
56308              */
56309             "beforecellselect" : true,
56310         /**
56311              * @event cellselect
56312              * Fires when a cell is selected.
56313              * @param {SelectionModel} this
56314              * @param {Number} rowIndex The selected row index
56315              * @param {Number} colIndex The selected cell index
56316              */
56317             "cellselect" : true,
56318         /**
56319              * @event selectionchange
56320              * Fires when the active selection changes.
56321              * @param {SelectionModel} this
56322              * @param {Object} selection null for no selection or an object (o) with two properties
56323                 <ul>
56324                 <li>o.record: the record object for the row the selection is in</li>
56325                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
56326                 </ul>
56327              */
56328             "selectionchange" : true,
56329         /**
56330              * @event tabend
56331              * Fires when the tab (or enter) was pressed on the last editable cell
56332              * You can use this to trigger add new row.
56333              * @param {SelectionModel} this
56334              */
56335             "tabend" : true,
56336          /**
56337              * @event beforeeditnext
56338              * Fires before the next editable sell is made active
56339              * You can use this to skip to another cell or fire the tabend
56340              *    if you set cell to false
56341              * @param {Object} eventdata object : { cell : [ row, col ] } 
56342              */
56343             "beforeeditnext" : true
56344     });
56345     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
56346 };
56347
56348 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
56349     
56350     enter_is_tab: false,
56351
56352     /** @ignore */
56353     initEvents : function(){
56354         this.grid.on("mousedown", this.handleMouseDown, this);
56355         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
56356         var view = this.grid.view;
56357         view.on("refresh", this.onViewChange, this);
56358         view.on("rowupdated", this.onRowUpdated, this);
56359         view.on("beforerowremoved", this.clearSelections, this);
56360         view.on("beforerowsinserted", this.clearSelections, this);
56361         if(this.grid.isEditor){
56362             this.grid.on("beforeedit", this.beforeEdit,  this);
56363         }
56364     },
56365
56366         //private
56367     beforeEdit : function(e){
56368         this.select(e.row, e.column, false, true, e.record);
56369     },
56370
56371         //private
56372     onRowUpdated : function(v, index, r){
56373         if(this.selection && this.selection.record == r){
56374             v.onCellSelect(index, this.selection.cell[1]);
56375         }
56376     },
56377
56378         //private
56379     onViewChange : function(){
56380         this.clearSelections(true);
56381     },
56382
56383         /**
56384          * Returns the currently selected cell,.
56385          * @return {Array} The selected cell (row, column) or null if none selected.
56386          */
56387     getSelectedCell : function(){
56388         return this.selection ? this.selection.cell : null;
56389     },
56390
56391     /**
56392      * Clears all selections.
56393      * @param {Boolean} true to prevent the gridview from being notified about the change.
56394      */
56395     clearSelections : function(preventNotify){
56396         var s = this.selection;
56397         if(s){
56398             if(preventNotify !== true){
56399                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
56400             }
56401             this.selection = null;
56402             this.fireEvent("selectionchange", this, null);
56403         }
56404     },
56405
56406     /**
56407      * Returns true if there is a selection.
56408      * @return {Boolean}
56409      */
56410     hasSelection : function(){
56411         return this.selection ? true : false;
56412     },
56413
56414     /** @ignore */
56415     handleMouseDown : function(e, t){
56416         var v = this.grid.getView();
56417         if(this.isLocked()){
56418             return;
56419         };
56420         var row = v.findRowIndex(t);
56421         var cell = v.findCellIndex(t);
56422         if(row !== false && cell !== false){
56423             this.select(row, cell);
56424         }
56425     },
56426
56427     /**
56428      * Selects a cell.
56429      * @param {Number} rowIndex
56430      * @param {Number} collIndex
56431      */
56432     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
56433         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
56434             this.clearSelections();
56435             r = r || this.grid.dataSource.getAt(rowIndex);
56436             this.selection = {
56437                 record : r,
56438                 cell : [rowIndex, colIndex]
56439             };
56440             if(!preventViewNotify){
56441                 var v = this.grid.getView();
56442                 v.onCellSelect(rowIndex, colIndex);
56443                 if(preventFocus !== true){
56444                     v.focusCell(rowIndex, colIndex);
56445                 }
56446             }
56447             this.fireEvent("cellselect", this, rowIndex, colIndex);
56448             this.fireEvent("selectionchange", this, this.selection);
56449         }
56450     },
56451
56452         //private
56453     isSelectable : function(rowIndex, colIndex, cm){
56454         return !cm.isHidden(colIndex);
56455     },
56456
56457     /** @ignore */
56458     handleKeyDown : function(e){
56459         //Roo.log('Cell Sel Model handleKeyDown');
56460         if(!e.isNavKeyPress()){
56461             return;
56462         }
56463         var g = this.grid, s = this.selection;
56464         if(!s){
56465             e.stopEvent();
56466             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
56467             if(cell){
56468                 this.select(cell[0], cell[1]);
56469             }
56470             return;
56471         }
56472         var sm = this;
56473         var walk = function(row, col, step){
56474             return g.walkCells(row, col, step, sm.isSelectable,  sm);
56475         };
56476         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
56477         var newCell;
56478
56479       
56480
56481         switch(k){
56482             case e.TAB:
56483                 // handled by onEditorKey
56484                 if (g.isEditor && g.editing) {
56485                     return;
56486                 }
56487                 if(e.shiftKey) {
56488                     newCell = walk(r, c-1, -1);
56489                 } else {
56490                     newCell = walk(r, c+1, 1);
56491                 }
56492                 break;
56493             
56494             case e.DOWN:
56495                newCell = walk(r+1, c, 1);
56496                 break;
56497             
56498             case e.UP:
56499                 newCell = walk(r-1, c, -1);
56500                 break;
56501             
56502             case e.RIGHT:
56503                 newCell = walk(r, c+1, 1);
56504                 break;
56505             
56506             case e.LEFT:
56507                 newCell = walk(r, c-1, -1);
56508                 break;
56509             
56510             case e.ENTER:
56511                 
56512                 if(g.isEditor && !g.editing){
56513                    g.startEditing(r, c);
56514                    e.stopEvent();
56515                    return;
56516                 }
56517                 
56518                 
56519              break;
56520         };
56521         if(newCell){
56522             this.select(newCell[0], newCell[1]);
56523             e.stopEvent();
56524             
56525         }
56526     },
56527
56528     acceptsNav : function(row, col, cm){
56529         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56530     },
56531     /**
56532      * Selects a cell.
56533      * @param {Number} field (not used) - as it's normally used as a listener
56534      * @param {Number} e - event - fake it by using
56535      *
56536      * var e = Roo.EventObjectImpl.prototype;
56537      * e.keyCode = e.TAB
56538      *
56539      * 
56540      */
56541     onEditorKey : function(field, e){
56542         
56543         var k = e.getKey(),
56544             newCell,
56545             g = this.grid,
56546             ed = g.activeEditor,
56547             forward = false;
56548         ///Roo.log('onEditorKey' + k);
56549         
56550         
56551         if (this.enter_is_tab && k == e.ENTER) {
56552             k = e.TAB;
56553         }
56554         
56555         if(k == e.TAB){
56556             if(e.shiftKey){
56557                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56558             }else{
56559                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56560                 forward = true;
56561             }
56562             
56563             e.stopEvent();
56564             
56565         } else if(k == e.ENTER &&  !e.ctrlKey){
56566             ed.completeEdit();
56567             e.stopEvent();
56568             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56569         
56570                 } else if(k == e.ESC){
56571             ed.cancelEdit();
56572         }
56573                 
56574         if (newCell) {
56575             var ecall = { cell : newCell, forward : forward };
56576             this.fireEvent('beforeeditnext', ecall );
56577             newCell = ecall.cell;
56578                         forward = ecall.forward;
56579         }
56580                 
56581         if(newCell){
56582             //Roo.log('next cell after edit');
56583             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
56584         } else if (forward) {
56585             // tabbed past last
56586             this.fireEvent.defer(100, this, ['tabend',this]);
56587         }
56588     }
56589 });/*
56590  * Based on:
56591  * Ext JS Library 1.1.1
56592  * Copyright(c) 2006-2007, Ext JS, LLC.
56593  *
56594  * Originally Released Under LGPL - original licence link has changed is not relivant.
56595  *
56596  * Fork - LGPL
56597  * <script type="text/javascript">
56598  */
56599  
56600 /**
56601  * @class Roo.grid.EditorGrid
56602  * @extends Roo.grid.Grid
56603  * Class for creating and editable grid.
56604  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
56605  * The container MUST have some type of size defined for the grid to fill. The container will be 
56606  * automatically set to position relative if it isn't already.
56607  * @param {Object} dataSource The data model to bind to
56608  * @param {Object} colModel The column model with info about this grid's columns
56609  */
56610 Roo.grid.EditorGrid = function(container, config){
56611     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
56612     this.getGridEl().addClass("xedit-grid");
56613
56614     if(!this.selModel){
56615         this.selModel = new Roo.grid.CellSelectionModel();
56616     }
56617
56618     this.activeEditor = null;
56619
56620         this.addEvents({
56621             /**
56622              * @event beforeedit
56623              * Fires before cell editing is triggered. The edit event object has the following properties <br />
56624              * <ul style="padding:5px;padding-left:16px;">
56625              * <li>grid - This grid</li>
56626              * <li>record - The record being edited</li>
56627              * <li>field - The field name being edited</li>
56628              * <li>value - The value for the field being edited.</li>
56629              * <li>row - The grid row index</li>
56630              * <li>column - The grid column index</li>
56631              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56632              * </ul>
56633              * @param {Object} e An edit event (see above for description)
56634              */
56635             "beforeedit" : true,
56636             /**
56637              * @event afteredit
56638              * Fires after a cell is edited. <br />
56639              * <ul style="padding:5px;padding-left:16px;">
56640              * <li>grid - This grid</li>
56641              * <li>record - The record being edited</li>
56642              * <li>field - The field name being edited</li>
56643              * <li>value - The value being set</li>
56644              * <li>originalValue - The original value for the field, before the edit.</li>
56645              * <li>row - The grid row index</li>
56646              * <li>column - The grid column index</li>
56647              * </ul>
56648              * @param {Object} e An edit event (see above for description)
56649              */
56650             "afteredit" : true,
56651             /**
56652              * @event validateedit
56653              * Fires after a cell is edited, but before the value is set in the record. 
56654          * You can use this to modify the value being set in the field, Return false
56655              * to cancel the change. The edit event object has the following properties <br />
56656              * <ul style="padding:5px;padding-left:16px;">
56657          * <li>editor - This editor</li>
56658              * <li>grid - This grid</li>
56659              * <li>record - The record being edited</li>
56660              * <li>field - The field name being edited</li>
56661              * <li>value - The value being set</li>
56662              * <li>originalValue - The original value for the field, before the edit.</li>
56663              * <li>row - The grid row index</li>
56664              * <li>column - The grid column index</li>
56665              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56666              * </ul>
56667              * @param {Object} e An edit event (see above for description)
56668              */
56669             "validateedit" : true
56670         });
56671     this.on("bodyscroll", this.stopEditing,  this);
56672     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56673 };
56674
56675 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56676     /**
56677      * @cfg {Number} clicksToEdit
56678      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56679      */
56680     clicksToEdit: 2,
56681
56682     // private
56683     isEditor : true,
56684     // private
56685     trackMouseOver: false, // causes very odd FF errors
56686
56687     onCellDblClick : function(g, row, col){
56688         this.startEditing(row, col);
56689     },
56690
56691     onEditComplete : function(ed, value, startValue){
56692         this.editing = false;
56693         this.activeEditor = null;
56694         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56695         var r = ed.record;
56696         var field = this.colModel.getDataIndex(ed.col);
56697         var e = {
56698             grid: this,
56699             record: r,
56700             field: field,
56701             originalValue: startValue,
56702             value: value,
56703             row: ed.row,
56704             column: ed.col,
56705             cancel:false,
56706             editor: ed
56707         };
56708         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
56709         cell.show();
56710           
56711         if(String(value) !== String(startValue)){
56712             
56713             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56714                 r.set(field, e.value);
56715                 // if we are dealing with a combo box..
56716                 // then we also set the 'name' colum to be the displayField
56717                 if (ed.field.displayField && ed.field.name) {
56718                     r.set(ed.field.name, ed.field.el.dom.value);
56719                 }
56720                 
56721                 delete e.cancel; //?? why!!!
56722                 this.fireEvent("afteredit", e);
56723             }
56724         } else {
56725             this.fireEvent("afteredit", e); // always fire it!
56726         }
56727         this.view.focusCell(ed.row, ed.col);
56728     },
56729
56730     /**
56731      * Starts editing the specified for the specified row/column
56732      * @param {Number} rowIndex
56733      * @param {Number} colIndex
56734      */
56735     startEditing : function(row, col){
56736         this.stopEditing();
56737         if(this.colModel.isCellEditable(col, row)){
56738             this.view.ensureVisible(row, col, true);
56739           
56740             var r = this.dataSource.getAt(row);
56741             var field = this.colModel.getDataIndex(col);
56742             var cell = Roo.get(this.view.getCell(row,col));
56743             var e = {
56744                 grid: this,
56745                 record: r,
56746                 field: field,
56747                 value: r.data[field],
56748                 row: row,
56749                 column: col,
56750                 cancel:false 
56751             };
56752             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56753                 this.editing = true;
56754                 var ed = this.colModel.getCellEditor(col, row);
56755                 
56756                 if (!ed) {
56757                     return;
56758                 }
56759                 if(!ed.rendered){
56760                     ed.render(ed.parentEl || document.body);
56761                 }
56762                 ed.field.reset();
56763                
56764                 cell.hide();
56765                 
56766                 (function(){ // complex but required for focus issues in safari, ie and opera
56767                     ed.row = row;
56768                     ed.col = col;
56769                     ed.record = r;
56770                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56771                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56772                     this.activeEditor = ed;
56773                     var v = r.data[field];
56774                     ed.startEdit(this.view.getCell(row, col), v);
56775                     // combo's with 'displayField and name set
56776                     if (ed.field.displayField && ed.field.name) {
56777                         ed.field.el.dom.value = r.data[ed.field.name];
56778                     }
56779                     
56780                     
56781                 }).defer(50, this);
56782             }
56783         }
56784     },
56785         
56786     /**
56787      * Stops any active editing
56788      */
56789     stopEditing : function(){
56790         if(this.activeEditor){
56791             this.activeEditor.completeEdit();
56792         }
56793         this.activeEditor = null;
56794     },
56795         
56796          /**
56797      * Called to get grid's drag proxy text, by default returns this.ddText.
56798      * @return {String}
56799      */
56800     getDragDropText : function(){
56801         var count = this.selModel.getSelectedCell() ? 1 : 0;
56802         return String.format(this.ddText, count, count == 1 ? '' : 's');
56803     }
56804         
56805 });/*
56806  * Based on:
56807  * Ext JS Library 1.1.1
56808  * Copyright(c) 2006-2007, Ext JS, LLC.
56809  *
56810  * Originally Released Under LGPL - original licence link has changed is not relivant.
56811  *
56812  * Fork - LGPL
56813  * <script type="text/javascript">
56814  */
56815
56816 // private - not really -- you end up using it !
56817 // This is a support class used internally by the Grid components
56818
56819 /**
56820  * @class Roo.grid.GridEditor
56821  * @extends Roo.Editor
56822  * Class for creating and editable grid elements.
56823  * @param {Object} config any settings (must include field)
56824  */
56825 Roo.grid.GridEditor = function(field, config){
56826     if (!config && field.field) {
56827         config = field;
56828         field = Roo.factory(config.field, Roo.form);
56829     }
56830     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56831     field.monitorTab = false;
56832 };
56833
56834 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56835     
56836     /**
56837      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56838      */
56839     
56840     alignment: "tl-tl",
56841     autoSize: "width",
56842     hideEl : false,
56843     cls: "x-small-editor x-grid-editor",
56844     shim:false,
56845     shadow:"frame"
56846 });/*
56847  * Based on:
56848  * Ext JS Library 1.1.1
56849  * Copyright(c) 2006-2007, Ext JS, LLC.
56850  *
56851  * Originally Released Under LGPL - original licence link has changed is not relivant.
56852  *
56853  * Fork - LGPL
56854  * <script type="text/javascript">
56855  */
56856   
56857
56858   
56859 Roo.grid.PropertyRecord = Roo.data.Record.create([
56860     {name:'name',type:'string'},  'value'
56861 ]);
56862
56863
56864 Roo.grid.PropertyStore = function(grid, source){
56865     this.grid = grid;
56866     this.store = new Roo.data.Store({
56867         recordType : Roo.grid.PropertyRecord
56868     });
56869     this.store.on('update', this.onUpdate,  this);
56870     if(source){
56871         this.setSource(source);
56872     }
56873     Roo.grid.PropertyStore.superclass.constructor.call(this);
56874 };
56875
56876
56877
56878 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56879     setSource : function(o){
56880         this.source = o;
56881         this.store.removeAll();
56882         var data = [];
56883         for(var k in o){
56884             if(this.isEditableValue(o[k])){
56885                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
56886             }
56887         }
56888         this.store.loadRecords({records: data}, {}, true);
56889     },
56890
56891     onUpdate : function(ds, record, type){
56892         if(type == Roo.data.Record.EDIT){
56893             var v = record.data['value'];
56894             var oldValue = record.modified['value'];
56895             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
56896                 this.source[record.id] = v;
56897                 record.commit();
56898                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
56899             }else{
56900                 record.reject();
56901             }
56902         }
56903     },
56904
56905     getProperty : function(row){
56906        return this.store.getAt(row);
56907     },
56908
56909     isEditableValue: function(val){
56910         if(val && val instanceof Date){
56911             return true;
56912         }else if(typeof val == 'object' || typeof val == 'function'){
56913             return false;
56914         }
56915         return true;
56916     },
56917
56918     setValue : function(prop, value){
56919         this.source[prop] = value;
56920         this.store.getById(prop).set('value', value);
56921     },
56922
56923     getSource : function(){
56924         return this.source;
56925     }
56926 });
56927
56928 Roo.grid.PropertyColumnModel = function(grid, store){
56929     this.grid = grid;
56930     var g = Roo.grid;
56931     g.PropertyColumnModel.superclass.constructor.call(this, [
56932         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
56933         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
56934     ]);
56935     this.store = store;
56936     this.bselect = Roo.DomHelper.append(document.body, {
56937         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
56938             {tag: 'option', value: 'true', html: 'true'},
56939             {tag: 'option', value: 'false', html: 'false'}
56940         ]
56941     });
56942     Roo.id(this.bselect);
56943     var f = Roo.form;
56944     this.editors = {
56945         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
56946         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
56947         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
56948         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
56949         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
56950     };
56951     this.renderCellDelegate = this.renderCell.createDelegate(this);
56952     this.renderPropDelegate = this.renderProp.createDelegate(this);
56953 };
56954
56955 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
56956     
56957     
56958     nameText : 'Name',
56959     valueText : 'Value',
56960     
56961     dateFormat : 'm/j/Y',
56962     
56963     
56964     renderDate : function(dateVal){
56965         return dateVal.dateFormat(this.dateFormat);
56966     },
56967
56968     renderBool : function(bVal){
56969         return bVal ? 'true' : 'false';
56970     },
56971
56972     isCellEditable : function(colIndex, rowIndex){
56973         return colIndex == 1;
56974     },
56975
56976     getRenderer : function(col){
56977         return col == 1 ?
56978             this.renderCellDelegate : this.renderPropDelegate;
56979     },
56980
56981     renderProp : function(v){
56982         return this.getPropertyName(v);
56983     },
56984
56985     renderCell : function(val){
56986         var rv = val;
56987         if(val instanceof Date){
56988             rv = this.renderDate(val);
56989         }else if(typeof val == 'boolean'){
56990             rv = this.renderBool(val);
56991         }
56992         return Roo.util.Format.htmlEncode(rv);
56993     },
56994
56995     getPropertyName : function(name){
56996         var pn = this.grid.propertyNames;
56997         return pn && pn[name] ? pn[name] : name;
56998     },
56999
57000     getCellEditor : function(colIndex, rowIndex){
57001         var p = this.store.getProperty(rowIndex);
57002         var n = p.data['name'], val = p.data['value'];
57003         
57004         if(typeof(this.grid.customEditors[n]) == 'string'){
57005             return this.editors[this.grid.customEditors[n]];
57006         }
57007         if(typeof(this.grid.customEditors[n]) != 'undefined'){
57008             return this.grid.customEditors[n];
57009         }
57010         if(val instanceof Date){
57011             return this.editors['date'];
57012         }else if(typeof val == 'number'){
57013             return this.editors['number'];
57014         }else if(typeof val == 'boolean'){
57015             return this.editors['boolean'];
57016         }else{
57017             return this.editors['string'];
57018         }
57019     }
57020 });
57021
57022 /**
57023  * @class Roo.grid.PropertyGrid
57024  * @extends Roo.grid.EditorGrid
57025  * This class represents the  interface of a component based property grid control.
57026  * <br><br>Usage:<pre><code>
57027  var grid = new Roo.grid.PropertyGrid("my-container-id", {
57028       
57029  });
57030  // set any options
57031  grid.render();
57032  * </code></pre>
57033   
57034  * @constructor
57035  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57036  * The container MUST have some type of size defined for the grid to fill. The container will be
57037  * automatically set to position relative if it isn't already.
57038  * @param {Object} config A config object that sets properties on this grid.
57039  */
57040 Roo.grid.PropertyGrid = function(container, config){
57041     config = config || {};
57042     var store = new Roo.grid.PropertyStore(this);
57043     this.store = store;
57044     var cm = new Roo.grid.PropertyColumnModel(this, store);
57045     store.store.sort('name', 'ASC');
57046     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
57047         ds: store.store,
57048         cm: cm,
57049         enableColLock:false,
57050         enableColumnMove:false,
57051         stripeRows:false,
57052         trackMouseOver: false,
57053         clicksToEdit:1
57054     }, config));
57055     this.getGridEl().addClass('x-props-grid');
57056     this.lastEditRow = null;
57057     this.on('columnresize', this.onColumnResize, this);
57058     this.addEvents({
57059          /**
57060              * @event beforepropertychange
57061              * Fires before a property changes (return false to stop?)
57062              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57063              * @param {String} id Record Id
57064              * @param {String} newval New Value
57065          * @param {String} oldval Old Value
57066              */
57067         "beforepropertychange": true,
57068         /**
57069              * @event propertychange
57070              * Fires after a property changes
57071              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57072              * @param {String} id Record Id
57073              * @param {String} newval New Value
57074          * @param {String} oldval Old Value
57075              */
57076         "propertychange": true
57077     });
57078     this.customEditors = this.customEditors || {};
57079 };
57080 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
57081     
57082      /**
57083      * @cfg {Object} customEditors map of colnames=> custom editors.
57084      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
57085      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
57086      * false disables editing of the field.
57087          */
57088     
57089       /**
57090      * @cfg {Object} propertyNames map of property Names to their displayed value
57091          */
57092     
57093     render : function(){
57094         Roo.grid.PropertyGrid.superclass.render.call(this);
57095         this.autoSize.defer(100, this);
57096     },
57097
57098     autoSize : function(){
57099         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
57100         if(this.view){
57101             this.view.fitColumns();
57102         }
57103     },
57104
57105     onColumnResize : function(){
57106         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
57107         this.autoSize();
57108     },
57109     /**
57110      * Sets the data for the Grid
57111      * accepts a Key => Value object of all the elements avaiable.
57112      * @param {Object} data  to appear in grid.
57113      */
57114     setSource : function(source){
57115         this.store.setSource(source);
57116         //this.autoSize();
57117     },
57118     /**
57119      * Gets all the data from the grid.
57120      * @return {Object} data  data stored in grid
57121      */
57122     getSource : function(){
57123         return this.store.getSource();
57124     }
57125 });/*
57126   
57127  * Licence LGPL
57128  
57129  */
57130  
57131 /**
57132  * @class Roo.grid.Calendar
57133  * @extends Roo.util.Grid
57134  * This class extends the Grid to provide a calendar widget
57135  * <br><br>Usage:<pre><code>
57136  var grid = new Roo.grid.Calendar("my-container-id", {
57137      ds: myDataStore,
57138      cm: myColModel,
57139      selModel: mySelectionModel,
57140      autoSizeColumns: true,
57141      monitorWindowResize: false,
57142      trackMouseOver: true
57143      eventstore : real data store..
57144  });
57145  // set any options
57146  grid.render();
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.Calendar = function(container, config){
57155         // initialize the container
57156         this.container = Roo.get(container);
57157         this.container.update("");
57158         this.container.setStyle("overflow", "hidden");
57159     this.container.addClass('x-grid-container');
57160
57161     this.id = this.container.id;
57162
57163     Roo.apply(this, config);
57164     // check and correct shorthanded configs
57165     
57166     var rows = [];
57167     var d =1;
57168     for (var r = 0;r < 6;r++) {
57169         
57170         rows[r]=[];
57171         for (var c =0;c < 7;c++) {
57172             rows[r][c]= '';
57173         }
57174     }
57175     if (this.eventStore) {
57176         this.eventStore= Roo.factory(this.eventStore, Roo.data);
57177         this.eventStore.on('load',this.onLoad, this);
57178         this.eventStore.on('beforeload',this.clearEvents, this);
57179          
57180     }
57181     
57182     this.dataSource = new Roo.data.Store({
57183             proxy: new Roo.data.MemoryProxy(rows),
57184             reader: new Roo.data.ArrayReader({}, [
57185                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
57186     });
57187
57188     this.dataSource.load();
57189     this.ds = this.dataSource;
57190     this.ds.xmodule = this.xmodule || false;
57191     
57192     
57193     var cellRender = function(v,x,r)
57194     {
57195         return String.format(
57196             '<div class="fc-day  fc-widget-content"><div>' +
57197                 '<div class="fc-event-container"></div>' +
57198                 '<div class="fc-day-number">{0}</div>'+
57199                 
57200                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
57201             '</div></div>', v);
57202     
57203     }
57204     
57205     
57206     this.colModel = new Roo.grid.ColumnModel( [
57207         {
57208             xtype: 'ColumnModel',
57209             xns: Roo.grid,
57210             dataIndex : 'weekday0',
57211             header : 'Sunday',
57212             renderer : cellRender
57213         },
57214         {
57215             xtype: 'ColumnModel',
57216             xns: Roo.grid,
57217             dataIndex : 'weekday1',
57218             header : 'Monday',
57219             renderer : cellRender
57220         },
57221         {
57222             xtype: 'ColumnModel',
57223             xns: Roo.grid,
57224             dataIndex : 'weekday2',
57225             header : 'Tuesday',
57226             renderer : cellRender
57227         },
57228         {
57229             xtype: 'ColumnModel',
57230             xns: Roo.grid,
57231             dataIndex : 'weekday3',
57232             header : 'Wednesday',
57233             renderer : cellRender
57234         },
57235         {
57236             xtype: 'ColumnModel',
57237             xns: Roo.grid,
57238             dataIndex : 'weekday4',
57239             header : 'Thursday',
57240             renderer : cellRender
57241         },
57242         {
57243             xtype: 'ColumnModel',
57244             xns: Roo.grid,
57245             dataIndex : 'weekday5',
57246             header : 'Friday',
57247             renderer : cellRender
57248         },
57249         {
57250             xtype: 'ColumnModel',
57251             xns: Roo.grid,
57252             dataIndex : 'weekday6',
57253             header : 'Saturday',
57254             renderer : cellRender
57255         }
57256     ]);
57257     this.cm = this.colModel;
57258     this.cm.xmodule = this.xmodule || false;
57259  
57260         
57261           
57262     //this.selModel = new Roo.grid.CellSelectionModel();
57263     //this.sm = this.selModel;
57264     //this.selModel.init(this);
57265     
57266     
57267     if(this.width){
57268         this.container.setWidth(this.width);
57269     }
57270
57271     if(this.height){
57272         this.container.setHeight(this.height);
57273     }
57274     /** @private */
57275         this.addEvents({
57276         // raw events
57277         /**
57278          * @event click
57279          * The raw click event for the entire grid.
57280          * @param {Roo.EventObject} e
57281          */
57282         "click" : true,
57283         /**
57284          * @event dblclick
57285          * The raw dblclick event for the entire grid.
57286          * @param {Roo.EventObject} e
57287          */
57288         "dblclick" : true,
57289         /**
57290          * @event contextmenu
57291          * The raw contextmenu event for the entire grid.
57292          * @param {Roo.EventObject} e
57293          */
57294         "contextmenu" : true,
57295         /**
57296          * @event mousedown
57297          * The raw mousedown event for the entire grid.
57298          * @param {Roo.EventObject} e
57299          */
57300         "mousedown" : true,
57301         /**
57302          * @event mouseup
57303          * The raw mouseup event for the entire grid.
57304          * @param {Roo.EventObject} e
57305          */
57306         "mouseup" : true,
57307         /**
57308          * @event mouseover
57309          * The raw mouseover event for the entire grid.
57310          * @param {Roo.EventObject} e
57311          */
57312         "mouseover" : true,
57313         /**
57314          * @event mouseout
57315          * The raw mouseout event for the entire grid.
57316          * @param {Roo.EventObject} e
57317          */
57318         "mouseout" : true,
57319         /**
57320          * @event keypress
57321          * The raw keypress event for the entire grid.
57322          * @param {Roo.EventObject} e
57323          */
57324         "keypress" : true,
57325         /**
57326          * @event keydown
57327          * The raw keydown event for the entire grid.
57328          * @param {Roo.EventObject} e
57329          */
57330         "keydown" : true,
57331
57332         // custom events
57333
57334         /**
57335          * @event cellclick
57336          * Fires when a cell is clicked
57337          * @param {Grid} this
57338          * @param {Number} rowIndex
57339          * @param {Number} columnIndex
57340          * @param {Roo.EventObject} e
57341          */
57342         "cellclick" : true,
57343         /**
57344          * @event celldblclick
57345          * Fires when a cell is double clicked
57346          * @param {Grid} this
57347          * @param {Number} rowIndex
57348          * @param {Number} columnIndex
57349          * @param {Roo.EventObject} e
57350          */
57351         "celldblclick" : true,
57352         /**
57353          * @event rowclick
57354          * Fires when a row is clicked
57355          * @param {Grid} this
57356          * @param {Number} rowIndex
57357          * @param {Roo.EventObject} e
57358          */
57359         "rowclick" : true,
57360         /**
57361          * @event rowdblclick
57362          * Fires when a row is double clicked
57363          * @param {Grid} this
57364          * @param {Number} rowIndex
57365          * @param {Roo.EventObject} e
57366          */
57367         "rowdblclick" : true,
57368         /**
57369          * @event headerclick
57370          * Fires when a header is clicked
57371          * @param {Grid} this
57372          * @param {Number} columnIndex
57373          * @param {Roo.EventObject} e
57374          */
57375         "headerclick" : true,
57376         /**
57377          * @event headerdblclick
57378          * Fires when a header cell is double clicked
57379          * @param {Grid} this
57380          * @param {Number} columnIndex
57381          * @param {Roo.EventObject} e
57382          */
57383         "headerdblclick" : true,
57384         /**
57385          * @event rowcontextmenu
57386          * Fires when a row is right clicked
57387          * @param {Grid} this
57388          * @param {Number} rowIndex
57389          * @param {Roo.EventObject} e
57390          */
57391         "rowcontextmenu" : true,
57392         /**
57393          * @event cellcontextmenu
57394          * Fires when a cell is right clicked
57395          * @param {Grid} this
57396          * @param {Number} rowIndex
57397          * @param {Number} cellIndex
57398          * @param {Roo.EventObject} e
57399          */
57400          "cellcontextmenu" : true,
57401         /**
57402          * @event headercontextmenu
57403          * Fires when a header is right clicked
57404          * @param {Grid} this
57405          * @param {Number} columnIndex
57406          * @param {Roo.EventObject} e
57407          */
57408         "headercontextmenu" : true,
57409         /**
57410          * @event bodyscroll
57411          * Fires when the body element is scrolled
57412          * @param {Number} scrollLeft
57413          * @param {Number} scrollTop
57414          */
57415         "bodyscroll" : true,
57416         /**
57417          * @event columnresize
57418          * Fires when the user resizes a column
57419          * @param {Number} columnIndex
57420          * @param {Number} newSize
57421          */
57422         "columnresize" : true,
57423         /**
57424          * @event columnmove
57425          * Fires when the user moves a column
57426          * @param {Number} oldIndex
57427          * @param {Number} newIndex
57428          */
57429         "columnmove" : true,
57430         /**
57431          * @event startdrag
57432          * Fires when row(s) start being dragged
57433          * @param {Grid} this
57434          * @param {Roo.GridDD} dd The drag drop object
57435          * @param {event} e The raw browser event
57436          */
57437         "startdrag" : true,
57438         /**
57439          * @event enddrag
57440          * Fires when a drag operation is complete
57441          * @param {Grid} this
57442          * @param {Roo.GridDD} dd The drag drop object
57443          * @param {event} e The raw browser event
57444          */
57445         "enddrag" : true,
57446         /**
57447          * @event dragdrop
57448          * Fires when dragged row(s) are dropped on a valid DD target
57449          * @param {Grid} this
57450          * @param {Roo.GridDD} dd The drag drop object
57451          * @param {String} targetId The target drag drop object
57452          * @param {event} e The raw browser event
57453          */
57454         "dragdrop" : true,
57455         /**
57456          * @event dragover
57457          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57458          * @param {Grid} this
57459          * @param {Roo.GridDD} dd The drag drop object
57460          * @param {String} targetId The target drag drop object
57461          * @param {event} e The raw browser event
57462          */
57463         "dragover" : true,
57464         /**
57465          * @event dragenter
57466          *  Fires when the dragged row(s) first cross another DD target while being dragged
57467          * @param {Grid} this
57468          * @param {Roo.GridDD} dd The drag drop object
57469          * @param {String} targetId The target drag drop object
57470          * @param {event} e The raw browser event
57471          */
57472         "dragenter" : true,
57473         /**
57474          * @event dragout
57475          * Fires when the dragged row(s) leave another DD target while being dragged
57476          * @param {Grid} this
57477          * @param {Roo.GridDD} dd The drag drop object
57478          * @param {String} targetId The target drag drop object
57479          * @param {event} e The raw browser event
57480          */
57481         "dragout" : true,
57482         /**
57483          * @event rowclass
57484          * Fires when a row is rendered, so you can change add a style to it.
57485          * @param {GridView} gridview   The grid view
57486          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57487          */
57488         'rowclass' : true,
57489
57490         /**
57491          * @event render
57492          * Fires when the grid is rendered
57493          * @param {Grid} grid
57494          */
57495         'render' : true,
57496             /**
57497              * @event select
57498              * Fires when a date is selected
57499              * @param {DatePicker} this
57500              * @param {Date} date The selected date
57501              */
57502         'select': true,
57503         /**
57504              * @event monthchange
57505              * Fires when the displayed month changes 
57506              * @param {DatePicker} this
57507              * @param {Date} date The selected month
57508              */
57509         'monthchange': true,
57510         /**
57511              * @event evententer
57512              * Fires when mouse over an event
57513              * @param {Calendar} this
57514              * @param {event} Event
57515              */
57516         'evententer': true,
57517         /**
57518              * @event eventleave
57519              * Fires when the mouse leaves an
57520              * @param {Calendar} this
57521              * @param {event}
57522              */
57523         'eventleave': true,
57524         /**
57525              * @event eventclick
57526              * Fires when the mouse click an
57527              * @param {Calendar} this
57528              * @param {event}
57529              */
57530         'eventclick': true,
57531         /**
57532              * @event eventrender
57533              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
57534              * @param {Calendar} this
57535              * @param {data} data to be modified
57536              */
57537         'eventrender': true
57538         
57539     });
57540
57541     Roo.grid.Grid.superclass.constructor.call(this);
57542     this.on('render', function() {
57543         this.view.el.addClass('x-grid-cal'); 
57544         
57545         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
57546
57547     },this);
57548     
57549     if (!Roo.grid.Calendar.style) {
57550         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
57551             
57552             
57553             '.x-grid-cal .x-grid-col' :  {
57554                 height: 'auto !important',
57555                 'vertical-align': 'top'
57556             },
57557             '.x-grid-cal  .fc-event-hori' : {
57558                 height: '14px'
57559             }
57560              
57561             
57562         }, Roo.id());
57563     }
57564
57565     
57566     
57567 };
57568 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
57569     /**
57570      * @cfg {Store} eventStore The store that loads events.
57571      */
57572     eventStore : 25,
57573
57574      
57575     activeDate : false,
57576     startDay : 0,
57577     autoWidth : true,
57578     monitorWindowResize : false,
57579
57580     
57581     resizeColumns : function() {
57582         var col = (this.view.el.getWidth() / 7) - 3;
57583         // loop through cols, and setWidth
57584         for(var i =0 ; i < 7 ; i++){
57585             this.cm.setColumnWidth(i, col);
57586         }
57587     },
57588      setDate :function(date) {
57589         
57590         Roo.log('setDate?');
57591         
57592         this.resizeColumns();
57593         var vd = this.activeDate;
57594         this.activeDate = date;
57595 //        if(vd && this.el){
57596 //            var t = date.getTime();
57597 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
57598 //                Roo.log('using add remove');
57599 //                
57600 //                this.fireEvent('monthchange', this, date);
57601 //                
57602 //                this.cells.removeClass("fc-state-highlight");
57603 //                this.cells.each(function(c){
57604 //                   if(c.dateValue == t){
57605 //                       c.addClass("fc-state-highlight");
57606 //                       setTimeout(function(){
57607 //                            try{c.dom.firstChild.focus();}catch(e){}
57608 //                       }, 50);
57609 //                       return false;
57610 //                   }
57611 //                   return true;
57612 //                });
57613 //                return;
57614 //            }
57615 //        }
57616         
57617         var days = date.getDaysInMonth();
57618         
57619         var firstOfMonth = date.getFirstDateOfMonth();
57620         var startingPos = firstOfMonth.getDay()-this.startDay;
57621         
57622         if(startingPos < this.startDay){
57623             startingPos += 7;
57624         }
57625         
57626         var pm = date.add(Date.MONTH, -1);
57627         var prevStart = pm.getDaysInMonth()-startingPos;
57628 //        
57629         
57630         
57631         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57632         
57633         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57634         //this.cells.addClassOnOver('fc-state-hover');
57635         
57636         var cells = this.cells.elements;
57637         var textEls = this.textNodes;
57638         
57639         //Roo.each(cells, function(cell){
57640         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57641         //});
57642         
57643         days += startingPos;
57644
57645         // convert everything to numbers so it's fast
57646         var day = 86400000;
57647         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57648         //Roo.log(d);
57649         //Roo.log(pm);
57650         //Roo.log(prevStart);
57651         
57652         var today = new Date().clearTime().getTime();
57653         var sel = date.clearTime().getTime();
57654         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57655         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57656         var ddMatch = this.disabledDatesRE;
57657         var ddText = this.disabledDatesText;
57658         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57659         var ddaysText = this.disabledDaysText;
57660         var format = this.format;
57661         
57662         var setCellClass = function(cal, cell){
57663             
57664             //Roo.log('set Cell Class');
57665             cell.title = "";
57666             var t = d.getTime();
57667             
57668             //Roo.log(d);
57669             
57670             
57671             cell.dateValue = t;
57672             if(t == today){
57673                 cell.className += " fc-today";
57674                 cell.className += " fc-state-highlight";
57675                 cell.title = cal.todayText;
57676             }
57677             if(t == sel){
57678                 // disable highlight in other month..
57679                 cell.className += " fc-state-highlight";
57680                 
57681             }
57682             // disabling
57683             if(t < min) {
57684                 //cell.className = " fc-state-disabled";
57685                 cell.title = cal.minText;
57686                 return;
57687             }
57688             if(t > max) {
57689                 //cell.className = " fc-state-disabled";
57690                 cell.title = cal.maxText;
57691                 return;
57692             }
57693             if(ddays){
57694                 if(ddays.indexOf(d.getDay()) != -1){
57695                     // cell.title = ddaysText;
57696                    // cell.className = " fc-state-disabled";
57697                 }
57698             }
57699             if(ddMatch && format){
57700                 var fvalue = d.dateFormat(format);
57701                 if(ddMatch.test(fvalue)){
57702                     cell.title = ddText.replace("%0", fvalue);
57703                    cell.className = " fc-state-disabled";
57704                 }
57705             }
57706             
57707             if (!cell.initialClassName) {
57708                 cell.initialClassName = cell.dom.className;
57709             }
57710             
57711             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57712         };
57713
57714         var i = 0;
57715         
57716         for(; i < startingPos; i++) {
57717             cells[i].dayName =  (++prevStart);
57718             Roo.log(textEls[i]);
57719             d.setDate(d.getDate()+1);
57720             
57721             //cells[i].className = "fc-past fc-other-month";
57722             setCellClass(this, cells[i]);
57723         }
57724         
57725         var intDay = 0;
57726         
57727         for(; i < days; i++){
57728             intDay = i - startingPos + 1;
57729             cells[i].dayName =  (intDay);
57730             d.setDate(d.getDate()+1);
57731             
57732             cells[i].className = ''; // "x-date-active";
57733             setCellClass(this, cells[i]);
57734         }
57735         var extraDays = 0;
57736         
57737         for(; i < 42; i++) {
57738             //textEls[i].innerHTML = (++extraDays);
57739             
57740             d.setDate(d.getDate()+1);
57741             cells[i].dayName = (++extraDays);
57742             cells[i].className = "fc-future fc-other-month";
57743             setCellClass(this, cells[i]);
57744         }
57745         
57746         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57747         
57748         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57749         
57750         // this will cause all the cells to mis
57751         var rows= [];
57752         var i =0;
57753         for (var r = 0;r < 6;r++) {
57754             for (var c =0;c < 7;c++) {
57755                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57756             }    
57757         }
57758         
57759         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57760         for(i=0;i<cells.length;i++) {
57761             
57762             this.cells.elements[i].dayName = cells[i].dayName ;
57763             this.cells.elements[i].className = cells[i].className;
57764             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57765             this.cells.elements[i].title = cells[i].title ;
57766             this.cells.elements[i].dateValue = cells[i].dateValue ;
57767         }
57768         
57769         
57770         
57771         
57772         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57773         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57774         
57775         ////if(totalRows != 6){
57776             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57777            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57778        // }
57779         
57780         this.fireEvent('monthchange', this, date);
57781         
57782         
57783     },
57784  /**
57785      * Returns the grid's SelectionModel.
57786      * @return {SelectionModel}
57787      */
57788     getSelectionModel : function(){
57789         if(!this.selModel){
57790             this.selModel = new Roo.grid.CellSelectionModel();
57791         }
57792         return this.selModel;
57793     },
57794
57795     load: function() {
57796         this.eventStore.load()
57797         
57798         
57799         
57800     },
57801     
57802     findCell : function(dt) {
57803         dt = dt.clearTime().getTime();
57804         var ret = false;
57805         this.cells.each(function(c){
57806             //Roo.log("check " +c.dateValue + '?=' + dt);
57807             if(c.dateValue == dt){
57808                 ret = c;
57809                 return false;
57810             }
57811             return true;
57812         });
57813         
57814         return ret;
57815     },
57816     
57817     findCells : function(rec) {
57818         var s = rec.data.start_dt.clone().clearTime().getTime();
57819        // Roo.log(s);
57820         var e= rec.data.end_dt.clone().clearTime().getTime();
57821        // Roo.log(e);
57822         var ret = [];
57823         this.cells.each(function(c){
57824              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57825             
57826             if(c.dateValue > e){
57827                 return ;
57828             }
57829             if(c.dateValue < s){
57830                 return ;
57831             }
57832             ret.push(c);
57833         });
57834         
57835         return ret;    
57836     },
57837     
57838     findBestRow: function(cells)
57839     {
57840         var ret = 0;
57841         
57842         for (var i =0 ; i < cells.length;i++) {
57843             ret  = Math.max(cells[i].rows || 0,ret);
57844         }
57845         return ret;
57846         
57847     },
57848     
57849     
57850     addItem : function(rec)
57851     {
57852         // look for vertical location slot in
57853         var cells = this.findCells(rec);
57854         
57855         rec.row = this.findBestRow(cells);
57856         
57857         // work out the location.
57858         
57859         var crow = false;
57860         var rows = [];
57861         for(var i =0; i < cells.length; i++) {
57862             if (!crow) {
57863                 crow = {
57864                     start : cells[i],
57865                     end :  cells[i]
57866                 };
57867                 continue;
57868             }
57869             if (crow.start.getY() == cells[i].getY()) {
57870                 // on same row.
57871                 crow.end = cells[i];
57872                 continue;
57873             }
57874             // different row.
57875             rows.push(crow);
57876             crow = {
57877                 start: cells[i],
57878                 end : cells[i]
57879             };
57880             
57881         }
57882         
57883         rows.push(crow);
57884         rec.els = [];
57885         rec.rows = rows;
57886         rec.cells = cells;
57887         for (var i = 0; i < cells.length;i++) {
57888             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
57889             
57890         }
57891         
57892         
57893     },
57894     
57895     clearEvents: function() {
57896         
57897         if (!this.eventStore.getCount()) {
57898             return;
57899         }
57900         // reset number of rows in cells.
57901         Roo.each(this.cells.elements, function(c){
57902             c.rows = 0;
57903         });
57904         
57905         this.eventStore.each(function(e) {
57906             this.clearEvent(e);
57907         },this);
57908         
57909     },
57910     
57911     clearEvent : function(ev)
57912     {
57913         if (ev.els) {
57914             Roo.each(ev.els, function(el) {
57915                 el.un('mouseenter' ,this.onEventEnter, this);
57916                 el.un('mouseleave' ,this.onEventLeave, this);
57917                 el.remove();
57918             },this);
57919             ev.els = [];
57920         }
57921     },
57922     
57923     
57924     renderEvent : function(ev,ctr) {
57925         if (!ctr) {
57926              ctr = this.view.el.select('.fc-event-container',true).first();
57927         }
57928         
57929          
57930         this.clearEvent(ev);
57931             //code
57932        
57933         
57934         
57935         ev.els = [];
57936         var cells = ev.cells;
57937         var rows = ev.rows;
57938         this.fireEvent('eventrender', this, ev);
57939         
57940         for(var i =0; i < rows.length; i++) {
57941             
57942             cls = '';
57943             if (i == 0) {
57944                 cls += ' fc-event-start';
57945             }
57946             if ((i+1) == rows.length) {
57947                 cls += ' fc-event-end';
57948             }
57949             
57950             //Roo.log(ev.data);
57951             // how many rows should it span..
57952             var cg = this.eventTmpl.append(ctr,Roo.apply({
57953                 fccls : cls
57954                 
57955             }, ev.data) , true);
57956             
57957             
57958             cg.on('mouseenter' ,this.onEventEnter, this, ev);
57959             cg.on('mouseleave' ,this.onEventLeave, this, ev);
57960             cg.on('click', this.onEventClick, this, ev);
57961             
57962             ev.els.push(cg);
57963             
57964             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
57965             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
57966             //Roo.log(cg);
57967              
57968             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
57969             cg.setWidth(ebox.right - sbox.x -2);
57970         }
57971     },
57972     
57973     renderEvents: function()
57974     {   
57975         // first make sure there is enough space..
57976         
57977         if (!this.eventTmpl) {
57978             this.eventTmpl = new Roo.Template(
57979                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57980                     '<div class="fc-event-inner">' +
57981                         '<span class="fc-event-time">{time}</span>' +
57982                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57983                     '</div>' +
57984                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57985                 '</div>'
57986             );
57987                 
57988         }
57989                
57990         
57991         
57992         this.cells.each(function(c) {
57993             //Roo.log(c.select('.fc-day-content div',true).first());
57994             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
57995         });
57996         
57997         var ctr = this.view.el.select('.fc-event-container',true).first();
57998         
57999         var cls;
58000         this.eventStore.each(function(ev){
58001             
58002             this.renderEvent(ev);
58003              
58004              
58005         }, this);
58006         this.view.layout();
58007         
58008     },
58009     
58010     onEventEnter: function (e, el,event,d) {
58011         this.fireEvent('evententer', this, el, event);
58012     },
58013     
58014     onEventLeave: function (e, el,event,d) {
58015         this.fireEvent('eventleave', this, el, event);
58016     },
58017     
58018     onEventClick: function (e, el,event,d) {
58019         this.fireEvent('eventclick', this, el, event);
58020     },
58021     
58022     onMonthChange: function () {
58023         this.store.load();
58024     },
58025     
58026     onLoad: function () {
58027         
58028         //Roo.log('calendar onload');
58029 //         
58030         if(this.eventStore.getCount() > 0){
58031             
58032            
58033             
58034             this.eventStore.each(function(d){
58035                 
58036                 
58037                 // FIXME..
58038                 var add =   d.data;
58039                 if (typeof(add.end_dt) == 'undefined')  {
58040                     Roo.log("Missing End time in calendar data: ");
58041                     Roo.log(d);
58042                     return;
58043                 }
58044                 if (typeof(add.start_dt) == 'undefined')  {
58045                     Roo.log("Missing Start time in calendar data: ");
58046                     Roo.log(d);
58047                     return;
58048                 }
58049                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
58050                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
58051                 add.id = add.id || d.id;
58052                 add.title = add.title || '??';
58053                 
58054                 this.addItem(d);
58055                 
58056              
58057             },this);
58058         }
58059         
58060         this.renderEvents();
58061     }
58062     
58063
58064 });
58065 /*
58066  grid : {
58067                 xtype: 'Grid',
58068                 xns: Roo.grid,
58069                 listeners : {
58070                     render : function ()
58071                     {
58072                         _this.grid = this;
58073                         
58074                         if (!this.view.el.hasClass('course-timesheet')) {
58075                             this.view.el.addClass('course-timesheet');
58076                         }
58077                         if (this.tsStyle) {
58078                             this.ds.load({});
58079                             return; 
58080                         }
58081                         Roo.log('width');
58082                         Roo.log(_this.grid.view.el.getWidth());
58083                         
58084                         
58085                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
58086                             '.course-timesheet .x-grid-row' : {
58087                                 height: '80px'
58088                             },
58089                             '.x-grid-row td' : {
58090                                 'vertical-align' : 0
58091                             },
58092                             '.course-edit-link' : {
58093                                 'color' : 'blue',
58094                                 'text-overflow' : 'ellipsis',
58095                                 'overflow' : 'hidden',
58096                                 'white-space' : 'nowrap',
58097                                 'cursor' : 'pointer'
58098                             },
58099                             '.sub-link' : {
58100                                 'color' : 'green'
58101                             },
58102                             '.de-act-sup-link' : {
58103                                 'color' : 'purple',
58104                                 'text-decoration' : 'line-through'
58105                             },
58106                             '.de-act-link' : {
58107                                 'color' : 'red',
58108                                 'text-decoration' : 'line-through'
58109                             },
58110                             '.course-timesheet .course-highlight' : {
58111                                 'border-top-style': 'dashed !important',
58112                                 'border-bottom-bottom': 'dashed !important'
58113                             },
58114                             '.course-timesheet .course-item' : {
58115                                 'font-family'   : 'tahoma, arial, helvetica',
58116                                 'font-size'     : '11px',
58117                                 'overflow'      : 'hidden',
58118                                 'padding-left'  : '10px',
58119                                 'padding-right' : '10px',
58120                                 'padding-top' : '10px' 
58121                             }
58122                             
58123                         }, Roo.id());
58124                                 this.ds.load({});
58125                     }
58126                 },
58127                 autoWidth : true,
58128                 monitorWindowResize : false,
58129                 cellrenderer : function(v,x,r)
58130                 {
58131                     return v;
58132                 },
58133                 sm : {
58134                     xtype: 'CellSelectionModel',
58135                     xns: Roo.grid
58136                 },
58137                 dataSource : {
58138                     xtype: 'Store',
58139                     xns: Roo.data,
58140                     listeners : {
58141                         beforeload : function (_self, options)
58142                         {
58143                             options.params = options.params || {};
58144                             options.params._month = _this.monthField.getValue();
58145                             options.params.limit = 9999;
58146                             options.params['sort'] = 'when_dt';    
58147                             options.params['dir'] = 'ASC';    
58148                             this.proxy.loadResponse = this.loadResponse;
58149                             Roo.log("load?");
58150                             //this.addColumns();
58151                         },
58152                         load : function (_self, records, options)
58153                         {
58154                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
58155                                 // if you click on the translation.. you can edit it...
58156                                 var el = Roo.get(this);
58157                                 var id = el.dom.getAttribute('data-id');
58158                                 var d = el.dom.getAttribute('data-date');
58159                                 var t = el.dom.getAttribute('data-time');
58160                                 //var id = this.child('span').dom.textContent;
58161                                 
58162                                 //Roo.log(this);
58163                                 Pman.Dialog.CourseCalendar.show({
58164                                     id : id,
58165                                     when_d : d,
58166                                     when_t : t,
58167                                     productitem_active : id ? 1 : 0
58168                                 }, function() {
58169                                     _this.grid.ds.load({});
58170                                 });
58171                            
58172                            });
58173                            
58174                            _this.panel.fireEvent('resize', [ '', '' ]);
58175                         }
58176                     },
58177                     loadResponse : function(o, success, response){
58178                             // this is overridden on before load..
58179                             
58180                             Roo.log("our code?");       
58181                             //Roo.log(success);
58182                             //Roo.log(response)
58183                             delete this.activeRequest;
58184                             if(!success){
58185                                 this.fireEvent("loadexception", this, o, response);
58186                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58187                                 return;
58188                             }
58189                             var result;
58190                             try {
58191                                 result = o.reader.read(response);
58192                             }catch(e){
58193                                 Roo.log("load exception?");
58194                                 this.fireEvent("loadexception", this, o, response, e);
58195                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58196                                 return;
58197                             }
58198                             Roo.log("ready...");        
58199                             // loop through result.records;
58200                             // and set this.tdate[date] = [] << array of records..
58201                             _this.tdata  = {};
58202                             Roo.each(result.records, function(r){
58203                                 //Roo.log(r.data);
58204                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
58205                                     _this.tdata[r.data.when_dt.format('j')] = [];
58206                                 }
58207                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
58208                             });
58209                             
58210                             //Roo.log(_this.tdata);
58211                             
58212                             result.records = [];
58213                             result.totalRecords = 6;
58214                     
58215                             // let's generate some duumy records for the rows.
58216                             //var st = _this.dateField.getValue();
58217                             
58218                             // work out monday..
58219                             //st = st.add(Date.DAY, -1 * st.format('w'));
58220                             
58221                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58222                             
58223                             var firstOfMonth = date.getFirstDayOfMonth();
58224                             var days = date.getDaysInMonth();
58225                             var d = 1;
58226                             var firstAdded = false;
58227                             for (var i = 0; i < result.totalRecords ; i++) {
58228                                 //var d= st.add(Date.DAY, i);
58229                                 var row = {};
58230                                 var added = 0;
58231                                 for(var w = 0 ; w < 7 ; w++){
58232                                     if(!firstAdded && firstOfMonth != w){
58233                                         continue;
58234                                     }
58235                                     if(d > days){
58236                                         continue;
58237                                     }
58238                                     firstAdded = true;
58239                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
58240                                     row['weekday'+w] = String.format(
58241                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
58242                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
58243                                                     d,
58244                                                     date.format('Y-m-')+dd
58245                                                 );
58246                                     added++;
58247                                     if(typeof(_this.tdata[d]) != 'undefined'){
58248                                         Roo.each(_this.tdata[d], function(r){
58249                                             var is_sub = '';
58250                                             var deactive = '';
58251                                             var id = r.id;
58252                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
58253                                             if(r.parent_id*1>0){
58254                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
58255                                                 id = r.parent_id;
58256                                             }
58257                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
58258                                                 deactive = 'de-act-link';
58259                                             }
58260                                             
58261                                             row['weekday'+w] += String.format(
58262                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
58263                                                     id, //0
58264                                                     r.product_id_name, //1
58265                                                     r.when_dt.format('h:ia'), //2
58266                                                     is_sub, //3
58267                                                     deactive, //4
58268                                                     desc // 5
58269                                             );
58270                                         });
58271                                     }
58272                                     d++;
58273                                 }
58274                                 
58275                                 // only do this if something added..
58276                                 if(added > 0){ 
58277                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
58278                                 }
58279                                 
58280                                 
58281                                 // push it twice. (second one with an hour..
58282                                 
58283                             }
58284                             //Roo.log(result);
58285                             this.fireEvent("load", this, o, o.request.arg);
58286                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
58287                         },
58288                     sortInfo : {field: 'when_dt', direction : 'ASC' },
58289                     proxy : {
58290                         xtype: 'HttpProxy',
58291                         xns: Roo.data,
58292                         method : 'GET',
58293                         url : baseURL + '/Roo/Shop_course.php'
58294                     },
58295                     reader : {
58296                         xtype: 'JsonReader',
58297                         xns: Roo.data,
58298                         id : 'id',
58299                         fields : [
58300                             {
58301                                 'name': 'id',
58302                                 'type': 'int'
58303                             },
58304                             {
58305                                 'name': 'when_dt',
58306                                 'type': 'string'
58307                             },
58308                             {
58309                                 'name': 'end_dt',
58310                                 'type': 'string'
58311                             },
58312                             {
58313                                 'name': 'parent_id',
58314                                 'type': 'int'
58315                             },
58316                             {
58317                                 'name': 'product_id',
58318                                 'type': 'int'
58319                             },
58320                             {
58321                                 'name': 'productitem_id',
58322                                 'type': 'int'
58323                             },
58324                             {
58325                                 'name': 'guid',
58326                                 'type': 'int'
58327                             }
58328                         ]
58329                     }
58330                 },
58331                 toolbar : {
58332                     xtype: 'Toolbar',
58333                     xns: Roo,
58334                     items : [
58335                         {
58336                             xtype: 'Button',
58337                             xns: Roo.Toolbar,
58338                             listeners : {
58339                                 click : function (_self, e)
58340                                 {
58341                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58342                                     sd.setMonth(sd.getMonth()-1);
58343                                     _this.monthField.setValue(sd.format('Y-m-d'));
58344                                     _this.grid.ds.load({});
58345                                 }
58346                             },
58347                             text : "Back"
58348                         },
58349                         {
58350                             xtype: 'Separator',
58351                             xns: Roo.Toolbar
58352                         },
58353                         {
58354                             xtype: 'MonthField',
58355                             xns: Roo.form,
58356                             listeners : {
58357                                 render : function (_self)
58358                                 {
58359                                     _this.monthField = _self;
58360                                    // _this.monthField.set  today
58361                                 },
58362                                 select : function (combo, date)
58363                                 {
58364                                     _this.grid.ds.load({});
58365                                 }
58366                             },
58367                             value : (function() { return new Date(); })()
58368                         },
58369                         {
58370                             xtype: 'Separator',
58371                             xns: Roo.Toolbar
58372                         },
58373                         {
58374                             xtype: 'TextItem',
58375                             xns: Roo.Toolbar,
58376                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
58377                         },
58378                         {
58379                             xtype: 'Fill',
58380                             xns: Roo.Toolbar
58381                         },
58382                         {
58383                             xtype: 'Button',
58384                             xns: Roo.Toolbar,
58385                             listeners : {
58386                                 click : function (_self, e)
58387                                 {
58388                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58389                                     sd.setMonth(sd.getMonth()+1);
58390                                     _this.monthField.setValue(sd.format('Y-m-d'));
58391                                     _this.grid.ds.load({});
58392                                 }
58393                             },
58394                             text : "Next"
58395                         }
58396                     ]
58397                 },
58398                  
58399             }
58400         };
58401         
58402         *//*
58403  * Based on:
58404  * Ext JS Library 1.1.1
58405  * Copyright(c) 2006-2007, Ext JS, LLC.
58406  *
58407  * Originally Released Under LGPL - original licence link has changed is not relivant.
58408  *
58409  * Fork - LGPL
58410  * <script type="text/javascript">
58411  */
58412  
58413 /**
58414  * @class Roo.LoadMask
58415  * A simple utility class for generically masking elements while loading data.  If the element being masked has
58416  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
58417  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
58418  * element's UpdateManager load indicator and will be destroyed after the initial load.
58419  * @constructor
58420  * Create a new LoadMask
58421  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
58422  * @param {Object} config The config object
58423  */
58424 Roo.LoadMask = function(el, config){
58425     this.el = Roo.get(el);
58426     Roo.apply(this, config);
58427     if(this.store){
58428         this.store.on('beforeload', this.onBeforeLoad, this);
58429         this.store.on('load', this.onLoad, this);
58430         this.store.on('loadexception', this.onLoadException, this);
58431         this.removeMask = false;
58432     }else{
58433         var um = this.el.getUpdateManager();
58434         um.showLoadIndicator = false; // disable the default indicator
58435         um.on('beforeupdate', this.onBeforeLoad, this);
58436         um.on('update', this.onLoad, this);
58437         um.on('failure', this.onLoad, this);
58438         this.removeMask = true;
58439     }
58440 };
58441
58442 Roo.LoadMask.prototype = {
58443     /**
58444      * @cfg {Boolean} removeMask
58445      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
58446      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
58447      */
58448     /**
58449      * @cfg {String} msg
58450      * The text to display in a centered loading message box (defaults to 'Loading...')
58451      */
58452     msg : 'Loading...',
58453     /**
58454      * @cfg {String} msgCls
58455      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
58456      */
58457     msgCls : 'x-mask-loading',
58458
58459     /**
58460      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
58461      * @type Boolean
58462      */
58463     disabled: false,
58464
58465     /**
58466      * Disables the mask to prevent it from being displayed
58467      */
58468     disable : function(){
58469        this.disabled = true;
58470     },
58471
58472     /**
58473      * Enables the mask so that it can be displayed
58474      */
58475     enable : function(){
58476         this.disabled = false;
58477     },
58478     
58479     onLoadException : function()
58480     {
58481         Roo.log(arguments);
58482         
58483         if (typeof(arguments[3]) != 'undefined') {
58484             Roo.MessageBox.alert("Error loading",arguments[3]);
58485         } 
58486         /*
58487         try {
58488             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
58489                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
58490             }   
58491         } catch(e) {
58492             
58493         }
58494         */
58495     
58496         
58497         
58498         this.el.unmask(this.removeMask);
58499     },
58500     // private
58501     onLoad : function()
58502     {
58503         this.el.unmask(this.removeMask);
58504     },
58505
58506     // private
58507     onBeforeLoad : function(){
58508         if(!this.disabled){
58509             this.el.mask(this.msg, this.msgCls);
58510         }
58511     },
58512
58513     // private
58514     destroy : function(){
58515         if(this.store){
58516             this.store.un('beforeload', this.onBeforeLoad, this);
58517             this.store.un('load', this.onLoad, this);
58518             this.store.un('loadexception', this.onLoadException, this);
58519         }else{
58520             var um = this.el.getUpdateManager();
58521             um.un('beforeupdate', this.onBeforeLoad, this);
58522             um.un('update', this.onLoad, this);
58523             um.un('failure', this.onLoad, this);
58524         }
58525     }
58526 };/*
58527  * Based on:
58528  * Ext JS Library 1.1.1
58529  * Copyright(c) 2006-2007, Ext JS, LLC.
58530  *
58531  * Originally Released Under LGPL - original licence link has changed is not relivant.
58532  *
58533  * Fork - LGPL
58534  * <script type="text/javascript">
58535  */
58536
58537
58538 /**
58539  * @class Roo.XTemplate
58540  * @extends Roo.Template
58541  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
58542 <pre><code>
58543 var t = new Roo.XTemplate(
58544         '&lt;select name="{name}"&gt;',
58545                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
58546         '&lt;/select&gt;'
58547 );
58548  
58549 // then append, applying the master template values
58550  </code></pre>
58551  *
58552  * Supported features:
58553  *
58554  *  Tags:
58555
58556 <pre><code>
58557       {a_variable} - output encoded.
58558       {a_variable.format:("Y-m-d")} - call a method on the variable
58559       {a_variable:raw} - unencoded output
58560       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
58561       {a_variable:this.method_on_template(...)} - call a method on the template object.
58562  
58563 </code></pre>
58564  *  The tpl tag:
58565 <pre><code>
58566         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
58567         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
58568         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
58569         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
58570   
58571         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
58572         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
58573 </code></pre>
58574  *      
58575  */
58576 Roo.XTemplate = function()
58577 {
58578     Roo.XTemplate.superclass.constructor.apply(this, arguments);
58579     if (this.html) {
58580         this.compile();
58581     }
58582 };
58583
58584
58585 Roo.extend(Roo.XTemplate, Roo.Template, {
58586
58587     /**
58588      * The various sub templates
58589      */
58590     tpls : false,
58591     /**
58592      *
58593      * basic tag replacing syntax
58594      * WORD:WORD()
58595      *
58596      * // you can fake an object call by doing this
58597      *  x.t:(test,tesT) 
58598      * 
58599      */
58600     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
58601
58602     /**
58603      * compile the template
58604      *
58605      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
58606      *
58607      */
58608     compile: function()
58609     {
58610         var s = this.html;
58611      
58612         s = ['<tpl>', s, '</tpl>'].join('');
58613     
58614         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
58615             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
58616             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
58617             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
58618             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
58619             m,
58620             id     = 0,
58621             tpls   = [];
58622     
58623         while(true == !!(m = s.match(re))){
58624             var forMatch   = m[0].match(nameRe),
58625                 ifMatch   = m[0].match(ifRe),
58626                 execMatch   = m[0].match(execRe),
58627                 namedMatch   = m[0].match(namedRe),
58628                 
58629                 exp  = null, 
58630                 fn   = null,
58631                 exec = null,
58632                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58633                 
58634             if (ifMatch) {
58635                 // if - puts fn into test..
58636                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58637                 if(exp){
58638                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58639                 }
58640             }
58641             
58642             if (execMatch) {
58643                 // exec - calls a function... returns empty if true is  returned.
58644                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58645                 if(exp){
58646                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58647                 }
58648             }
58649             
58650             
58651             if (name) {
58652                 // for = 
58653                 switch(name){
58654                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58655                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58656                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58657                 }
58658             }
58659             var uid = namedMatch ? namedMatch[1] : id;
58660             
58661             
58662             tpls.push({
58663                 id:     namedMatch ? namedMatch[1] : id,
58664                 target: name,
58665                 exec:   exec,
58666                 test:   fn,
58667                 body:   m[1] || ''
58668             });
58669             if (namedMatch) {
58670                 s = s.replace(m[0], '');
58671             } else { 
58672                 s = s.replace(m[0], '{xtpl'+ id + '}');
58673             }
58674             ++id;
58675         }
58676         this.tpls = [];
58677         for(var i = tpls.length-1; i >= 0; --i){
58678             this.compileTpl(tpls[i]);
58679             this.tpls[tpls[i].id] = tpls[i];
58680         }
58681         this.master = tpls[tpls.length-1];
58682         return this;
58683     },
58684     /**
58685      * same as applyTemplate, except it's done to one of the subTemplates
58686      * when using named templates, you can do:
58687      *
58688      * var str = pl.applySubTemplate('your-name', values);
58689      *
58690      * 
58691      * @param {Number} id of the template
58692      * @param {Object} values to apply to template
58693      * @param {Object} parent (normaly the instance of this object)
58694      */
58695     applySubTemplate : function(id, values, parent)
58696     {
58697         
58698         
58699         var t = this.tpls[id];
58700         
58701         
58702         try { 
58703             if(t.test && !t.test.call(this, values, parent)){
58704                 return '';
58705             }
58706         } catch(e) {
58707             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58708             Roo.log(e.toString());
58709             Roo.log(t.test);
58710             return ''
58711         }
58712         try { 
58713             
58714             if(t.exec && t.exec.call(this, values, parent)){
58715                 return '';
58716             }
58717         } catch(e) {
58718             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58719             Roo.log(e.toString());
58720             Roo.log(t.exec);
58721             return ''
58722         }
58723         try {
58724             var vs = t.target ? t.target.call(this, values, parent) : values;
58725             parent = t.target ? values : parent;
58726             if(t.target && vs instanceof Array){
58727                 var buf = [];
58728                 for(var i = 0, len = vs.length; i < len; i++){
58729                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58730                 }
58731                 return buf.join('');
58732             }
58733             return t.compiled.call(this, vs, parent);
58734         } catch (e) {
58735             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58736             Roo.log(e.toString());
58737             Roo.log(t.compiled);
58738             return '';
58739         }
58740     },
58741
58742     compileTpl : function(tpl)
58743     {
58744         var fm = Roo.util.Format;
58745         var useF = this.disableFormats !== true;
58746         var sep = Roo.isGecko ? "+" : ",";
58747         var undef = function(str) {
58748             Roo.log("Property not found :"  + str);
58749             return '';
58750         };
58751         
58752         var fn = function(m, name, format, args)
58753         {
58754             //Roo.log(arguments);
58755             args = args ? args.replace(/\\'/g,"'") : args;
58756             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58757             if (typeof(format) == 'undefined') {
58758                 format= 'htmlEncode';
58759             }
58760             if (format == 'raw' ) {
58761                 format = false;
58762             }
58763             
58764             if(name.substr(0, 4) == 'xtpl'){
58765                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58766             }
58767             
58768             // build an array of options to determine if value is undefined..
58769             
58770             // basically get 'xxxx.yyyy' then do
58771             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58772             //    (function () { Roo.log("Property not found"); return ''; })() :
58773             //    ......
58774             
58775             var udef_ar = [];
58776             var lookfor = '';
58777             Roo.each(name.split('.'), function(st) {
58778                 lookfor += (lookfor.length ? '.': '') + st;
58779                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58780             });
58781             
58782             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58783             
58784             
58785             if(format && useF){
58786                 
58787                 args = args ? ',' + args : "";
58788                  
58789                 if(format.substr(0, 5) != "this."){
58790                     format = "fm." + format + '(';
58791                 }else{
58792                     format = 'this.call("'+ format.substr(5) + '", ';
58793                     args = ", values";
58794                 }
58795                 
58796                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58797             }
58798              
58799             if (args.length) {
58800                 // called with xxyx.yuu:(test,test)
58801                 // change to ()
58802                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58803             }
58804             // raw.. - :raw modifier..
58805             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58806             
58807         };
58808         var body;
58809         // branched to use + in gecko and [].join() in others
58810         if(Roo.isGecko){
58811             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58812                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58813                     "';};};";
58814         }else{
58815             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58816             body.push(tpl.body.replace(/(\r\n|\n)/g,
58817                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58818             body.push("'].join('');};};");
58819             body = body.join('');
58820         }
58821         
58822         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58823        
58824         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58825         eval(body);
58826         
58827         return this;
58828     },
58829
58830     applyTemplate : function(values){
58831         return this.master.compiled.call(this, values, {});
58832         //var s = this.subs;
58833     },
58834
58835     apply : function(){
58836         return this.applyTemplate.apply(this, arguments);
58837     }
58838
58839  });
58840
58841 Roo.XTemplate.from = function(el){
58842     el = Roo.getDom(el);
58843     return new Roo.XTemplate(el.value || el.innerHTML);
58844 };