roojs-all.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isGecko = !isSafari && ua.indexOf("gecko") > -1,
61         isBorderBox = isIE && !isStrict,
62         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
63         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
64         isLinux = (ua.indexOf("linux") != -1),
65         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
66         isIOS = /iphone|ipad/.test(ua),
67         isTouch =  (function() {
68             try {  
69                 document.createEvent("TouchEvent");  
70                 return true;  
71             } catch (e) {  
72                 return false;  
73             } 
74             
75         })();
76     // remove css image flicker
77         if(isIE && !isIE7){
78         try{
79             document.execCommand("BackgroundImageCache", false, true);
80         }catch(e){}
81     }
82     
83     Roo.apply(Roo, {
84         /**
85          * True if the browser is in strict mode
86          * @type Boolean
87          */
88         isStrict : isStrict,
89         /**
90          * True if the page is running over SSL
91          * @type Boolean
92          */
93         isSecure : isSecure,
94         /**
95          * True when the document is fully initialized and ready for action
96          * @type Boolean
97          */
98         isReady : false,
99         /**
100          * Turn on debugging output (currently only the factory uses this)
101          * @type Boolean
102          */
103         
104         debug: false,
105
106         /**
107          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
108          * @type Boolean
109          */
110         enableGarbageCollector : true,
111
112         /**
113          * True to automatically purge event listeners after uncaching an element (defaults to false).
114          * Note: this only happens if enableGarbageCollector is true.
115          * @type Boolean
116          */
117         enableListenerCollection:false,
118
119         /**
120          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
121          * the IE insecure content warning (defaults to javascript:false).
122          * @type String
123          */
124         SSL_SECURE_URL : "javascript:false",
125
126         /**
127          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
128          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
129          * @type String
130          */
131         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
132
133         emptyFn : function(){},
134         
135         /**
136          * Copies all the properties of config to obj if they don't already exist.
137          * @param {Object} obj The receiver of the properties
138          * @param {Object} config The source of the properties
139          * @return {Object} returns obj
140          */
141         applyIf : function(o, c){
142             if(o && c){
143                 for(var p in c){
144                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
145                 }
146             }
147             return o;
148         },
149
150         /**
151          * Applies event listeners to elements by selectors when the document is ready.
152          * The event name is specified with an @ suffix.
153 <pre><code>
154 Roo.addBehaviors({
155    // add a listener for click on all anchors in element with id foo
156    '#foo a@click' : function(e, t){
157        // do something
158    },
159
160    // add the same listener to multiple selectors (separated by comma BEFORE the @)
161    '#foo a, #bar span.some-class@mouseover' : function(){
162        // do something
163    }
164 });
165 </code></pre>
166          * @param {Object} obj The list of behaviors to apply
167          */
168         addBehaviors : function(o){
169             if(!Roo.isReady){
170                 Roo.onReady(function(){
171                     Roo.addBehaviors(o);
172                 });
173                 return;
174             }
175             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
176             for(var b in o){
177                 var parts = b.split('@');
178                 if(parts[1]){ // for Object prototype breakers
179                     var s = parts[0];
180                     if(!cache[s]){
181                         cache[s] = Roo.select(s);
182                     }
183                     cache[s].on(parts[1], o[b]);
184                 }
185             }
186             cache = null;
187         },
188
189         /**
190          * Generates unique ids. If the element already has an id, it is unchanged
191          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
192          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
193          * @return {String} The generated Id.
194          */
195         id : function(el, prefix){
196             prefix = prefix || "roo-gen";
197             el = Roo.getDom(el);
198             var id = prefix + (++idSeed);
199             return el ? (el.id ? el.id : (el.id = id)) : id;
200         },
201          
202        
203         /**
204          * Extends one class with another class and optionally overrides members with the passed literal. This class
205          * also adds the function "override()" to the class that can be used to override
206          * members on an instance.
207          * @param {Object} subclass The class inheriting the functionality
208          * @param {Object} superclass The class being extended
209          * @param {Object} overrides (optional) A literal with members
210          * @method extend
211          */
212         extend : function(){
213             // inline overrides
214             var io = function(o){
215                 for(var m in o){
216                     this[m] = o[m];
217                 }
218             };
219             return function(sb, sp, overrides){
220                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
221                     overrides = sp;
222                     sp = sb;
223                     sb = function(){sp.apply(this, arguments);};
224                 }
225                 var F = function(){}, sbp, spp = sp.prototype;
226                 F.prototype = spp;
227                 sbp = sb.prototype = new F();
228                 sbp.constructor=sb;
229                 sb.superclass=spp;
230                 
231                 if(spp.constructor == Object.prototype.constructor){
232                     spp.constructor=sp;
233                    
234                 }
235                 
236                 sb.override = function(o){
237                     Roo.override(sb, o);
238                 };
239                 sbp.override = io;
240                 Roo.override(sb, overrides);
241                 return sb;
242             };
243         }(),
244
245         /**
246          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
247          * Usage:<pre><code>
248 Roo.override(MyClass, {
249     newMethod1: function(){
250         // etc.
251     },
252     newMethod2: function(foo){
253         // etc.
254     }
255 });
256  </code></pre>
257          * @param {Object} origclass The class to override
258          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
259          * containing one or more methods.
260          * @method override
261          */
262         override : function(origclass, overrides){
263             if(overrides){
264                 var p = origclass.prototype;
265                 for(var method in overrides){
266                     p[method] = overrides[method];
267                 }
268             }
269         },
270         /**
271          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
272          * <pre><code>
273 Roo.namespace('Company', 'Company.data');
274 Company.Widget = function() { ... }
275 Company.data.CustomStore = function(config) { ... }
276 </code></pre>
277          * @param {String} namespace1
278          * @param {String} namespace2
279          * @param {String} etc
280          * @method namespace
281          */
282         namespace : function(){
283             var a=arguments, o=null, i, j, d, rt;
284             for (i=0; i<a.length; ++i) {
285                 d=a[i].split(".");
286                 rt = d[0];
287                 /** eval:var:o */
288                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
289                 for (j=1; j<d.length; ++j) {
290                     o[d[j]]=o[d[j]] || {};
291                     o=o[d[j]];
292                 }
293             }
294         },
295         /**
296          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
297          * <pre><code>
298 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
299 Roo.factory(conf, Roo.data);
300 </code></pre>
301          * @param {String} classname
302          * @param {String} namespace (optional)
303          * @method factory
304          */
305          
306         factory : function(c, ns)
307         {
308             // no xtype, no ns or c.xns - or forced off by c.xns
309             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
310                 return c;
311             }
312             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
313             if (c.constructor == ns[c.xtype]) {// already created...
314                 return c;
315             }
316             if (ns[c.xtype]) {
317                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
318                 var ret = new ns[c.xtype](c);
319                 ret.xns = false;
320                 return ret;
321             }
322             c.xns = false; // prevent recursion..
323             return c;
324         },
325          /**
326          * Logs to console if it can.
327          *
328          * @param {String|Object} string
329          * @method log
330          */
331         log : function(s)
332         {
333             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
334                 return; // alerT?
335             }
336             console.log(s);
337             
338         },
339         /**
340          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
341          * @param {Object} o
342          * @return {String}
343          */
344         urlEncode : function(o){
345             if(!o){
346                 return "";
347             }
348             var buf = [];
349             for(var key in o){
350                 var ov = o[key], k = Roo.encodeURIComponent(key);
351                 var type = typeof ov;
352                 if(type == 'undefined'){
353                     buf.push(k, "=&");
354                 }else if(type != "function" && type != "object"){
355                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
356                 }else if(ov instanceof Array){
357                     if (ov.length) {
358                             for(var i = 0, len = ov.length; i < len; i++) {
359                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
360                             }
361                         } else {
362                             buf.push(k, "=&");
363                         }
364                 }
365             }
366             buf.pop();
367             return buf.join("");
368         },
369          /**
370          * Safe version of encodeURIComponent
371          * @param {String} data 
372          * @return {String} 
373          */
374         
375         encodeURIComponent : function (data)
376         {
377             try {
378                 return encodeURIComponent(data);
379             } catch(e) {} // should be an uri encode error.
380             
381             if (data == '' || data == null){
382                return '';
383             }
384             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
385             function nibble_to_hex(nibble){
386                 var chars = '0123456789ABCDEF';
387                 return chars.charAt(nibble);
388             }
389             data = data.toString();
390             var buffer = '';
391             for(var i=0; i<data.length; i++){
392                 var c = data.charCodeAt(i);
393                 var bs = new Array();
394                 if (c > 0x10000){
395                         // 4 bytes
396                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
397                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
398                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
399                     bs[3] = 0x80 | (c & 0x3F);
400                 }else if (c > 0x800){
401                          // 3 bytes
402                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
403                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
404                     bs[2] = 0x80 | (c & 0x3F);
405                 }else if (c > 0x80){
406                        // 2 bytes
407                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
408                     bs[1] = 0x80 | (c & 0x3F);
409                 }else{
410                         // 1 byte
411                     bs[0] = c;
412                 }
413                 for(var j=0; j<bs.length; j++){
414                     var b = bs[j];
415                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
416                             + nibble_to_hex(b &0x0F);
417                     buffer += '%'+hex;
418                }
419             }
420             return buffer;    
421              
422         },
423
424         /**
425          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
426          * @param {String} string
427          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
428          * @return {Object} A literal with members
429          */
430         urlDecode : function(string, overwrite){
431             if(!string || !string.length){
432                 return {};
433             }
434             var obj = {};
435             var pairs = string.split('&');
436             var pair, name, value;
437             for(var i = 0, len = pairs.length; i < len; i++){
438                 pair = pairs[i].split('=');
439                 name = decodeURIComponent(pair[0]);
440                 value = decodeURIComponent(pair[1]);
441                 if(overwrite !== true){
442                     if(typeof obj[name] == "undefined"){
443                         obj[name] = value;
444                     }else if(typeof obj[name] == "string"){
445                         obj[name] = [obj[name]];
446                         obj[name].push(value);
447                     }else{
448                         obj[name].push(value);
449                     }
450                 }else{
451                     obj[name] = value;
452                 }
453             }
454             return obj;
455         },
456
457         /**
458          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
459          * passed array is not really an array, your function is called once with it.
460          * The supplied function is called with (Object item, Number index, Array allItems).
461          * @param {Array/NodeList/Mixed} array
462          * @param {Function} fn
463          * @param {Object} scope
464          */
465         each : function(array, fn, scope){
466             if(typeof array.length == "undefined" || typeof array == "string"){
467                 array = [array];
468             }
469             for(var i = 0, len = array.length; i < len; i++){
470                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
471             }
472         },
473
474         // deprecated
475         combine : function(){
476             var as = arguments, l = as.length, r = [];
477             for(var i = 0; i < l; i++){
478                 var a = as[i];
479                 if(a instanceof Array){
480                     r = r.concat(a);
481                 }else if(a.length !== undefined && !a.substr){
482                     r = r.concat(Array.prototype.slice.call(a, 0));
483                 }else{
484                     r.push(a);
485                 }
486             }
487             return r;
488         },
489
490         /**
491          * Escapes the passed string for use in a regular expression
492          * @param {String} str
493          * @return {String}
494          */
495         escapeRe : function(s) {
496             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
497         },
498
499         // internal
500         callback : function(cb, scope, args, delay){
501             if(typeof cb == "function"){
502                 if(delay){
503                     cb.defer(delay, scope, args || []);
504                 }else{
505                     cb.apply(scope, args || []);
506                 }
507             }
508         },
509
510         /**
511          * Return the dom node for the passed string (id), dom node, or Roo.Element
512          * @param {String/HTMLElement/Roo.Element} el
513          * @return HTMLElement
514          */
515         getDom : function(el){
516             if(!el){
517                 return null;
518             }
519             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
520         },
521
522         /**
523         * Shorthand for {@link Roo.ComponentMgr#get}
524         * @param {String} id
525         * @return Roo.Component
526         */
527         getCmp : function(id){
528             return Roo.ComponentMgr.get(id);
529         },
530          
531         num : function(v, defaultValue){
532             if(typeof v != 'number'){
533                 return defaultValue;
534             }
535             return v;
536         },
537
538         destroy : function(){
539             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
540                 var as = a[i];
541                 if(as){
542                     if(as.dom){
543                         as.removeAllListeners();
544                         as.remove();
545                         continue;
546                     }
547                     if(typeof as.purgeListeners == 'function'){
548                         as.purgeListeners();
549                     }
550                     if(typeof as.destroy == 'function'){
551                         as.destroy();
552                     }
553                 }
554             }
555         },
556
557         // inpired by a similar function in mootools library
558         /**
559          * Returns the type of object that is passed in. If the object passed in is null or undefined it
560          * return false otherwise it returns one of the following values:<ul>
561          * <li><b>string</b>: If the object passed is a string</li>
562          * <li><b>number</b>: If the object passed is a number</li>
563          * <li><b>boolean</b>: If the object passed is a boolean value</li>
564          * <li><b>function</b>: If the object passed is a function reference</li>
565          * <li><b>object</b>: If the object passed is an object</li>
566          * <li><b>array</b>: If the object passed is an array</li>
567          * <li><b>regexp</b>: If the object passed is a regular expression</li>
568          * <li><b>element</b>: If the object passed is a DOM Element</li>
569          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
570          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
571          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
572          * @param {Mixed} object
573          * @return {String}
574          */
575         type : function(o){
576             if(o === undefined || o === null){
577                 return false;
578             }
579             if(o.htmlElement){
580                 return 'element';
581             }
582             var t = typeof o;
583             if(t == 'object' && o.nodeName) {
584                 switch(o.nodeType) {
585                     case 1: return 'element';
586                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
587                 }
588             }
589             if(t == 'object' || t == 'function') {
590                 switch(o.constructor) {
591                     case Array: return 'array';
592                     case RegExp: return 'regexp';
593                 }
594                 if(typeof o.length == 'number' && typeof o.item == 'function') {
595                     return 'nodelist';
596                 }
597             }
598             return t;
599         },
600
601         /**
602          * Returns true if the passed value is null, undefined or an empty string (optional).
603          * @param {Mixed} value The value to test
604          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
605          * @return {Boolean}
606          */
607         isEmpty : function(v, allowBlank){
608             return v === null || v === undefined || (!allowBlank ? v === '' : false);
609         },
610         
611         /** @type Boolean */
612         isOpera : isOpera,
613         /** @type Boolean */
614         isSafari : isSafari,
615         /** @type Boolean */
616         isFirefox : isFirefox,
617         /** @type Boolean */
618         isIE : isIE,
619         /** @type Boolean */
620         isIE7 : isIE7,
621         /** @type Boolean */
622         isIE11 : isIE11,
623         /** @type Boolean */
624         isGecko : isGecko,
625         /** @type Boolean */
626         isBorderBox : isBorderBox,
627         /** @type Boolean */
628         isWindows : isWindows,
629         /** @type Boolean */
630         isLinux : isLinux,
631         /** @type Boolean */
632         isMac : isMac,
633         /** @type Boolean */
634         isIOS : isIOS,
635         /** @type Boolean */
636         isTouch : isTouch,
637
638         /**
639          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
640          * you may want to set this to true.
641          * @type Boolean
642          */
643         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
644         
645         
646                 
647         /**
648          * Selects a single element as a Roo Element
649          * This is about as close as you can get to jQuery's $('do crazy stuff')
650          * @param {String} selector The selector/xpath query
651          * @param {Node} root (optional) The start of the query (defaults to document).
652          * @return {Roo.Element}
653          */
654         selectNode : function(selector, root) 
655         {
656             var node = Roo.DomQuery.selectNode(selector,root);
657             return node ? Roo.get(node) : new Roo.Element(false);
658         }
659         
660     });
661
662
663 })();
664
665 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
666                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
667                 "Roo.app", "Roo.ux",
668                 "Roo.bootstrap",
669                 "Roo.bootstrap.dash");
670 /*
671  * Based on:
672  * Ext JS Library 1.1.1
673  * Copyright(c) 2006-2007, Ext JS, LLC.
674  *
675  * Originally Released Under LGPL - original licence link has changed is not relivant.
676  *
677  * Fork - LGPL
678  * <script type="text/javascript">
679  */
680
681 (function() {    
682     // wrappedn so fnCleanup is not in global scope...
683     if(Roo.isIE) {
684         function fnCleanUp() {
685             var p = Function.prototype;
686             delete p.createSequence;
687             delete p.defer;
688             delete p.createDelegate;
689             delete p.createCallback;
690             delete p.createInterceptor;
691
692             window.detachEvent("onunload", fnCleanUp);
693         }
694         window.attachEvent("onunload", fnCleanUp);
695     }
696 })();
697
698
699 /**
700  * @class Function
701  * These functions are available on every Function object (any JavaScript function).
702  */
703 Roo.apply(Function.prototype, {
704      /**
705      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
706      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
707      * Will create a function that is bound to those 2 args.
708      * @return {Function} The new function
709     */
710     createCallback : function(/*args...*/){
711         // make args available, in function below
712         var args = arguments;
713         var method = this;
714         return function() {
715             return method.apply(window, args);
716         };
717     },
718
719     /**
720      * Creates a delegate (callback) that sets the scope to obj.
721      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
722      * Will create a function that is automatically scoped to this.
723      * @param {Object} obj (optional) The object for which the scope is set
724      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
725      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
726      *                                             if a number the args are inserted at the specified position
727      * @return {Function} The new function
728      */
729     createDelegate : function(obj, args, appendArgs){
730         var method = this;
731         return function() {
732             var callArgs = args || arguments;
733             if(appendArgs === true){
734                 callArgs = Array.prototype.slice.call(arguments, 0);
735                 callArgs = callArgs.concat(args);
736             }else if(typeof appendArgs == "number"){
737                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
738                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
739                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
740             }
741             return method.apply(obj || window, callArgs);
742         };
743     },
744
745     /**
746      * Calls this function after the number of millseconds specified.
747      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
748      * @param {Object} obj (optional) The object for which the scope is set
749      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
750      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
751      *                                             if a number the args are inserted at the specified position
752      * @return {Number} The timeout id that can be used with clearTimeout
753      */
754     defer : function(millis, obj, args, appendArgs){
755         var fn = this.createDelegate(obj, args, appendArgs);
756         if(millis){
757             return setTimeout(fn, millis);
758         }
759         fn();
760         return 0;
761     },
762     /**
763      * Create a combined function call sequence of the original function + the passed function.
764      * The resulting function returns the results of the original function.
765      * The passed fcn is called with the parameters of the original function
766      * @param {Function} fcn The function to sequence
767      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
768      * @return {Function} The new function
769      */
770     createSequence : function(fcn, scope){
771         if(typeof fcn != "function"){
772             return this;
773         }
774         var method = this;
775         return function() {
776             var retval = method.apply(this || window, arguments);
777             fcn.apply(scope || this || window, arguments);
778             return retval;
779         };
780     },
781
782     /**
783      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
784      * The resulting function returns the results of the original function.
785      * The passed fcn is called with the parameters of the original function.
786      * @addon
787      * @param {Function} fcn The function to call before the original
788      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
789      * @return {Function} The new function
790      */
791     createInterceptor : function(fcn, scope){
792         if(typeof fcn != "function"){
793             return this;
794         }
795         var method = this;
796         return function() {
797             fcn.target = this;
798             fcn.method = method;
799             if(fcn.apply(scope || this || window, arguments) === false){
800                 return;
801             }
802             return method.apply(this || window, arguments);
803         };
804     }
805 });
806 /*
807  * Based on:
808  * Ext JS Library 1.1.1
809  * Copyright(c) 2006-2007, Ext JS, LLC.
810  *
811  * Originally Released Under LGPL - original licence link has changed is not relivant.
812  *
813  * Fork - LGPL
814  * <script type="text/javascript">
815  */
816
817 Roo.applyIf(String, {
818     
819     /** @scope String */
820     
821     /**
822      * Escapes the passed string for ' and \
823      * @param {String} string The string to escape
824      * @return {String} The escaped string
825      * @static
826      */
827     escape : function(string) {
828         return string.replace(/('|\\)/g, "\\$1");
829     },
830
831     /**
832      * Pads the left side of a string with a specified character.  This is especially useful
833      * for normalizing number and date strings.  Example usage:
834      * <pre><code>
835 var s = String.leftPad('123', 5, '0');
836 // s now contains the string: '00123'
837 </code></pre>
838      * @param {String} string The original string
839      * @param {Number} size The total length of the output string
840      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
841      * @return {String} The padded string
842      * @static
843      */
844     leftPad : function (val, size, ch) {
845         var result = new String(val);
846         if(ch === null || ch === undefined || ch === '') {
847             ch = " ";
848         }
849         while (result.length < size) {
850             result = ch + result;
851         }
852         return result;
853     },
854
855     /**
856      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
857      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
858      * <pre><code>
859 var cls = 'my-class', text = 'Some text';
860 var s = String.format('<div class="{0}">{1}</div>', cls, text);
861 // s now contains the string: '<div class="my-class">Some text</div>'
862 </code></pre>
863      * @param {String} string The tokenized string to be formatted
864      * @param {String} value1 The value to replace token {0}
865      * @param {String} value2 Etc...
866      * @return {String} The formatted string
867      * @static
868      */
869     format : function(format){
870         var args = Array.prototype.slice.call(arguments, 1);
871         return format.replace(/\{(\d+)\}/g, function(m, i){
872             return Roo.util.Format.htmlEncode(args[i]);
873         });
874     }
875 });
876
877 /**
878  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
879  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
880  * they are already different, the first value passed in is returned.  Note that this method returns the new value
881  * but does not change the current string.
882  * <pre><code>
883 // alternate sort directions
884 sort = sort.toggle('ASC', 'DESC');
885
886 // instead of conditional logic:
887 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
888 </code></pre>
889  * @param {String} value The value to compare to the current string
890  * @param {String} other The new value to use if the string already equals the first value passed in
891  * @return {String} The new value
892  */
893  
894 String.prototype.toggle = function(value, other){
895     return this == value ? other : value;
896 };/*
897  * Based on:
898  * Ext JS Library 1.1.1
899  * Copyright(c) 2006-2007, Ext JS, LLC.
900  *
901  * Originally Released Under LGPL - original licence link has changed is not relivant.
902  *
903  * Fork - LGPL
904  * <script type="text/javascript">
905  */
906
907  /**
908  * @class Number
909  */
910 Roo.applyIf(Number.prototype, {
911     /**
912      * Checks whether or not the current number is within a desired range.  If the number is already within the
913      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
914      * exceeded.  Note that this method returns the constrained value but does not change the current number.
915      * @param {Number} min The minimum number in the range
916      * @param {Number} max The maximum number in the range
917      * @return {Number} The constrained value if outside the range, otherwise the current value
918      */
919     constrain : function(min, max){
920         return Math.min(Math.max(this, min), max);
921     }
922 });/*
923  * Based on:
924  * Ext JS Library 1.1.1
925  * Copyright(c) 2006-2007, Ext JS, LLC.
926  *
927  * Originally Released Under LGPL - original licence link has changed is not relivant.
928  *
929  * Fork - LGPL
930  * <script type="text/javascript">
931  */
932  /**
933  * @class Array
934  */
935 Roo.applyIf(Array.prototype, {
936     /**
937      * 
938      * Checks whether or not the specified object exists in the array.
939      * @param {Object} o The object to check for
940      * @return {Number} The index of o in the array (or -1 if it is not found)
941      */
942     indexOf : function(o){
943        for (var i = 0, len = this.length; i < len; i++){
944               if(this[i] == o) { return i; }
945        }
946            return -1;
947     },
948
949     /**
950      * Removes the specified object from the array.  If the object is not found nothing happens.
951      * @param {Object} o The object to remove
952      */
953     remove : function(o){
954        var index = this.indexOf(o);
955        if(index != -1){
956            this.splice(index, 1);
957        }
958     },
959     /**
960      * Map (JS 1.6 compatibility)
961      * @param {Function} function  to call
962      */
963     map : function(fun )
964     {
965         var len = this.length >>> 0;
966         if (typeof fun != "function") {
967             throw new TypeError();
968         }
969         var res = new Array(len);
970         var thisp = arguments[1];
971         for (var i = 0; i < len; i++)
972         {
973             if (i in this) {
974                 res[i] = fun.call(thisp, this[i], i, this);
975             }
976         }
977
978         return res;
979     }
980     
981 });
982
983
984  
985 /*
986  * Based on:
987  * Ext JS Library 1.1.1
988  * Copyright(c) 2006-2007, Ext JS, LLC.
989  *
990  * Originally Released Under LGPL - original licence link has changed is not relivant.
991  *
992  * Fork - LGPL
993  * <script type="text/javascript">
994  */
995
996 /**
997  * @class Date
998  *
999  * The date parsing and format syntax is a subset of
1000  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1001  * supported will provide results equivalent to their PHP versions.
1002  *
1003  * Following is the list of all currently supported formats:
1004  *<pre>
1005 Sample date:
1006 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1007
1008 Format  Output      Description
1009 ------  ----------  --------------------------------------------------------------
1010   d      10         Day of the month, 2 digits with leading zeros
1011   D      Wed        A textual representation of a day, three letters
1012   j      10         Day of the month without leading zeros
1013   l      Wednesday  A full textual representation of the day of the week
1014   S      th         English ordinal day of month suffix, 2 chars (use with j)
1015   w      3          Numeric representation of the day of the week
1016   z      9          The julian date, or day of the year (0-365)
1017   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1018   F      January    A full textual representation of the month
1019   m      01         Numeric representation of a month, with leading zeros
1020   M      Jan        Month name abbreviation, three letters
1021   n      1          Numeric representation of a month, without leading zeros
1022   t      31         Number of days in the given month
1023   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1024   Y      2007       A full numeric representation of a year, 4 digits
1025   y      07         A two digit representation of a year
1026   a      pm         Lowercase Ante meridiem and Post meridiem
1027   A      PM         Uppercase Ante meridiem and Post meridiem
1028   g      3          12-hour format of an hour without leading zeros
1029   G      15         24-hour format of an hour without leading zeros
1030   h      03         12-hour format of an hour with leading zeros
1031   H      15         24-hour format of an hour with leading zeros
1032   i      05         Minutes with leading zeros
1033   s      01         Seconds, with leading zeros
1034   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1035   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1036   T      CST        Timezone setting of the machine running the code
1037   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1038 </pre>
1039  *
1040  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1041  * <pre><code>
1042 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1043 document.write(dt.format('Y-m-d'));                         //2007-01-10
1044 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1045 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1046  </code></pre>
1047  *
1048  * Here are some standard date/time patterns that you might find helpful.  They
1049  * are not part of the source of Date.js, but to use them you can simply copy this
1050  * block of code into any script that is included after Date.js and they will also become
1051  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1052  * <pre><code>
1053 Date.patterns = {
1054     ISO8601Long:"Y-m-d H:i:s",
1055     ISO8601Short:"Y-m-d",
1056     ShortDate: "n/j/Y",
1057     LongDate: "l, F d, Y",
1058     FullDateTime: "l, F d, Y g:i:s A",
1059     MonthDay: "F d",
1060     ShortTime: "g:i A",
1061     LongTime: "g:i:s A",
1062     SortableDateTime: "Y-m-d\\TH:i:s",
1063     UniversalSortableDateTime: "Y-m-d H:i:sO",
1064     YearMonth: "F, Y"
1065 };
1066 </code></pre>
1067  *
1068  * Example usage:
1069  * <pre><code>
1070 var dt = new Date();
1071 document.write(dt.format(Date.patterns.ShortDate));
1072  </code></pre>
1073  */
1074
1075 /*
1076  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1077  * They generate precompiled functions from date formats instead of parsing and
1078  * processing the pattern every time you format a date.  These functions are available
1079  * on every Date object (any javascript function).
1080  *
1081  * The original article and download are here:
1082  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1083  *
1084  */
1085  
1086  
1087  // was in core
1088 /**
1089  Returns the number of milliseconds between this date and date
1090  @param {Date} date (optional) Defaults to now
1091  @return {Number} The diff in milliseconds
1092  @member Date getElapsed
1093  */
1094 Date.prototype.getElapsed = function(date) {
1095         return Math.abs((date || new Date()).getTime()-this.getTime());
1096 };
1097 // was in date file..
1098
1099
1100 // private
1101 Date.parseFunctions = {count:0};
1102 // private
1103 Date.parseRegexes = [];
1104 // private
1105 Date.formatFunctions = {count:0};
1106
1107 // private
1108 Date.prototype.dateFormat = function(format) {
1109     if (Date.formatFunctions[format] == null) {
1110         Date.createNewFormat(format);
1111     }
1112     var func = Date.formatFunctions[format];
1113     return this[func]();
1114 };
1115
1116
1117 /**
1118  * Formats a date given the supplied format string
1119  * @param {String} format The format string
1120  * @return {String} The formatted date
1121  * @method
1122  */
1123 Date.prototype.format = Date.prototype.dateFormat;
1124
1125 // private
1126 Date.createNewFormat = function(format) {
1127     var funcName = "format" + Date.formatFunctions.count++;
1128     Date.formatFunctions[format] = funcName;
1129     var code = "Date.prototype." + funcName + " = function(){return ";
1130     var special = false;
1131     var ch = '';
1132     for (var i = 0; i < format.length; ++i) {
1133         ch = format.charAt(i);
1134         if (!special && ch == "\\") {
1135             special = true;
1136         }
1137         else if (special) {
1138             special = false;
1139             code += "'" + String.escape(ch) + "' + ";
1140         }
1141         else {
1142             code += Date.getFormatCode(ch);
1143         }
1144     }
1145     /** eval:var:zzzzzzzzzzzzz */
1146     eval(code.substring(0, code.length - 3) + ";}");
1147 };
1148
1149 // private
1150 Date.getFormatCode = function(character) {
1151     switch (character) {
1152     case "d":
1153         return "String.leftPad(this.getDate(), 2, '0') + ";
1154     case "D":
1155         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1156     case "j":
1157         return "this.getDate() + ";
1158     case "l":
1159         return "Date.dayNames[this.getDay()] + ";
1160     case "S":
1161         return "this.getSuffix() + ";
1162     case "w":
1163         return "this.getDay() + ";
1164     case "z":
1165         return "this.getDayOfYear() + ";
1166     case "W":
1167         return "this.getWeekOfYear() + ";
1168     case "F":
1169         return "Date.monthNames[this.getMonth()] + ";
1170     case "m":
1171         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1172     case "M":
1173         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1174     case "n":
1175         return "(this.getMonth() + 1) + ";
1176     case "t":
1177         return "this.getDaysInMonth() + ";
1178     case "L":
1179         return "(this.isLeapYear() ? 1 : 0) + ";
1180     case "Y":
1181         return "this.getFullYear() + ";
1182     case "y":
1183         return "('' + this.getFullYear()).substring(2, 4) + ";
1184     case "a":
1185         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1186     case "A":
1187         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1188     case "g":
1189         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1190     case "G":
1191         return "this.getHours() + ";
1192     case "h":
1193         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1194     case "H":
1195         return "String.leftPad(this.getHours(), 2, '0') + ";
1196     case "i":
1197         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1198     case "s":
1199         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1200     case "O":
1201         return "this.getGMTOffset() + ";
1202     case "P":
1203         return "this.getGMTColonOffset() + ";
1204     case "T":
1205         return "this.getTimezone() + ";
1206     case "Z":
1207         return "(this.getTimezoneOffset() * -60) + ";
1208     default:
1209         return "'" + String.escape(character) + "' + ";
1210     }
1211 };
1212
1213 /**
1214  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1215  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1216  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1217  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1218  * string or the parse operation will fail.
1219  * Example Usage:
1220 <pre><code>
1221 //dt = Fri May 25 2007 (current date)
1222 var dt = new Date();
1223
1224 //dt = Thu May 25 2006 (today's month/day in 2006)
1225 dt = Date.parseDate("2006", "Y");
1226
1227 //dt = Sun Jan 15 2006 (all date parts specified)
1228 dt = Date.parseDate("2006-1-15", "Y-m-d");
1229
1230 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1231 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1232 </code></pre>
1233  * @param {String} input The unparsed date as a string
1234  * @param {String} format The format the date is in
1235  * @return {Date} The parsed date
1236  * @static
1237  */
1238 Date.parseDate = function(input, format) {
1239     if (Date.parseFunctions[format] == null) {
1240         Date.createParser(format);
1241     }
1242     var func = Date.parseFunctions[format];
1243     return Date[func](input);
1244 };
1245 /**
1246  * @private
1247  */
1248
1249 Date.createParser = function(format) {
1250     var funcName = "parse" + Date.parseFunctions.count++;
1251     var regexNum = Date.parseRegexes.length;
1252     var currentGroup = 1;
1253     Date.parseFunctions[format] = funcName;
1254
1255     var code = "Date." + funcName + " = function(input){\n"
1256         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1257         + "var d = new Date();\n"
1258         + "y = d.getFullYear();\n"
1259         + "m = d.getMonth();\n"
1260         + "d = d.getDate();\n"
1261         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1262         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1263         + "if (results && results.length > 0) {";
1264     var regex = "";
1265
1266     var special = false;
1267     var ch = '';
1268     for (var i = 0; i < format.length; ++i) {
1269         ch = format.charAt(i);
1270         if (!special && ch == "\\") {
1271             special = true;
1272         }
1273         else if (special) {
1274             special = false;
1275             regex += String.escape(ch);
1276         }
1277         else {
1278             var obj = Date.formatCodeToRegex(ch, currentGroup);
1279             currentGroup += obj.g;
1280             regex += obj.s;
1281             if (obj.g && obj.c) {
1282                 code += obj.c;
1283             }
1284         }
1285     }
1286
1287     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1288         + "{v = new Date(y, m, d, h, i, s);}\n"
1289         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1290         + "{v = new Date(y, m, d, h, i);}\n"
1291         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1292         + "{v = new Date(y, m, d, h);}\n"
1293         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1294         + "{v = new Date(y, m, d);}\n"
1295         + "else if (y >= 0 && m >= 0)\n"
1296         + "{v = new Date(y, m);}\n"
1297         + "else if (y >= 0)\n"
1298         + "{v = new Date(y);}\n"
1299         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1300         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1301         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1302         + ";}";
1303
1304     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1305     /** eval:var:zzzzzzzzzzzzz */
1306     eval(code);
1307 };
1308
1309 // private
1310 Date.formatCodeToRegex = function(character, currentGroup) {
1311     switch (character) {
1312     case "D":
1313         return {g:0,
1314         c:null,
1315         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1316     case "j":
1317         return {g:1,
1318             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1319             s:"(\\d{1,2})"}; // day of month without leading zeroes
1320     case "d":
1321         return {g:1,
1322             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1323             s:"(\\d{2})"}; // day of month with leading zeroes
1324     case "l":
1325         return {g:0,
1326             c:null,
1327             s:"(?:" + Date.dayNames.join("|") + ")"};
1328     case "S":
1329         return {g:0,
1330             c:null,
1331             s:"(?:st|nd|rd|th)"};
1332     case "w":
1333         return {g:0,
1334             c:null,
1335             s:"\\d"};
1336     case "z":
1337         return {g:0,
1338             c:null,
1339             s:"(?:\\d{1,3})"};
1340     case "W":
1341         return {g:0,
1342             c:null,
1343             s:"(?:\\d{2})"};
1344     case "F":
1345         return {g:1,
1346             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1347             s:"(" + Date.monthNames.join("|") + ")"};
1348     case "M":
1349         return {g:1,
1350             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1351             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1352     case "n":
1353         return {g:1,
1354             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1355             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1356     case "m":
1357         return {g:1,
1358             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1359             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1360     case "t":
1361         return {g:0,
1362             c:null,
1363             s:"\\d{1,2}"};
1364     case "L":
1365         return {g:0,
1366             c:null,
1367             s:"(?:1|0)"};
1368     case "Y":
1369         return {g:1,
1370             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1371             s:"(\\d{4})"};
1372     case "y":
1373         return {g:1,
1374             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1375                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1376             s:"(\\d{1,2})"};
1377     case "a":
1378         return {g:1,
1379             c:"if (results[" + currentGroup + "] == 'am') {\n"
1380                 + "if (h == 12) { h = 0; }\n"
1381                 + "} else { if (h < 12) { h += 12; }}",
1382             s:"(am|pm)"};
1383     case "A":
1384         return {g:1,
1385             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1386                 + "if (h == 12) { h = 0; }\n"
1387                 + "} else { if (h < 12) { h += 12; }}",
1388             s:"(AM|PM)"};
1389     case "g":
1390     case "G":
1391         return {g:1,
1392             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1393             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1394     case "h":
1395     case "H":
1396         return {g:1,
1397             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1398             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1399     case "i":
1400         return {g:1,
1401             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1402             s:"(\\d{2})"};
1403     case "s":
1404         return {g:1,
1405             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1406             s:"(\\d{2})"};
1407     case "O":
1408         return {g:1,
1409             c:[
1410                 "o = results[", currentGroup, "];\n",
1411                 "var sn = o.substring(0,1);\n", // get + / - sign
1412                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1413                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1414                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1415                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1416             ].join(""),
1417             s:"([+\-]\\d{2,4})"};
1418     
1419     
1420     case "P":
1421         return {g:1,
1422                 c:[
1423                    "o = results[", currentGroup, "];\n",
1424                    "var sn = o.substring(0,1);\n",
1425                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1426                    "var mn = o.substring(4,6) % 60;\n",
1427                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1428                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1429             ].join(""),
1430             s:"([+\-]\\d{4})"};
1431     case "T":
1432         return {g:0,
1433             c:null,
1434             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1435     case "Z":
1436         return {g:1,
1437             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1438                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1439             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1440     default:
1441         return {g:0,
1442             c:null,
1443             s:String.escape(character)};
1444     }
1445 };
1446
1447 /**
1448  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1449  * @return {String} The abbreviated timezone name (e.g. 'CST')
1450  */
1451 Date.prototype.getTimezone = function() {
1452     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1453 };
1454
1455 /**
1456  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1457  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1458  */
1459 Date.prototype.getGMTOffset = function() {
1460     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1461         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1462         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1463 };
1464
1465 /**
1466  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1467  * @return {String} 2-characters representing hours and 2-characters representing minutes
1468  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1469  */
1470 Date.prototype.getGMTColonOffset = function() {
1471         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1472                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1473                 + ":"
1474                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1475 }
1476
1477 /**
1478  * Get the numeric day number of the year, adjusted for leap year.
1479  * @return {Number} 0 through 364 (365 in leap years)
1480  */
1481 Date.prototype.getDayOfYear = function() {
1482     var num = 0;
1483     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1484     for (var i = 0; i < this.getMonth(); ++i) {
1485         num += Date.daysInMonth[i];
1486     }
1487     return num + this.getDate() - 1;
1488 };
1489
1490 /**
1491  * Get the string representation of the numeric week number of the year
1492  * (equivalent to the format specifier 'W').
1493  * @return {String} '00' through '52'
1494  */
1495 Date.prototype.getWeekOfYear = function() {
1496     // Skip to Thursday of this week
1497     var now = this.getDayOfYear() + (4 - this.getDay());
1498     // Find the first Thursday of the year
1499     var jan1 = new Date(this.getFullYear(), 0, 1);
1500     var then = (7 - jan1.getDay() + 4);
1501     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1502 };
1503
1504 /**
1505  * Whether or not the current date is in a leap year.
1506  * @return {Boolean} True if the current date is in a leap year, else false
1507  */
1508 Date.prototype.isLeapYear = function() {
1509     var year = this.getFullYear();
1510     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1511 };
1512
1513 /**
1514  * Get the first day of the current month, adjusted for leap year.  The returned value
1515  * is the numeric day index within the week (0-6) which can be used in conjunction with
1516  * the {@link #monthNames} array to retrieve the textual day name.
1517  * Example:
1518  *<pre><code>
1519 var dt = new Date('1/10/2007');
1520 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1521 </code></pre>
1522  * @return {Number} The day number (0-6)
1523  */
1524 Date.prototype.getFirstDayOfMonth = function() {
1525     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1526     return (day < 0) ? (day + 7) : day;
1527 };
1528
1529 /**
1530  * Get the last day of the current month, adjusted for leap year.  The returned value
1531  * is the numeric day index within the week (0-6) which can be used in conjunction with
1532  * the {@link #monthNames} array to retrieve the textual day name.
1533  * Example:
1534  *<pre><code>
1535 var dt = new Date('1/10/2007');
1536 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1537 </code></pre>
1538  * @return {Number} The day number (0-6)
1539  */
1540 Date.prototype.getLastDayOfMonth = function() {
1541     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1542     return (day < 0) ? (day + 7) : day;
1543 };
1544
1545
1546 /**
1547  * Get the first date of this date's month
1548  * @return {Date}
1549  */
1550 Date.prototype.getFirstDateOfMonth = function() {
1551     return new Date(this.getFullYear(), this.getMonth(), 1);
1552 };
1553
1554 /**
1555  * Get the last date of this date's month
1556  * @return {Date}
1557  */
1558 Date.prototype.getLastDateOfMonth = function() {
1559     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1560 };
1561 /**
1562  * Get the number of days in the current month, adjusted for leap year.
1563  * @return {Number} The number of days in the month
1564  */
1565 Date.prototype.getDaysInMonth = function() {
1566     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1567     return Date.daysInMonth[this.getMonth()];
1568 };
1569
1570 /**
1571  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1572  * @return {String} 'st, 'nd', 'rd' or 'th'
1573  */
1574 Date.prototype.getSuffix = function() {
1575     switch (this.getDate()) {
1576         case 1:
1577         case 21:
1578         case 31:
1579             return "st";
1580         case 2:
1581         case 22:
1582             return "nd";
1583         case 3:
1584         case 23:
1585             return "rd";
1586         default:
1587             return "th";
1588     }
1589 };
1590
1591 // private
1592 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1593
1594 /**
1595  * An array of textual month names.
1596  * Override these values for international dates, for example...
1597  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1598  * @type Array
1599  * @static
1600  */
1601 Date.monthNames =
1602    ["January",
1603     "February",
1604     "March",
1605     "April",
1606     "May",
1607     "June",
1608     "July",
1609     "August",
1610     "September",
1611     "October",
1612     "November",
1613     "December"];
1614
1615 /**
1616  * An array of textual day names.
1617  * Override these values for international dates, for example...
1618  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1619  * @type Array
1620  * @static
1621  */
1622 Date.dayNames =
1623    ["Sunday",
1624     "Monday",
1625     "Tuesday",
1626     "Wednesday",
1627     "Thursday",
1628     "Friday",
1629     "Saturday"];
1630
1631 // private
1632 Date.y2kYear = 50;
1633 // private
1634 Date.monthNumbers = {
1635     Jan:0,
1636     Feb:1,
1637     Mar:2,
1638     Apr:3,
1639     May:4,
1640     Jun:5,
1641     Jul:6,
1642     Aug:7,
1643     Sep:8,
1644     Oct:9,
1645     Nov:10,
1646     Dec:11};
1647
1648 /**
1649  * Creates and returns a new Date instance with the exact same date value as the called instance.
1650  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1651  * variable will also be changed.  When the intention is to create a new variable that will not
1652  * modify the original instance, you should create a clone.
1653  *
1654  * Example of correctly cloning a date:
1655  * <pre><code>
1656 //wrong way:
1657 var orig = new Date('10/1/2006');
1658 var copy = orig;
1659 copy.setDate(5);
1660 document.write(orig);  //returns 'Thu Oct 05 2006'!
1661
1662 //correct way:
1663 var orig = new Date('10/1/2006');
1664 var copy = orig.clone();
1665 copy.setDate(5);
1666 document.write(orig);  //returns 'Thu Oct 01 2006'
1667 </code></pre>
1668  * @return {Date} The new Date instance
1669  */
1670 Date.prototype.clone = function() {
1671         return new Date(this.getTime());
1672 };
1673
1674 /**
1675  * Clears any time information from this date
1676  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1677  @return {Date} this or the clone
1678  */
1679 Date.prototype.clearTime = function(clone){
1680     if(clone){
1681         return this.clone().clearTime();
1682     }
1683     this.setHours(0);
1684     this.setMinutes(0);
1685     this.setSeconds(0);
1686     this.setMilliseconds(0);
1687     return this;
1688 };
1689
1690 // private
1691 // safari setMonth is broken -- check that this is only donw once...
1692 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1693     Date.brokenSetMonth = Date.prototype.setMonth;
1694         Date.prototype.setMonth = function(num){
1695                 if(num <= -1){
1696                         var n = Math.ceil(-num);
1697                         var back_year = Math.ceil(n/12);
1698                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1699                         this.setFullYear(this.getFullYear() - back_year);
1700                         return Date.brokenSetMonth.call(this, month);
1701                 } else {
1702                         return Date.brokenSetMonth.apply(this, arguments);
1703                 }
1704         };
1705 }
1706
1707 /** Date interval constant 
1708 * @static 
1709 * @type String */
1710 Date.MILLI = "ms";
1711 /** Date interval constant 
1712 * @static 
1713 * @type String */
1714 Date.SECOND = "s";
1715 /** Date interval constant 
1716 * @static 
1717 * @type String */
1718 Date.MINUTE = "mi";
1719 /** Date interval constant 
1720 * @static 
1721 * @type String */
1722 Date.HOUR = "h";
1723 /** Date interval constant 
1724 * @static 
1725 * @type String */
1726 Date.DAY = "d";
1727 /** Date interval constant 
1728 * @static 
1729 * @type String */
1730 Date.MONTH = "mo";
1731 /** Date interval constant 
1732 * @static 
1733 * @type String */
1734 Date.YEAR = "y";
1735
1736 /**
1737  * Provides a convenient method of performing basic date arithmetic.  This method
1738  * does not modify the Date instance being called - it creates and returns
1739  * a new Date instance containing the resulting date value.
1740  *
1741  * Examples:
1742  * <pre><code>
1743 //Basic usage:
1744 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1745 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1746
1747 //Negative values will subtract correctly:
1748 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1749 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1750
1751 //You can even chain several calls together in one line!
1752 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1753 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1754  </code></pre>
1755  *
1756  * @param {String} interval   A valid date interval enum value
1757  * @param {Number} value      The amount to add to the current date
1758  * @return {Date} The new Date instance
1759  */
1760 Date.prototype.add = function(interval, value){
1761   var d = this.clone();
1762   if (!interval || value === 0) { return d; }
1763   switch(interval.toLowerCase()){
1764     case Date.MILLI:
1765       d.setMilliseconds(this.getMilliseconds() + value);
1766       break;
1767     case Date.SECOND:
1768       d.setSeconds(this.getSeconds() + value);
1769       break;
1770     case Date.MINUTE:
1771       d.setMinutes(this.getMinutes() + value);
1772       break;
1773     case Date.HOUR:
1774       d.setHours(this.getHours() + value);
1775       break;
1776     case Date.DAY:
1777       d.setDate(this.getDate() + value);
1778       break;
1779     case Date.MONTH:
1780       var day = this.getDate();
1781       if(day > 28){
1782           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1783       }
1784       d.setDate(day);
1785       d.setMonth(this.getMonth() + value);
1786       break;
1787     case Date.YEAR:
1788       d.setFullYear(this.getFullYear() + value);
1789       break;
1790   }
1791   return d;
1792 };
1793 /*
1794  * Based on:
1795  * Ext JS Library 1.1.1
1796  * Copyright(c) 2006-2007, Ext JS, LLC.
1797  *
1798  * Originally Released Under LGPL - original licence link has changed is not relivant.
1799  *
1800  * Fork - LGPL
1801  * <script type="text/javascript">
1802  */
1803
1804 /**
1805  * @class Roo.lib.Dom
1806  * @static
1807  * 
1808  * Dom utils (from YIU afaik)
1809  * 
1810  **/
1811 Roo.lib.Dom = {
1812     /**
1813      * Get the view width
1814      * @param {Boolean} full True will get the full document, otherwise it's the view width
1815      * @return {Number} The width
1816      */
1817      
1818     getViewWidth : function(full) {
1819         return full ? this.getDocumentWidth() : this.getViewportWidth();
1820     },
1821     /**
1822      * Get the view height
1823      * @param {Boolean} full True will get the full document, otherwise it's the view height
1824      * @return {Number} The height
1825      */
1826     getViewHeight : function(full) {
1827         return full ? this.getDocumentHeight() : this.getViewportHeight();
1828     },
1829
1830     getDocumentHeight: function() {
1831         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1832         return Math.max(scrollHeight, this.getViewportHeight());
1833     },
1834
1835     getDocumentWidth: function() {
1836         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1837         return Math.max(scrollWidth, this.getViewportWidth());
1838     },
1839
1840     getViewportHeight: function() {
1841         var height = self.innerHeight;
1842         var mode = document.compatMode;
1843
1844         if ((mode || Roo.isIE) && !Roo.isOpera) {
1845             height = (mode == "CSS1Compat") ?
1846                      document.documentElement.clientHeight :
1847                      document.body.clientHeight;
1848         }
1849
1850         return height;
1851     },
1852
1853     getViewportWidth: function() {
1854         var width = self.innerWidth;
1855         var mode = document.compatMode;
1856
1857         if (mode || Roo.isIE) {
1858             width = (mode == "CSS1Compat") ?
1859                     document.documentElement.clientWidth :
1860                     document.body.clientWidth;
1861         }
1862         return width;
1863     },
1864
1865     isAncestor : function(p, c) {
1866         p = Roo.getDom(p);
1867         c = Roo.getDom(c);
1868         if (!p || !c) {
1869             return false;
1870         }
1871
1872         if (p.contains && !Roo.isSafari) {
1873             return p.contains(c);
1874         } else if (p.compareDocumentPosition) {
1875             return !!(p.compareDocumentPosition(c) & 16);
1876         } else {
1877             var parent = c.parentNode;
1878             while (parent) {
1879                 if (parent == p) {
1880                     return true;
1881                 }
1882                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1883                     return false;
1884                 }
1885                 parent = parent.parentNode;
1886             }
1887             return false;
1888         }
1889     },
1890
1891     getRegion : function(el) {
1892         return Roo.lib.Region.getRegion(el);
1893     },
1894
1895     getY : function(el) {
1896         return this.getXY(el)[1];
1897     },
1898
1899     getX : function(el) {
1900         return this.getXY(el)[0];
1901     },
1902
1903     getXY : function(el) {
1904         var p, pe, b, scroll, bd = document.body;
1905         el = Roo.getDom(el);
1906         var fly = Roo.lib.AnimBase.fly;
1907         if (el.getBoundingClientRect) {
1908             b = el.getBoundingClientRect();
1909             scroll = fly(document).getScroll();
1910             return [b.left + scroll.left, b.top + scroll.top];
1911         }
1912         var x = 0, y = 0;
1913
1914         p = el;
1915
1916         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1917
1918         while (p) {
1919
1920             x += p.offsetLeft;
1921             y += p.offsetTop;
1922
1923             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1924                 hasAbsolute = true;
1925             }
1926
1927             if (Roo.isGecko) {
1928                 pe = fly(p);
1929
1930                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1931                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1932
1933
1934                 x += bl;
1935                 y += bt;
1936
1937
1938                 if (p != el && pe.getStyle('overflow') != 'visible') {
1939                     x += bl;
1940                     y += bt;
1941                 }
1942             }
1943             p = p.offsetParent;
1944         }
1945
1946         if (Roo.isSafari && hasAbsolute) {
1947             x -= bd.offsetLeft;
1948             y -= bd.offsetTop;
1949         }
1950
1951         if (Roo.isGecko && !hasAbsolute) {
1952             var dbd = fly(bd);
1953             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1954             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1955         }
1956
1957         p = el.parentNode;
1958         while (p && p != bd) {
1959             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1960                 x -= p.scrollLeft;
1961                 y -= p.scrollTop;
1962             }
1963             p = p.parentNode;
1964         }
1965         return [x, y];
1966     },
1967  
1968   
1969
1970
1971     setXY : function(el, xy) {
1972         el = Roo.fly(el, '_setXY');
1973         el.position();
1974         var pts = el.translatePoints(xy);
1975         if (xy[0] !== false) {
1976             el.dom.style.left = pts.left + "px";
1977         }
1978         if (xy[1] !== false) {
1979             el.dom.style.top = pts.top + "px";
1980         }
1981     },
1982
1983     setX : function(el, x) {
1984         this.setXY(el, [x, false]);
1985     },
1986
1987     setY : function(el, y) {
1988         this.setXY(el, [false, y]);
1989     }
1990 };
1991 /*
1992  * Portions of this file are based on pieces of Yahoo User Interface Library
1993  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1994  * YUI licensed under the BSD License:
1995  * http://developer.yahoo.net/yui/license.txt
1996  * <script type="text/javascript">
1997  *
1998  */
1999
2000 Roo.lib.Event = function() {
2001     var loadComplete = false;
2002     var listeners = [];
2003     var unloadListeners = [];
2004     var retryCount = 0;
2005     var onAvailStack = [];
2006     var counter = 0;
2007     var lastError = null;
2008
2009     return {
2010         POLL_RETRYS: 200,
2011         POLL_INTERVAL: 20,
2012         EL: 0,
2013         TYPE: 1,
2014         FN: 2,
2015         WFN: 3,
2016         OBJ: 3,
2017         ADJ_SCOPE: 4,
2018         _interval: null,
2019
2020         startInterval: function() {
2021             if (!this._interval) {
2022                 var self = this;
2023                 var callback = function() {
2024                     self._tryPreloadAttach();
2025                 };
2026                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2027
2028             }
2029         },
2030
2031         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2032             onAvailStack.push({ id:         p_id,
2033                 fn:         p_fn,
2034                 obj:        p_obj,
2035                 override:   p_override,
2036                 checkReady: false    });
2037
2038             retryCount = this.POLL_RETRYS;
2039             this.startInterval();
2040         },
2041
2042
2043         addListener: function(el, eventName, fn) {
2044             el = Roo.getDom(el);
2045             if (!el || !fn) {
2046                 return false;
2047             }
2048
2049             if ("unload" == eventName) {
2050                 unloadListeners[unloadListeners.length] =
2051                 [el, eventName, fn];
2052                 return true;
2053             }
2054
2055             var wrappedFn = function(e) {
2056                 return fn(Roo.lib.Event.getEvent(e));
2057             };
2058
2059             var li = [el, eventName, fn, wrappedFn];
2060
2061             var index = listeners.length;
2062             listeners[index] = li;
2063
2064             this.doAdd(el, eventName, wrappedFn, false);
2065             return true;
2066
2067         },
2068
2069
2070         removeListener: function(el, eventName, fn) {
2071             var i, len;
2072
2073             el = Roo.getDom(el);
2074
2075             if(!fn) {
2076                 return this.purgeElement(el, false, eventName);
2077             }
2078
2079
2080             if ("unload" == eventName) {
2081
2082                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2083                     var li = unloadListeners[i];
2084                     if (li &&
2085                         li[0] == el &&
2086                         li[1] == eventName &&
2087                         li[2] == fn) {
2088                         unloadListeners.splice(i, 1);
2089                         return true;
2090                     }
2091                 }
2092
2093                 return false;
2094             }
2095
2096             var cacheItem = null;
2097
2098
2099             var index = arguments[3];
2100
2101             if ("undefined" == typeof index) {
2102                 index = this._getCacheIndex(el, eventName, fn);
2103             }
2104
2105             if (index >= 0) {
2106                 cacheItem = listeners[index];
2107             }
2108
2109             if (!el || !cacheItem) {
2110                 return false;
2111             }
2112
2113             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2114
2115             delete listeners[index][this.WFN];
2116             delete listeners[index][this.FN];
2117             listeners.splice(index, 1);
2118
2119             return true;
2120
2121         },
2122
2123
2124         getTarget: function(ev, resolveTextNode) {
2125             ev = ev.browserEvent || ev;
2126             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2127             var t = ev.target || ev.srcElement;
2128             return this.resolveTextNode(t);
2129         },
2130
2131
2132         resolveTextNode: function(node) {
2133             if (Roo.isSafari && node && 3 == node.nodeType) {
2134                 return node.parentNode;
2135             } else {
2136                 return node;
2137             }
2138         },
2139
2140
2141         getPageX: function(ev) {
2142             ev = ev.browserEvent || ev;
2143             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2144             var x = ev.pageX;
2145             if (!x && 0 !== x) {
2146                 x = ev.clientX || 0;
2147
2148                 if (Roo.isIE) {
2149                     x += this.getScroll()[1];
2150                 }
2151             }
2152
2153             return x;
2154         },
2155
2156
2157         getPageY: function(ev) {
2158             ev = ev.browserEvent || ev;
2159             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2160             var y = ev.pageY;
2161             if (!y && 0 !== y) {
2162                 y = ev.clientY || 0;
2163
2164                 if (Roo.isIE) {
2165                     y += this.getScroll()[0];
2166                 }
2167             }
2168
2169
2170             return y;
2171         },
2172
2173
2174         getXY: function(ev) {
2175             ev = ev.browserEvent || ev;
2176             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2177             return [this.getPageX(ev), this.getPageY(ev)];
2178         },
2179
2180
2181         getRelatedTarget: function(ev) {
2182             ev = ev.browserEvent || ev;
2183             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2184             var t = ev.relatedTarget;
2185             if (!t) {
2186                 if (ev.type == "mouseout") {
2187                     t = ev.toElement;
2188                 } else if (ev.type == "mouseover") {
2189                     t = ev.fromElement;
2190                 }
2191             }
2192
2193             return this.resolveTextNode(t);
2194         },
2195
2196
2197         getTime: function(ev) {
2198             ev = ev.browserEvent || ev;
2199             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2200             if (!ev.time) {
2201                 var t = new Date().getTime();
2202                 try {
2203                     ev.time = t;
2204                 } catch(ex) {
2205                     this.lastError = ex;
2206                     return t;
2207                 }
2208             }
2209
2210             return ev.time;
2211         },
2212
2213
2214         stopEvent: function(ev) {
2215             this.stopPropagation(ev);
2216             this.preventDefault(ev);
2217         },
2218
2219
2220         stopPropagation: function(ev) {
2221             ev = ev.browserEvent || ev;
2222             if (ev.stopPropagation) {
2223                 ev.stopPropagation();
2224             } else {
2225                 ev.cancelBubble = true;
2226             }
2227         },
2228
2229
2230         preventDefault: function(ev) {
2231             ev = ev.browserEvent || ev;
2232             if(ev.preventDefault) {
2233                 ev.preventDefault();
2234             } else {
2235                 ev.returnValue = false;
2236             }
2237         },
2238
2239
2240         getEvent: function(e) {
2241             var ev = e || window.event;
2242             if (!ev) {
2243                 var c = this.getEvent.caller;
2244                 while (c) {
2245                     ev = c.arguments[0];
2246                     if (ev && Event == ev.constructor) {
2247                         break;
2248                     }
2249                     c = c.caller;
2250                 }
2251             }
2252             return ev;
2253         },
2254
2255
2256         getCharCode: function(ev) {
2257             ev = ev.browserEvent || ev;
2258             return ev.charCode || ev.keyCode || 0;
2259         },
2260
2261
2262         _getCacheIndex: function(el, eventName, fn) {
2263             for (var i = 0,len = listeners.length; i < len; ++i) {
2264                 var li = listeners[i];
2265                 if (li &&
2266                     li[this.FN] == fn &&
2267                     li[this.EL] == el &&
2268                     li[this.TYPE] == eventName) {
2269                     return i;
2270                 }
2271             }
2272
2273             return -1;
2274         },
2275
2276
2277         elCache: {},
2278
2279
2280         getEl: function(id) {
2281             return document.getElementById(id);
2282         },
2283
2284
2285         clearCache: function() {
2286         },
2287
2288
2289         _load: function(e) {
2290             loadComplete = true;
2291             var EU = Roo.lib.Event;
2292
2293
2294             if (Roo.isIE) {
2295                 EU.doRemove(window, "load", EU._load);
2296             }
2297         },
2298
2299
2300         _tryPreloadAttach: function() {
2301
2302             if (this.locked) {
2303                 return false;
2304             }
2305
2306             this.locked = true;
2307
2308
2309             var tryAgain = !loadComplete;
2310             if (!tryAgain) {
2311                 tryAgain = (retryCount > 0);
2312             }
2313
2314
2315             var notAvail = [];
2316             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2317                 var item = onAvailStack[i];
2318                 if (item) {
2319                     var el = this.getEl(item.id);
2320
2321                     if (el) {
2322                         if (!item.checkReady ||
2323                             loadComplete ||
2324                             el.nextSibling ||
2325                             (document && document.body)) {
2326
2327                             var scope = el;
2328                             if (item.override) {
2329                                 if (item.override === true) {
2330                                     scope = item.obj;
2331                                 } else {
2332                                     scope = item.override;
2333                                 }
2334                             }
2335                             item.fn.call(scope, item.obj);
2336                             onAvailStack[i] = null;
2337                         }
2338                     } else {
2339                         notAvail.push(item);
2340                     }
2341                 }
2342             }
2343
2344             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2345
2346             if (tryAgain) {
2347
2348                 this.startInterval();
2349             } else {
2350                 clearInterval(this._interval);
2351                 this._interval = null;
2352             }
2353
2354             this.locked = false;
2355
2356             return true;
2357
2358         },
2359
2360
2361         purgeElement: function(el, recurse, eventName) {
2362             var elListeners = this.getListeners(el, eventName);
2363             if (elListeners) {
2364                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2365                     var l = elListeners[i];
2366                     this.removeListener(el, l.type, l.fn);
2367                 }
2368             }
2369
2370             if (recurse && el && el.childNodes) {
2371                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2372                     this.purgeElement(el.childNodes[i], recurse, eventName);
2373                 }
2374             }
2375         },
2376
2377
2378         getListeners: function(el, eventName) {
2379             var results = [], searchLists;
2380             if (!eventName) {
2381                 searchLists = [listeners, unloadListeners];
2382             } else if (eventName == "unload") {
2383                 searchLists = [unloadListeners];
2384             } else {
2385                 searchLists = [listeners];
2386             }
2387
2388             for (var j = 0; j < searchLists.length; ++j) {
2389                 var searchList = searchLists[j];
2390                 if (searchList && searchList.length > 0) {
2391                     for (var i = 0,len = searchList.length; i < len; ++i) {
2392                         var l = searchList[i];
2393                         if (l && l[this.EL] === el &&
2394                             (!eventName || eventName === l[this.TYPE])) {
2395                             results.push({
2396                                 type:   l[this.TYPE],
2397                                 fn:     l[this.FN],
2398                                 obj:    l[this.OBJ],
2399                                 adjust: l[this.ADJ_SCOPE],
2400                                 index:  i
2401                             });
2402                         }
2403                     }
2404                 }
2405             }
2406
2407             return (results.length) ? results : null;
2408         },
2409
2410
2411         _unload: function(e) {
2412
2413             var EU = Roo.lib.Event, i, j, l, len, index;
2414
2415             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2416                 l = unloadListeners[i];
2417                 if (l) {
2418                     var scope = window;
2419                     if (l[EU.ADJ_SCOPE]) {
2420                         if (l[EU.ADJ_SCOPE] === true) {
2421                             scope = l[EU.OBJ];
2422                         } else {
2423                             scope = l[EU.ADJ_SCOPE];
2424                         }
2425                     }
2426                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2427                     unloadListeners[i] = null;
2428                     l = null;
2429                     scope = null;
2430                 }
2431             }
2432
2433             unloadListeners = null;
2434
2435             if (listeners && listeners.length > 0) {
2436                 j = listeners.length;
2437                 while (j) {
2438                     index = j - 1;
2439                     l = listeners[index];
2440                     if (l) {
2441                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2442                                 l[EU.FN], index);
2443                     }
2444                     j = j - 1;
2445                 }
2446                 l = null;
2447
2448                 EU.clearCache();
2449             }
2450
2451             EU.doRemove(window, "unload", EU._unload);
2452
2453         },
2454
2455
2456         getScroll: function() {
2457             var dd = document.documentElement, db = document.body;
2458             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2459                 return [dd.scrollTop, dd.scrollLeft];
2460             } else if (db) {
2461                 return [db.scrollTop, db.scrollLeft];
2462             } else {
2463                 return [0, 0];
2464             }
2465         },
2466
2467
2468         doAdd: function () {
2469             if (window.addEventListener) {
2470                 return function(el, eventName, fn, capture) {
2471                     el.addEventListener(eventName, fn, (capture));
2472                 };
2473             } else if (window.attachEvent) {
2474                 return function(el, eventName, fn, capture) {
2475                     el.attachEvent("on" + eventName, fn);
2476                 };
2477             } else {
2478                 return function() {
2479                 };
2480             }
2481         }(),
2482
2483
2484         doRemove: function() {
2485             if (window.removeEventListener) {
2486                 return function (el, eventName, fn, capture) {
2487                     el.removeEventListener(eventName, fn, (capture));
2488                 };
2489             } else if (window.detachEvent) {
2490                 return function (el, eventName, fn) {
2491                     el.detachEvent("on" + eventName, fn);
2492                 };
2493             } else {
2494                 return function() {
2495                 };
2496             }
2497         }()
2498     };
2499     
2500 }();
2501 (function() {     
2502    
2503     var E = Roo.lib.Event;
2504     E.on = E.addListener;
2505     E.un = E.removeListener;
2506
2507     if (document && document.body) {
2508         E._load();
2509     } else {
2510         E.doAdd(window, "load", E._load);
2511     }
2512     E.doAdd(window, "unload", E._unload);
2513     E._tryPreloadAttach();
2514 })();
2515
2516 /*
2517  * Portions of this file are based on pieces of Yahoo User Interface Library
2518  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2519  * YUI licensed under the BSD License:
2520  * http://developer.yahoo.net/yui/license.txt
2521  * <script type="text/javascript">
2522  *
2523  */
2524
2525 (function() {
2526     /**
2527      * @class Roo.lib.Ajax
2528      *
2529      */
2530     Roo.lib.Ajax = {
2531         /**
2532          * @static 
2533          */
2534         request : function(method, uri, cb, data, options) {
2535             if(options){
2536                 var hs = options.headers;
2537                 if(hs){
2538                     for(var h in hs){
2539                         if(hs.hasOwnProperty(h)){
2540                             this.initHeader(h, hs[h], false);
2541                         }
2542                     }
2543                 }
2544                 if(options.xmlData){
2545                     this.initHeader('Content-Type', 'text/xml', false);
2546                     method = 'POST';
2547                     data = options.xmlData;
2548                 }
2549             }
2550
2551             return this.asyncRequest(method, uri, cb, data);
2552         },
2553
2554         serializeForm : function(form) {
2555             if(typeof form == 'string') {
2556                 form = (document.getElementById(form) || document.forms[form]);
2557             }
2558
2559             var el, name, val, disabled, data = '', hasSubmit = false;
2560             for (var i = 0; i < form.elements.length; i++) {
2561                 el = form.elements[i];
2562                 disabled = form.elements[i].disabled;
2563                 name = form.elements[i].name;
2564                 val = form.elements[i].value;
2565
2566                 if (!disabled && name){
2567                     switch (el.type)
2568                             {
2569                         case 'select-one':
2570                         case 'select-multiple':
2571                             for (var j = 0; j < el.options.length; j++) {
2572                                 if (el.options[j].selected) {
2573                                     if (Roo.isIE) {
2574                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2575                                     }
2576                                     else {
2577                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2578                                     }
2579                                 }
2580                             }
2581                             break;
2582                         case 'radio':
2583                         case 'checkbox':
2584                             if (el.checked) {
2585                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2586                             }
2587                             break;
2588                         case 'file':
2589
2590                         case undefined:
2591
2592                         case 'reset':
2593
2594                         case 'button':
2595
2596                             break;
2597                         case 'submit':
2598                             if(hasSubmit == false) {
2599                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2600                                 hasSubmit = true;
2601                             }
2602                             break;
2603                         default:
2604                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2605                             break;
2606                     }
2607                 }
2608             }
2609             data = data.substr(0, data.length - 1);
2610             return data;
2611         },
2612
2613         headers:{},
2614
2615         hasHeaders:false,
2616
2617         useDefaultHeader:true,
2618
2619         defaultPostHeader:'application/x-www-form-urlencoded',
2620
2621         useDefaultXhrHeader:true,
2622
2623         defaultXhrHeader:'XMLHttpRequest',
2624
2625         hasDefaultHeaders:true,
2626
2627         defaultHeaders:{},
2628
2629         poll:{},
2630
2631         timeout:{},
2632
2633         pollInterval:50,
2634
2635         transactionId:0,
2636
2637         setProgId:function(id)
2638         {
2639             this.activeX.unshift(id);
2640         },
2641
2642         setDefaultPostHeader:function(b)
2643         {
2644             this.useDefaultHeader = b;
2645         },
2646
2647         setDefaultXhrHeader:function(b)
2648         {
2649             this.useDefaultXhrHeader = b;
2650         },
2651
2652         setPollingInterval:function(i)
2653         {
2654             if (typeof i == 'number' && isFinite(i)) {
2655                 this.pollInterval = i;
2656             }
2657         },
2658
2659         createXhrObject:function(transactionId)
2660         {
2661             var obj,http;
2662             try
2663             {
2664
2665                 http = new XMLHttpRequest();
2666
2667                 obj = { conn:http, tId:transactionId };
2668             }
2669             catch(e)
2670             {
2671                 for (var i = 0; i < this.activeX.length; ++i) {
2672                     try
2673                     {
2674
2675                         http = new ActiveXObject(this.activeX[i]);
2676
2677                         obj = { conn:http, tId:transactionId };
2678                         break;
2679                     }
2680                     catch(e) {
2681                     }
2682                 }
2683             }
2684             finally
2685             {
2686                 return obj;
2687             }
2688         },
2689
2690         getConnectionObject:function()
2691         {
2692             var o;
2693             var tId = this.transactionId;
2694
2695             try
2696             {
2697                 o = this.createXhrObject(tId);
2698                 if (o) {
2699                     this.transactionId++;
2700                 }
2701             }
2702             catch(e) {
2703             }
2704             finally
2705             {
2706                 return o;
2707             }
2708         },
2709
2710         asyncRequest:function(method, uri, callback, postData)
2711         {
2712             var o = this.getConnectionObject();
2713
2714             if (!o) {
2715                 return null;
2716             }
2717             else {
2718                 o.conn.open(method, uri, true);
2719
2720                 if (this.useDefaultXhrHeader) {
2721                     if (!this.defaultHeaders['X-Requested-With']) {
2722                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2723                     }
2724                 }
2725
2726                 if(postData && this.useDefaultHeader){
2727                     this.initHeader('Content-Type', this.defaultPostHeader);
2728                 }
2729
2730                  if (this.hasDefaultHeaders || this.hasHeaders) {
2731                     this.setHeader(o);
2732                 }
2733
2734                 this.handleReadyState(o, callback);
2735                 o.conn.send(postData || null);
2736
2737                 return o;
2738             }
2739         },
2740
2741         handleReadyState:function(o, callback)
2742         {
2743             var oConn = this;
2744
2745             if (callback && callback.timeout) {
2746                 
2747                 this.timeout[o.tId] = window.setTimeout(function() {
2748                     oConn.abort(o, callback, true);
2749                 }, callback.timeout);
2750             }
2751
2752             this.poll[o.tId] = window.setInterval(
2753                     function() {
2754                         if (o.conn && o.conn.readyState == 4) {
2755                             window.clearInterval(oConn.poll[o.tId]);
2756                             delete oConn.poll[o.tId];
2757
2758                             if(callback && callback.timeout) {
2759                                 window.clearTimeout(oConn.timeout[o.tId]);
2760                                 delete oConn.timeout[o.tId];
2761                             }
2762
2763                             oConn.handleTransactionResponse(o, callback);
2764                         }
2765                     }
2766                     , this.pollInterval);
2767         },
2768
2769         handleTransactionResponse:function(o, callback, isAbort)
2770         {
2771
2772             if (!callback) {
2773                 this.releaseObject(o);
2774                 return;
2775             }
2776
2777             var httpStatus, responseObject;
2778
2779             try
2780             {
2781                 if (o.conn.status !== undefined && o.conn.status != 0) {
2782                     httpStatus = o.conn.status;
2783                 }
2784                 else {
2785                     httpStatus = 13030;
2786                 }
2787             }
2788             catch(e) {
2789
2790
2791                 httpStatus = 13030;
2792             }
2793
2794             if (httpStatus >= 200 && httpStatus < 300) {
2795                 responseObject = this.createResponseObject(o, callback.argument);
2796                 if (callback.success) {
2797                     if (!callback.scope) {
2798                         callback.success(responseObject);
2799                     }
2800                     else {
2801
2802
2803                         callback.success.apply(callback.scope, [responseObject]);
2804                     }
2805                 }
2806             }
2807             else {
2808                 switch (httpStatus) {
2809
2810                     case 12002:
2811                     case 12029:
2812                     case 12030:
2813                     case 12031:
2814                     case 12152:
2815                     case 13030:
2816                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2817                         if (callback.failure) {
2818                             if (!callback.scope) {
2819                                 callback.failure(responseObject);
2820                             }
2821                             else {
2822                                 callback.failure.apply(callback.scope, [responseObject]);
2823                             }
2824                         }
2825                         break;
2826                     default:
2827                         responseObject = this.createResponseObject(o, callback.argument);
2828                         if (callback.failure) {
2829                             if (!callback.scope) {
2830                                 callback.failure(responseObject);
2831                             }
2832                             else {
2833                                 callback.failure.apply(callback.scope, [responseObject]);
2834                             }
2835                         }
2836                 }
2837             }
2838
2839             this.releaseObject(o);
2840             responseObject = null;
2841         },
2842
2843         createResponseObject:function(o, callbackArg)
2844         {
2845             var obj = {};
2846             var headerObj = {};
2847
2848             try
2849             {
2850                 var headerStr = o.conn.getAllResponseHeaders();
2851                 var header = headerStr.split('\n');
2852                 for (var i = 0; i < header.length; i++) {
2853                     var delimitPos = header[i].indexOf(':');
2854                     if (delimitPos != -1) {
2855                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2856                     }
2857                 }
2858             }
2859             catch(e) {
2860             }
2861
2862             obj.tId = o.tId;
2863             obj.status = o.conn.status;
2864             obj.statusText = o.conn.statusText;
2865             obj.getResponseHeader = headerObj;
2866             obj.getAllResponseHeaders = headerStr;
2867             obj.responseText = o.conn.responseText;
2868             obj.responseXML = o.conn.responseXML;
2869
2870             if (typeof callbackArg !== undefined) {
2871                 obj.argument = callbackArg;
2872             }
2873
2874             return obj;
2875         },
2876
2877         createExceptionObject:function(tId, callbackArg, isAbort)
2878         {
2879             var COMM_CODE = 0;
2880             var COMM_ERROR = 'communication failure';
2881             var ABORT_CODE = -1;
2882             var ABORT_ERROR = 'transaction aborted';
2883
2884             var obj = {};
2885
2886             obj.tId = tId;
2887             if (isAbort) {
2888                 obj.status = ABORT_CODE;
2889                 obj.statusText = ABORT_ERROR;
2890             }
2891             else {
2892                 obj.status = COMM_CODE;
2893                 obj.statusText = COMM_ERROR;
2894             }
2895
2896             if (callbackArg) {
2897                 obj.argument = callbackArg;
2898             }
2899
2900             return obj;
2901         },
2902
2903         initHeader:function(label, value, isDefault)
2904         {
2905             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2906
2907             if (headerObj[label] === undefined) {
2908                 headerObj[label] = value;
2909             }
2910             else {
2911
2912
2913                 headerObj[label] = value + "," + headerObj[label];
2914             }
2915
2916             if (isDefault) {
2917                 this.hasDefaultHeaders = true;
2918             }
2919             else {
2920                 this.hasHeaders = true;
2921             }
2922         },
2923
2924
2925         setHeader:function(o)
2926         {
2927             if (this.hasDefaultHeaders) {
2928                 for (var prop in this.defaultHeaders) {
2929                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2930                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2931                     }
2932                 }
2933             }
2934
2935             if (this.hasHeaders) {
2936                 for (var prop in this.headers) {
2937                     if (this.headers.hasOwnProperty(prop)) {
2938                         o.conn.setRequestHeader(prop, this.headers[prop]);
2939                     }
2940                 }
2941                 this.headers = {};
2942                 this.hasHeaders = false;
2943             }
2944         },
2945
2946         resetDefaultHeaders:function() {
2947             delete this.defaultHeaders;
2948             this.defaultHeaders = {};
2949             this.hasDefaultHeaders = false;
2950         },
2951
2952         abort:function(o, callback, isTimeout)
2953         {
2954             if(this.isCallInProgress(o)) {
2955                 o.conn.abort();
2956                 window.clearInterval(this.poll[o.tId]);
2957                 delete this.poll[o.tId];
2958                 if (isTimeout) {
2959                     delete this.timeout[o.tId];
2960                 }
2961
2962                 this.handleTransactionResponse(o, callback, true);
2963
2964                 return true;
2965             }
2966             else {
2967                 return false;
2968             }
2969         },
2970
2971
2972         isCallInProgress:function(o)
2973         {
2974             if (o && o.conn) {
2975                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2976             }
2977             else {
2978
2979                 return false;
2980             }
2981         },
2982
2983
2984         releaseObject:function(o)
2985         {
2986
2987             o.conn = null;
2988
2989             o = null;
2990         },
2991
2992         activeX:[
2993         'MSXML2.XMLHTTP.3.0',
2994         'MSXML2.XMLHTTP',
2995         'Microsoft.XMLHTTP'
2996         ]
2997
2998
2999     };
3000 })();/*
3001  * Portions of this file are based on pieces of Yahoo User Interface Library
3002  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3003  * YUI licensed under the BSD License:
3004  * http://developer.yahoo.net/yui/license.txt
3005  * <script type="text/javascript">
3006  *
3007  */
3008
3009 Roo.lib.Region = function(t, r, b, l) {
3010     this.top = t;
3011     this[1] = t;
3012     this.right = r;
3013     this.bottom = b;
3014     this.left = l;
3015     this[0] = l;
3016 };
3017
3018
3019 Roo.lib.Region.prototype = {
3020     contains : function(region) {
3021         return ( region.left >= this.left &&
3022                  region.right <= this.right &&
3023                  region.top >= this.top &&
3024                  region.bottom <= this.bottom    );
3025
3026     },
3027
3028     getArea : function() {
3029         return ( (this.bottom - this.top) * (this.right - this.left) );
3030     },
3031
3032     intersect : function(region) {
3033         var t = Math.max(this.top, region.top);
3034         var r = Math.min(this.right, region.right);
3035         var b = Math.min(this.bottom, region.bottom);
3036         var l = Math.max(this.left, region.left);
3037
3038         if (b >= t && r >= l) {
3039             return new Roo.lib.Region(t, r, b, l);
3040         } else {
3041             return null;
3042         }
3043     },
3044     union : function(region) {
3045         var t = Math.min(this.top, region.top);
3046         var r = Math.max(this.right, region.right);
3047         var b = Math.max(this.bottom, region.bottom);
3048         var l = Math.min(this.left, region.left);
3049
3050         return new Roo.lib.Region(t, r, b, l);
3051     },
3052
3053     adjust : function(t, l, b, r) {
3054         this.top += t;
3055         this.left += l;
3056         this.right += r;
3057         this.bottom += b;
3058         return this;
3059     }
3060 };
3061
3062 Roo.lib.Region.getRegion = function(el) {
3063     var p = Roo.lib.Dom.getXY(el);
3064
3065     var t = p[1];
3066     var r = p[0] + el.offsetWidth;
3067     var b = p[1] + el.offsetHeight;
3068     var l = p[0];
3069
3070     return new Roo.lib.Region(t, r, b, l);
3071 };
3072 /*
3073  * Portions of this file are based on pieces of Yahoo User Interface Library
3074  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3075  * YUI licensed under the BSD License:
3076  * http://developer.yahoo.net/yui/license.txt
3077  * <script type="text/javascript">
3078  *
3079  */
3080 //@@dep Roo.lib.Region
3081
3082
3083 Roo.lib.Point = function(x, y) {
3084     if (x instanceof Array) {
3085         y = x[1];
3086         x = x[0];
3087     }
3088     this.x = this.right = this.left = this[0] = x;
3089     this.y = this.top = this.bottom = this[1] = y;
3090 };
3091
3092 Roo.lib.Point.prototype = new Roo.lib.Region();
3093 /*
3094  * Portions of this file are based on pieces of Yahoo User Interface Library
3095  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3096  * YUI licensed under the BSD License:
3097  * http://developer.yahoo.net/yui/license.txt
3098  * <script type="text/javascript">
3099  *
3100  */
3101  
3102 (function() {   
3103
3104     Roo.lib.Anim = {
3105         scroll : function(el, args, duration, easing, cb, scope) {
3106             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3107         },
3108
3109         motion : function(el, args, duration, easing, cb, scope) {
3110             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3111         },
3112
3113         color : function(el, args, duration, easing, cb, scope) {
3114             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3115         },
3116
3117         run : function(el, args, duration, easing, cb, scope, type) {
3118             type = type || Roo.lib.AnimBase;
3119             if (typeof easing == "string") {
3120                 easing = Roo.lib.Easing[easing];
3121             }
3122             var anim = new type(el, args, duration, easing);
3123             anim.animateX(function() {
3124                 Roo.callback(cb, scope);
3125             });
3126             return anim;
3127         }
3128     };
3129 })();/*
3130  * Portions of this file are based on pieces of Yahoo User Interface Library
3131  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3132  * YUI licensed under the BSD License:
3133  * http://developer.yahoo.net/yui/license.txt
3134  * <script type="text/javascript">
3135  *
3136  */
3137
3138 (function() {    
3139     var libFlyweight;
3140     
3141     function fly(el) {
3142         if (!libFlyweight) {
3143             libFlyweight = new Roo.Element.Flyweight();
3144         }
3145         libFlyweight.dom = el;
3146         return libFlyweight;
3147     }
3148
3149     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3150     
3151    
3152     
3153     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3154         if (el) {
3155             this.init(el, attributes, duration, method);
3156         }
3157     };
3158
3159     Roo.lib.AnimBase.fly = fly;
3160     
3161     
3162     
3163     Roo.lib.AnimBase.prototype = {
3164
3165         toString: function() {
3166             var el = this.getEl();
3167             var id = el.id || el.tagName;
3168             return ("Anim " + id);
3169         },
3170
3171         patterns: {
3172             noNegatives:        /width|height|opacity|padding/i,
3173             offsetAttribute:  /^((width|height)|(top|left))$/,
3174             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3175             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3176         },
3177
3178
3179         doMethod: function(attr, start, end) {
3180             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3181         },
3182
3183
3184         setAttribute: function(attr, val, unit) {
3185             if (this.patterns.noNegatives.test(attr)) {
3186                 val = (val > 0) ? val : 0;
3187             }
3188
3189             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3190         },
3191
3192
3193         getAttribute: function(attr) {
3194             var el = this.getEl();
3195             var val = fly(el).getStyle(attr);
3196
3197             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3198                 return parseFloat(val);
3199             }
3200
3201             var a = this.patterns.offsetAttribute.exec(attr) || [];
3202             var pos = !!( a[3] );
3203             var box = !!( a[2] );
3204
3205
3206             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3207                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3208             } else {
3209                 val = 0;
3210             }
3211
3212             return val;
3213         },
3214
3215
3216         getDefaultUnit: function(attr) {
3217             if (this.patterns.defaultUnit.test(attr)) {
3218                 return 'px';
3219             }
3220
3221             return '';
3222         },
3223
3224         animateX : function(callback, scope) {
3225             var f = function() {
3226                 this.onComplete.removeListener(f);
3227                 if (typeof callback == "function") {
3228                     callback.call(scope || this, this);
3229                 }
3230             };
3231             this.onComplete.addListener(f, this);
3232             this.animate();
3233         },
3234
3235
3236         setRuntimeAttribute: function(attr) {
3237             var start;
3238             var end;
3239             var attributes = this.attributes;
3240
3241             this.runtimeAttributes[attr] = {};
3242
3243             var isset = function(prop) {
3244                 return (typeof prop !== 'undefined');
3245             };
3246
3247             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3248                 return false;
3249             }
3250
3251             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3252
3253
3254             if (isset(attributes[attr]['to'])) {
3255                 end = attributes[attr]['to'];
3256             } else if (isset(attributes[attr]['by'])) {
3257                 if (start.constructor == Array) {
3258                     end = [];
3259                     for (var i = 0, len = start.length; i < len; ++i) {
3260                         end[i] = start[i] + attributes[attr]['by'][i];
3261                     }
3262                 } else {
3263                     end = start + attributes[attr]['by'];
3264                 }
3265             }
3266
3267             this.runtimeAttributes[attr].start = start;
3268             this.runtimeAttributes[attr].end = end;
3269
3270
3271             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3272         },
3273
3274
3275         init: function(el, attributes, duration, method) {
3276
3277             var isAnimated = false;
3278
3279
3280             var startTime = null;
3281
3282
3283             var actualFrames = 0;
3284
3285
3286             el = Roo.getDom(el);
3287
3288
3289             this.attributes = attributes || {};
3290
3291
3292             this.duration = duration || 1;
3293
3294
3295             this.method = method || Roo.lib.Easing.easeNone;
3296
3297
3298             this.useSeconds = true;
3299
3300
3301             this.currentFrame = 0;
3302
3303
3304             this.totalFrames = Roo.lib.AnimMgr.fps;
3305
3306
3307             this.getEl = function() {
3308                 return el;
3309             };
3310
3311
3312             this.isAnimated = function() {
3313                 return isAnimated;
3314             };
3315
3316
3317             this.getStartTime = function() {
3318                 return startTime;
3319             };
3320
3321             this.runtimeAttributes = {};
3322
3323
3324             this.animate = function() {
3325                 if (this.isAnimated()) {
3326                     return false;
3327                 }
3328
3329                 this.currentFrame = 0;
3330
3331                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3332
3333                 Roo.lib.AnimMgr.registerElement(this);
3334             };
3335
3336
3337             this.stop = function(finish) {
3338                 if (finish) {
3339                     this.currentFrame = this.totalFrames;
3340                     this._onTween.fire();
3341                 }
3342                 Roo.lib.AnimMgr.stop(this);
3343             };
3344
3345             var onStart = function() {
3346                 this.onStart.fire();
3347
3348                 this.runtimeAttributes = {};
3349                 for (var attr in this.attributes) {
3350                     this.setRuntimeAttribute(attr);
3351                 }
3352
3353                 isAnimated = true;
3354                 actualFrames = 0;
3355                 startTime = new Date();
3356             };
3357
3358
3359             var onTween = function() {
3360                 var data = {
3361                     duration: new Date() - this.getStartTime(),
3362                     currentFrame: this.currentFrame
3363                 };
3364
3365                 data.toString = function() {
3366                     return (
3367                             'duration: ' + data.duration +
3368                             ', currentFrame: ' + data.currentFrame
3369                             );
3370                 };
3371
3372                 this.onTween.fire(data);
3373
3374                 var runtimeAttributes = this.runtimeAttributes;
3375
3376                 for (var attr in runtimeAttributes) {
3377                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3378                 }
3379
3380                 actualFrames += 1;
3381             };
3382
3383             var onComplete = function() {
3384                 var actual_duration = (new Date() - startTime) / 1000 ;
3385
3386                 var data = {
3387                     duration: actual_duration,
3388                     frames: actualFrames,
3389                     fps: actualFrames / actual_duration
3390                 };
3391
3392                 data.toString = function() {
3393                     return (
3394                             'duration: ' + data.duration +
3395                             ', frames: ' + data.frames +
3396                             ', fps: ' + data.fps
3397                             );
3398                 };
3399
3400                 isAnimated = false;
3401                 actualFrames = 0;
3402                 this.onComplete.fire(data);
3403             };
3404
3405
3406             this._onStart = new Roo.util.Event(this);
3407             this.onStart = new Roo.util.Event(this);
3408             this.onTween = new Roo.util.Event(this);
3409             this._onTween = new Roo.util.Event(this);
3410             this.onComplete = new Roo.util.Event(this);
3411             this._onComplete = new Roo.util.Event(this);
3412             this._onStart.addListener(onStart);
3413             this._onTween.addListener(onTween);
3414             this._onComplete.addListener(onComplete);
3415         }
3416     };
3417 })();
3418 /*
3419  * Portions of this file are based on pieces of Yahoo User Interface Library
3420  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3421  * YUI licensed under the BSD License:
3422  * http://developer.yahoo.net/yui/license.txt
3423  * <script type="text/javascript">
3424  *
3425  */
3426
3427 Roo.lib.AnimMgr = new function() {
3428
3429     var thread = null;
3430
3431
3432     var queue = [];
3433
3434
3435     var tweenCount = 0;
3436
3437
3438     this.fps = 1000;
3439
3440
3441     this.delay = 1;
3442
3443
3444     this.registerElement = function(tween) {
3445         queue[queue.length] = tween;
3446         tweenCount += 1;
3447         tween._onStart.fire();
3448         this.start();
3449     };
3450
3451
3452     this.unRegister = function(tween, index) {
3453         tween._onComplete.fire();
3454         index = index || getIndex(tween);
3455         if (index != -1) {
3456             queue.splice(index, 1);
3457         }
3458
3459         tweenCount -= 1;
3460         if (tweenCount <= 0) {
3461             this.stop();
3462         }
3463     };
3464
3465
3466     this.start = function() {
3467         if (thread === null) {
3468             thread = setInterval(this.run, this.delay);
3469         }
3470     };
3471
3472
3473     this.stop = function(tween) {
3474         if (!tween) {
3475             clearInterval(thread);
3476
3477             for (var i = 0, len = queue.length; i < len; ++i) {
3478                 if (queue[0].isAnimated()) {
3479                     this.unRegister(queue[0], 0);
3480                 }
3481             }
3482
3483             queue = [];
3484             thread = null;
3485             tweenCount = 0;
3486         }
3487         else {
3488             this.unRegister(tween);
3489         }
3490     };
3491
3492
3493     this.run = function() {
3494         for (var i = 0, len = queue.length; i < len; ++i) {
3495             var tween = queue[i];
3496             if (!tween || !tween.isAnimated()) {
3497                 continue;
3498             }
3499
3500             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3501             {
3502                 tween.currentFrame += 1;
3503
3504                 if (tween.useSeconds) {
3505                     correctFrame(tween);
3506                 }
3507                 tween._onTween.fire();
3508             }
3509             else {
3510                 Roo.lib.AnimMgr.stop(tween, i);
3511             }
3512         }
3513     };
3514
3515     var getIndex = function(anim) {
3516         for (var i = 0, len = queue.length; i < len; ++i) {
3517             if (queue[i] == anim) {
3518                 return i;
3519             }
3520         }
3521         return -1;
3522     };
3523
3524
3525     var correctFrame = function(tween) {
3526         var frames = tween.totalFrames;
3527         var frame = tween.currentFrame;
3528         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3529         var elapsed = (new Date() - tween.getStartTime());
3530         var tweak = 0;
3531
3532         if (elapsed < tween.duration * 1000) {
3533             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3534         } else {
3535             tweak = frames - (frame + 1);
3536         }
3537         if (tweak > 0 && isFinite(tweak)) {
3538             if (tween.currentFrame + tweak >= frames) {
3539                 tweak = frames - (frame + 1);
3540             }
3541
3542             tween.currentFrame += tweak;
3543         }
3544     };
3545 };
3546
3547     /*
3548  * Portions of this file are based on pieces of Yahoo User Interface Library
3549  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3550  * YUI licensed under the BSD License:
3551  * http://developer.yahoo.net/yui/license.txt
3552  * <script type="text/javascript">
3553  *
3554  */
3555 Roo.lib.Bezier = new function() {
3556
3557         this.getPosition = function(points, t) {
3558             var n = points.length;
3559             var tmp = [];
3560
3561             for (var i = 0; i < n; ++i) {
3562                 tmp[i] = [points[i][0], points[i][1]];
3563             }
3564
3565             for (var j = 1; j < n; ++j) {
3566                 for (i = 0; i < n - j; ++i) {
3567                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3568                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3569                 }
3570             }
3571
3572             return [ tmp[0][0], tmp[0][1] ];
3573
3574         };
3575     };/*
3576  * Portions of this file are based on pieces of Yahoo User Interface Library
3577  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3578  * YUI licensed under the BSD License:
3579  * http://developer.yahoo.net/yui/license.txt
3580  * <script type="text/javascript">
3581  *
3582  */
3583 (function() {
3584
3585     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3586         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3587     };
3588
3589     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3590
3591     var fly = Roo.lib.AnimBase.fly;
3592     var Y = Roo.lib;
3593     var superclass = Y.ColorAnim.superclass;
3594     var proto = Y.ColorAnim.prototype;
3595
3596     proto.toString = function() {
3597         var el = this.getEl();
3598         var id = el.id || el.tagName;
3599         return ("ColorAnim " + id);
3600     };
3601
3602     proto.patterns.color = /color$/i;
3603     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3604     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3605     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3606     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3607
3608
3609     proto.parseColor = function(s) {
3610         if (s.length == 3) {
3611             return s;
3612         }
3613
3614         var c = this.patterns.hex.exec(s);
3615         if (c && c.length == 4) {
3616             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3617         }
3618
3619         c = this.patterns.rgb.exec(s);
3620         if (c && c.length == 4) {
3621             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3622         }
3623
3624         c = this.patterns.hex3.exec(s);
3625         if (c && c.length == 4) {
3626             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3627         }
3628
3629         return null;
3630     };
3631     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3632     proto.getAttribute = function(attr) {
3633         var el = this.getEl();
3634         if (this.patterns.color.test(attr)) {
3635             var val = fly(el).getStyle(attr);
3636
3637             if (this.patterns.transparent.test(val)) {
3638                 var parent = el.parentNode;
3639                 val = fly(parent).getStyle(attr);
3640
3641                 while (parent && this.patterns.transparent.test(val)) {
3642                     parent = parent.parentNode;
3643                     val = fly(parent).getStyle(attr);
3644                     if (parent.tagName.toUpperCase() == 'HTML') {
3645                         val = '#fff';
3646                     }
3647                 }
3648             }
3649         } else {
3650             val = superclass.getAttribute.call(this, attr);
3651         }
3652
3653         return val;
3654     };
3655     proto.getAttribute = function(attr) {
3656         var el = this.getEl();
3657         if (this.patterns.color.test(attr)) {
3658             var val = fly(el).getStyle(attr);
3659
3660             if (this.patterns.transparent.test(val)) {
3661                 var parent = el.parentNode;
3662                 val = fly(parent).getStyle(attr);
3663
3664                 while (parent && this.patterns.transparent.test(val)) {
3665                     parent = parent.parentNode;
3666                     val = fly(parent).getStyle(attr);
3667                     if (parent.tagName.toUpperCase() == 'HTML') {
3668                         val = '#fff';
3669                     }
3670                 }
3671             }
3672         } else {
3673             val = superclass.getAttribute.call(this, attr);
3674         }
3675
3676         return val;
3677     };
3678
3679     proto.doMethod = function(attr, start, end) {
3680         var val;
3681
3682         if (this.patterns.color.test(attr)) {
3683             val = [];
3684             for (var i = 0, len = start.length; i < len; ++i) {
3685                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3686             }
3687
3688             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3689         }
3690         else {
3691             val = superclass.doMethod.call(this, attr, start, end);
3692         }
3693
3694         return val;
3695     };
3696
3697     proto.setRuntimeAttribute = function(attr) {
3698         superclass.setRuntimeAttribute.call(this, attr);
3699
3700         if (this.patterns.color.test(attr)) {
3701             var attributes = this.attributes;
3702             var start = this.parseColor(this.runtimeAttributes[attr].start);
3703             var end = this.parseColor(this.runtimeAttributes[attr].end);
3704
3705             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3706                 end = this.parseColor(attributes[attr].by);
3707
3708                 for (var i = 0, len = start.length; i < len; ++i) {
3709                     end[i] = start[i] + end[i];
3710                 }
3711             }
3712
3713             this.runtimeAttributes[attr].start = start;
3714             this.runtimeAttributes[attr].end = end;
3715         }
3716     };
3717 })();
3718
3719 /*
3720  * Portions of this file are based on pieces of Yahoo User Interface Library
3721  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3722  * YUI licensed under the BSD License:
3723  * http://developer.yahoo.net/yui/license.txt
3724  * <script type="text/javascript">
3725  *
3726  */
3727 Roo.lib.Easing = {
3728
3729
3730     easeNone: function (t, b, c, d) {
3731         return c * t / d + b;
3732     },
3733
3734
3735     easeIn: function (t, b, c, d) {
3736         return c * (t /= d) * t + b;
3737     },
3738
3739
3740     easeOut: function (t, b, c, d) {
3741         return -c * (t /= d) * (t - 2) + b;
3742     },
3743
3744
3745     easeBoth: function (t, b, c, d) {
3746         if ((t /= d / 2) < 1) {
3747             return c / 2 * t * t + b;
3748         }
3749
3750         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3751     },
3752
3753
3754     easeInStrong: function (t, b, c, d) {
3755         return c * (t /= d) * t * t * t + b;
3756     },
3757
3758
3759     easeOutStrong: function (t, b, c, d) {
3760         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3761     },
3762
3763
3764     easeBothStrong: function (t, b, c, d) {
3765         if ((t /= d / 2) < 1) {
3766             return c / 2 * t * t * t * t + b;
3767         }
3768
3769         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3770     },
3771
3772
3773
3774     elasticIn: function (t, b, c, d, a, p) {
3775         if (t == 0) {
3776             return b;
3777         }
3778         if ((t /= d) == 1) {
3779             return b + c;
3780         }
3781         if (!p) {
3782             p = d * .3;
3783         }
3784
3785         if (!a || a < Math.abs(c)) {
3786             a = c;
3787             var s = p / 4;
3788         }
3789         else {
3790             var s = p / (2 * Math.PI) * Math.asin(c / a);
3791         }
3792
3793         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3794     },
3795
3796
3797     elasticOut: function (t, b, c, d, a, p) {
3798         if (t == 0) {
3799             return b;
3800         }
3801         if ((t /= d) == 1) {
3802             return b + c;
3803         }
3804         if (!p) {
3805             p = d * .3;
3806         }
3807
3808         if (!a || a < Math.abs(c)) {
3809             a = c;
3810             var s = p / 4;
3811         }
3812         else {
3813             var s = p / (2 * Math.PI) * Math.asin(c / a);
3814         }
3815
3816         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3817     },
3818
3819
3820     elasticBoth: function (t, b, c, d, a, p) {
3821         if (t == 0) {
3822             return b;
3823         }
3824
3825         if ((t /= d / 2) == 2) {
3826             return b + c;
3827         }
3828
3829         if (!p) {
3830             p = d * (.3 * 1.5);
3831         }
3832
3833         if (!a || a < Math.abs(c)) {
3834             a = c;
3835             var s = p / 4;
3836         }
3837         else {
3838             var s = p / (2 * Math.PI) * Math.asin(c / a);
3839         }
3840
3841         if (t < 1) {
3842             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3843                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3844         }
3845         return a * Math.pow(2, -10 * (t -= 1)) *
3846                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3847     },
3848
3849
3850
3851     backIn: function (t, b, c, d, s) {
3852         if (typeof s == 'undefined') {
3853             s = 1.70158;
3854         }
3855         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3856     },
3857
3858
3859     backOut: function (t, b, c, d, s) {
3860         if (typeof s == 'undefined') {
3861             s = 1.70158;
3862         }
3863         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3864     },
3865
3866
3867     backBoth: function (t, b, c, d, s) {
3868         if (typeof s == 'undefined') {
3869             s = 1.70158;
3870         }
3871
3872         if ((t /= d / 2 ) < 1) {
3873             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3874         }
3875         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3876     },
3877
3878
3879     bounceIn: function (t, b, c, d) {
3880         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3881     },
3882
3883
3884     bounceOut: function (t, b, c, d) {
3885         if ((t /= d) < (1 / 2.75)) {
3886             return c * (7.5625 * t * t) + b;
3887         } else if (t < (2 / 2.75)) {
3888             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3889         } else if (t < (2.5 / 2.75)) {
3890             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3891         }
3892         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3893     },
3894
3895
3896     bounceBoth: function (t, b, c, d) {
3897         if (t < d / 2) {
3898             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3899         }
3900         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3901     }
3902 };/*
3903  * Portions of this file are based on pieces of Yahoo User Interface Library
3904  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3905  * YUI licensed under the BSD License:
3906  * http://developer.yahoo.net/yui/license.txt
3907  * <script type="text/javascript">
3908  *
3909  */
3910     (function() {
3911         Roo.lib.Motion = function(el, attributes, duration, method) {
3912             if (el) {
3913                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3914             }
3915         };
3916
3917         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3918
3919
3920         var Y = Roo.lib;
3921         var superclass = Y.Motion.superclass;
3922         var proto = Y.Motion.prototype;
3923
3924         proto.toString = function() {
3925             var el = this.getEl();
3926             var id = el.id || el.tagName;
3927             return ("Motion " + id);
3928         };
3929
3930         proto.patterns.points = /^points$/i;
3931
3932         proto.setAttribute = function(attr, val, unit) {
3933             if (this.patterns.points.test(attr)) {
3934                 unit = unit || 'px';
3935                 superclass.setAttribute.call(this, 'left', val[0], unit);
3936                 superclass.setAttribute.call(this, 'top', val[1], unit);
3937             } else {
3938                 superclass.setAttribute.call(this, attr, val, unit);
3939             }
3940         };
3941
3942         proto.getAttribute = function(attr) {
3943             if (this.patterns.points.test(attr)) {
3944                 var val = [
3945                         superclass.getAttribute.call(this, 'left'),
3946                         superclass.getAttribute.call(this, 'top')
3947                         ];
3948             } else {
3949                 val = superclass.getAttribute.call(this, attr);
3950             }
3951
3952             return val;
3953         };
3954
3955         proto.doMethod = function(attr, start, end) {
3956             var val = null;
3957
3958             if (this.patterns.points.test(attr)) {
3959                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3960                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3961             } else {
3962                 val = superclass.doMethod.call(this, attr, start, end);
3963             }
3964             return val;
3965         };
3966
3967         proto.setRuntimeAttribute = function(attr) {
3968             if (this.patterns.points.test(attr)) {
3969                 var el = this.getEl();
3970                 var attributes = this.attributes;
3971                 var start;
3972                 var control = attributes['points']['control'] || [];
3973                 var end;
3974                 var i, len;
3975
3976                 if (control.length > 0 && !(control[0] instanceof Array)) {
3977                     control = [control];
3978                 } else {
3979                     var tmp = [];
3980                     for (i = 0,len = control.length; i < len; ++i) {
3981                         tmp[i] = control[i];
3982                     }
3983                     control = tmp;
3984                 }
3985
3986                 Roo.fly(el).position();
3987
3988                 if (isset(attributes['points']['from'])) {
3989                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3990                 }
3991                 else {
3992                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3993                 }
3994
3995                 start = this.getAttribute('points');
3996
3997
3998                 if (isset(attributes['points']['to'])) {
3999                     end = translateValues.call(this, attributes['points']['to'], start);
4000
4001                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4002                     for (i = 0,len = control.length; i < len; ++i) {
4003                         control[i] = translateValues.call(this, control[i], start);
4004                     }
4005
4006
4007                 } else if (isset(attributes['points']['by'])) {
4008                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4009
4010                     for (i = 0,len = control.length; i < len; ++i) {
4011                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4012                     }
4013                 }
4014
4015                 this.runtimeAttributes[attr] = [start];
4016
4017                 if (control.length > 0) {
4018                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4019                 }
4020
4021                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4022             }
4023             else {
4024                 superclass.setRuntimeAttribute.call(this, attr);
4025             }
4026         };
4027
4028         var translateValues = function(val, start) {
4029             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4030             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4031
4032             return val;
4033         };
4034
4035         var isset = function(prop) {
4036             return (typeof prop !== 'undefined');
4037         };
4038     })();
4039 /*
4040  * Portions of this file are based on pieces of Yahoo User Interface Library
4041  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4042  * YUI licensed under the BSD License:
4043  * http://developer.yahoo.net/yui/license.txt
4044  * <script type="text/javascript">
4045  *
4046  */
4047     (function() {
4048         Roo.lib.Scroll = function(el, attributes, duration, method) {
4049             if (el) {
4050                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4051             }
4052         };
4053
4054         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4055
4056
4057         var Y = Roo.lib;
4058         var superclass = Y.Scroll.superclass;
4059         var proto = Y.Scroll.prototype;
4060
4061         proto.toString = function() {
4062             var el = this.getEl();
4063             var id = el.id || el.tagName;
4064             return ("Scroll " + id);
4065         };
4066
4067         proto.doMethod = function(attr, start, end) {
4068             var val = null;
4069
4070             if (attr == 'scroll') {
4071                 val = [
4072                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4073                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4074                         ];
4075
4076             } else {
4077                 val = superclass.doMethod.call(this, attr, start, end);
4078             }
4079             return val;
4080         };
4081
4082         proto.getAttribute = function(attr) {
4083             var val = null;
4084             var el = this.getEl();
4085
4086             if (attr == 'scroll') {
4087                 val = [ el.scrollLeft, el.scrollTop ];
4088             } else {
4089                 val = superclass.getAttribute.call(this, attr);
4090             }
4091
4092             return val;
4093         };
4094
4095         proto.setAttribute = function(attr, val, unit) {
4096             var el = this.getEl();
4097
4098             if (attr == 'scroll') {
4099                 el.scrollLeft = val[0];
4100                 el.scrollTop = val[1];
4101             } else {
4102                 superclass.setAttribute.call(this, attr, val, unit);
4103             }
4104         };
4105     })();
4106 /*
4107  * Based on:
4108  * Ext JS Library 1.1.1
4109  * Copyright(c) 2006-2007, Ext JS, LLC.
4110  *
4111  * Originally Released Under LGPL - original licence link has changed is not relivant.
4112  *
4113  * Fork - LGPL
4114  * <script type="text/javascript">
4115  */
4116
4117
4118 // nasty IE9 hack - what a pile of crap that is..
4119
4120  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4121     Range.prototype.createContextualFragment = function (html) {
4122         var doc = window.document;
4123         var container = doc.createElement("div");
4124         container.innerHTML = html;
4125         var frag = doc.createDocumentFragment(), n;
4126         while ((n = container.firstChild)) {
4127             frag.appendChild(n);
4128         }
4129         return frag;
4130     };
4131 }
4132
4133 /**
4134  * @class Roo.DomHelper
4135  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4136  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4137  * @singleton
4138  */
4139 Roo.DomHelper = function(){
4140     var tempTableEl = null;
4141     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4142     var tableRe = /^table|tbody|tr|td$/i;
4143     var xmlns = {};
4144     // build as innerHTML where available
4145     /** @ignore */
4146     var createHtml = function(o){
4147         if(typeof o == 'string'){
4148             return o;
4149         }
4150         var b = "";
4151         if(!o.tag){
4152             o.tag = "div";
4153         }
4154         b += "<" + o.tag;
4155         for(var attr in o){
4156             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4157             if(attr == "style"){
4158                 var s = o["style"];
4159                 if(typeof s == "function"){
4160                     s = s.call();
4161                 }
4162                 if(typeof s == "string"){
4163                     b += ' style="' + s + '"';
4164                 }else if(typeof s == "object"){
4165                     b += ' style="';
4166                     for(var key in s){
4167                         if(typeof s[key] != "function"){
4168                             b += key + ":" + s[key] + ";";
4169                         }
4170                     }
4171                     b += '"';
4172                 }
4173             }else{
4174                 if(attr == "cls"){
4175                     b += ' class="' + o["cls"] + '"';
4176                 }else if(attr == "htmlFor"){
4177                     b += ' for="' + o["htmlFor"] + '"';
4178                 }else{
4179                     b += " " + attr + '="' + o[attr] + '"';
4180                 }
4181             }
4182         }
4183         if(emptyTags.test(o.tag)){
4184             b += "/>";
4185         }else{
4186             b += ">";
4187             var cn = o.children || o.cn;
4188             if(cn){
4189                 //http://bugs.kde.org/show_bug.cgi?id=71506
4190                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4191                     for(var i = 0, len = cn.length; i < len; i++) {
4192                         b += createHtml(cn[i], b);
4193                     }
4194                 }else{
4195                     b += createHtml(cn, b);
4196                 }
4197             }
4198             if(o.html){
4199                 b += o.html;
4200             }
4201             b += "</" + o.tag + ">";
4202         }
4203         return b;
4204     };
4205
4206     // build as dom
4207     /** @ignore */
4208     var createDom = function(o, parentNode){
4209          
4210         // defininition craeted..
4211         var ns = false;
4212         if (o.ns && o.ns != 'html') {
4213                
4214             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4215                 xmlns[o.ns] = o.xmlns;
4216                 ns = o.xmlns;
4217             }
4218             if (typeof(xmlns[o.ns]) == 'undefined') {
4219                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4220             }
4221             ns = xmlns[o.ns];
4222         }
4223         
4224         
4225         if (typeof(o) == 'string') {
4226             return parentNode.appendChild(document.createTextNode(o));
4227         }
4228         o.tag = o.tag || div;
4229         if (o.ns && Roo.isIE) {
4230             ns = false;
4231             o.tag = o.ns + ':' + o.tag;
4232             
4233         }
4234         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4235         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4236         for(var attr in o){
4237             
4238             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4239                     attr == "style" || typeof o[attr] == "function") { continue; }
4240                     
4241             if(attr=="cls" && Roo.isIE){
4242                 el.className = o["cls"];
4243             }else{
4244                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4245                 else { 
4246                     el[attr] = o[attr];
4247                 }
4248             }
4249         }
4250         Roo.DomHelper.applyStyles(el, o.style);
4251         var cn = o.children || o.cn;
4252         if(cn){
4253             //http://bugs.kde.org/show_bug.cgi?id=71506
4254              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4255                 for(var i = 0, len = cn.length; i < len; i++) {
4256                     createDom(cn[i], el);
4257                 }
4258             }else{
4259                 createDom(cn, el);
4260             }
4261         }
4262         if(o.html){
4263             el.innerHTML = o.html;
4264         }
4265         if(parentNode){
4266            parentNode.appendChild(el);
4267         }
4268         return el;
4269     };
4270
4271     var ieTable = function(depth, s, h, e){
4272         tempTableEl.innerHTML = [s, h, e].join('');
4273         var i = -1, el = tempTableEl;
4274         while(++i < depth){
4275             el = el.firstChild;
4276         }
4277         return el;
4278     };
4279
4280     // kill repeat to save bytes
4281     var ts = '<table>',
4282         te = '</table>',
4283         tbs = ts+'<tbody>',
4284         tbe = '</tbody>'+te,
4285         trs = tbs + '<tr>',
4286         tre = '</tr>'+tbe;
4287
4288     /**
4289      * @ignore
4290      * Nasty code for IE's broken table implementation
4291      */
4292     var insertIntoTable = function(tag, where, el, html){
4293         if(!tempTableEl){
4294             tempTableEl = document.createElement('div');
4295         }
4296         var node;
4297         var before = null;
4298         if(tag == 'td'){
4299             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4300                 return;
4301             }
4302             if(where == 'beforebegin'){
4303                 before = el;
4304                 el = el.parentNode;
4305             } else{
4306                 before = el.nextSibling;
4307                 el = el.parentNode;
4308             }
4309             node = ieTable(4, trs, html, tre);
4310         }
4311         else if(tag == 'tr'){
4312             if(where == 'beforebegin'){
4313                 before = el;
4314                 el = el.parentNode;
4315                 node = ieTable(3, tbs, html, tbe);
4316             } else if(where == 'afterend'){
4317                 before = el.nextSibling;
4318                 el = el.parentNode;
4319                 node = ieTable(3, tbs, html, tbe);
4320             } else{ // INTO a TR
4321                 if(where == 'afterbegin'){
4322                     before = el.firstChild;
4323                 }
4324                 node = ieTable(4, trs, html, tre);
4325             }
4326         } else if(tag == 'tbody'){
4327             if(where == 'beforebegin'){
4328                 before = el;
4329                 el = el.parentNode;
4330                 node = ieTable(2, ts, html, te);
4331             } else if(where == 'afterend'){
4332                 before = el.nextSibling;
4333                 el = el.parentNode;
4334                 node = ieTable(2, ts, html, te);
4335             } else{
4336                 if(where == 'afterbegin'){
4337                     before = el.firstChild;
4338                 }
4339                 node = ieTable(3, tbs, html, tbe);
4340             }
4341         } else{ // TABLE
4342             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4343                 return;
4344             }
4345             if(where == 'afterbegin'){
4346                 before = el.firstChild;
4347             }
4348             node = ieTable(2, ts, html, te);
4349         }
4350         el.insertBefore(node, before);
4351         return node;
4352     };
4353
4354     return {
4355     /** True to force the use of DOM instead of html fragments @type Boolean */
4356     useDom : false,
4357
4358     /**
4359      * Returns the markup for the passed Element(s) config
4360      * @param {Object} o The Dom object spec (and children)
4361      * @return {String}
4362      */
4363     markup : function(o){
4364         return createHtml(o);
4365     },
4366
4367     /**
4368      * Applies a style specification to an element
4369      * @param {String/HTMLElement} el The element to apply styles to
4370      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4371      * a function which returns such a specification.
4372      */
4373     applyStyles : function(el, styles){
4374         if(styles){
4375            el = Roo.fly(el);
4376            if(typeof styles == "string"){
4377                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4378                var matches;
4379                while ((matches = re.exec(styles)) != null){
4380                    el.setStyle(matches[1], matches[2]);
4381                }
4382            }else if (typeof styles == "object"){
4383                for (var style in styles){
4384                   el.setStyle(style, styles[style]);
4385                }
4386            }else if (typeof styles == "function"){
4387                 Roo.DomHelper.applyStyles(el, styles.call());
4388            }
4389         }
4390     },
4391
4392     /**
4393      * Inserts an HTML fragment into the Dom
4394      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4395      * @param {HTMLElement} el The context element
4396      * @param {String} html The HTML fragmenet
4397      * @return {HTMLElement} The new node
4398      */
4399     insertHtml : function(where, el, html){
4400         where = where.toLowerCase();
4401         if(el.insertAdjacentHTML){
4402             if(tableRe.test(el.tagName)){
4403                 var rs;
4404                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4405                     return rs;
4406                 }
4407             }
4408             switch(where){
4409                 case "beforebegin":
4410                     el.insertAdjacentHTML('BeforeBegin', html);
4411                     return el.previousSibling;
4412                 case "afterbegin":
4413                     el.insertAdjacentHTML('AfterBegin', html);
4414                     return el.firstChild;
4415                 case "beforeend":
4416                     el.insertAdjacentHTML('BeforeEnd', html);
4417                     return el.lastChild;
4418                 case "afterend":
4419                     el.insertAdjacentHTML('AfterEnd', html);
4420                     return el.nextSibling;
4421             }
4422             throw 'Illegal insertion point -> "' + where + '"';
4423         }
4424         var range = el.ownerDocument.createRange();
4425         var frag;
4426         switch(where){
4427              case "beforebegin":
4428                 range.setStartBefore(el);
4429                 frag = range.createContextualFragment(html);
4430                 el.parentNode.insertBefore(frag, el);
4431                 return el.previousSibling;
4432              case "afterbegin":
4433                 if(el.firstChild){
4434                     range.setStartBefore(el.firstChild);
4435                     frag = range.createContextualFragment(html);
4436                     el.insertBefore(frag, el.firstChild);
4437                     return el.firstChild;
4438                 }else{
4439                     el.innerHTML = html;
4440                     return el.firstChild;
4441                 }
4442             case "beforeend":
4443                 if(el.lastChild){
4444                     range.setStartAfter(el.lastChild);
4445                     frag = range.createContextualFragment(html);
4446                     el.appendChild(frag);
4447                     return el.lastChild;
4448                 }else{
4449                     el.innerHTML = html;
4450                     return el.lastChild;
4451                 }
4452             case "afterend":
4453                 range.setStartAfter(el);
4454                 frag = range.createContextualFragment(html);
4455                 el.parentNode.insertBefore(frag, el.nextSibling);
4456                 return el.nextSibling;
4457             }
4458             throw 'Illegal insertion point -> "' + where + '"';
4459     },
4460
4461     /**
4462      * Creates new Dom element(s) and inserts them before el
4463      * @param {String/HTMLElement/Element} el The context element
4464      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4465      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4466      * @return {HTMLElement/Roo.Element} The new node
4467      */
4468     insertBefore : function(el, o, returnElement){
4469         return this.doInsert(el, o, returnElement, "beforeBegin");
4470     },
4471
4472     /**
4473      * Creates new Dom element(s) and inserts them after el
4474      * @param {String/HTMLElement/Element} el The context element
4475      * @param {Object} o The Dom object spec (and children)
4476      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4477      * @return {HTMLElement/Roo.Element} The new node
4478      */
4479     insertAfter : function(el, o, returnElement){
4480         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4481     },
4482
4483     /**
4484      * Creates new Dom element(s) and inserts them as the first child of el
4485      * @param {String/HTMLElement/Element} el The context element
4486      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4487      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4488      * @return {HTMLElement/Roo.Element} The new node
4489      */
4490     insertFirst : function(el, o, returnElement){
4491         return this.doInsert(el, o, returnElement, "afterBegin");
4492     },
4493
4494     // private
4495     doInsert : function(el, o, returnElement, pos, sibling){
4496         el = Roo.getDom(el);
4497         var newNode;
4498         if(this.useDom || o.ns){
4499             newNode = createDom(o, null);
4500             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4501         }else{
4502             var html = createHtml(o);
4503             newNode = this.insertHtml(pos, el, html);
4504         }
4505         return returnElement ? Roo.get(newNode, true) : newNode;
4506     },
4507
4508     /**
4509      * Creates new Dom element(s) and appends them to el
4510      * @param {String/HTMLElement/Element} el The context element
4511      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4512      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4513      * @return {HTMLElement/Roo.Element} The new node
4514      */
4515     append : function(el, o, returnElement){
4516         el = Roo.getDom(el);
4517         var newNode;
4518         if(this.useDom || o.ns){
4519             newNode = createDom(o, null);
4520             el.appendChild(newNode);
4521         }else{
4522             var html = createHtml(o);
4523             newNode = this.insertHtml("beforeEnd", el, html);
4524         }
4525         return returnElement ? Roo.get(newNode, true) : newNode;
4526     },
4527
4528     /**
4529      * Creates new Dom element(s) and overwrites the contents of el with them
4530      * @param {String/HTMLElement/Element} el The context element
4531      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4532      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4533      * @return {HTMLElement/Roo.Element} The new node
4534      */
4535     overwrite : function(el, o, returnElement){
4536         el = Roo.getDom(el);
4537         if (o.ns) {
4538           
4539             while (el.childNodes.length) {
4540                 el.removeChild(el.firstChild);
4541             }
4542             createDom(o, el);
4543         } else {
4544             el.innerHTML = createHtml(o);   
4545         }
4546         
4547         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4548     },
4549
4550     /**
4551      * Creates a new Roo.DomHelper.Template from the Dom object spec
4552      * @param {Object} o The Dom object spec (and children)
4553      * @return {Roo.DomHelper.Template} The new template
4554      */
4555     createTemplate : function(o){
4556         var html = createHtml(o);
4557         return new Roo.Template(html);
4558     }
4559     };
4560 }();
4561 /*
4562  * Based on:
4563  * Ext JS Library 1.1.1
4564  * Copyright(c) 2006-2007, Ext JS, LLC.
4565  *
4566  * Originally Released Under LGPL - original licence link has changed is not relivant.
4567  *
4568  * Fork - LGPL
4569  * <script type="text/javascript">
4570  */
4571  
4572 /**
4573 * @class Roo.Template
4574 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4575 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4576 * Usage:
4577 <pre><code>
4578 var t = new Roo.Template({
4579     html :  '&lt;div name="{id}"&gt;' + 
4580         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4581         '&lt;/div&gt;',
4582     myformat: function (value, allValues) {
4583         return 'XX' + value;
4584     }
4585 });
4586 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4587 </code></pre>
4588 * For more information see this blog post with examples:
4589 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4590      - Create Elements using DOM, HTML fragments and Templates</a>. 
4591 * @constructor
4592 * @param {Object} cfg - Configuration object.
4593 */
4594 Roo.Template = function(cfg){
4595     // BC!
4596     if(cfg instanceof Array){
4597         cfg = cfg.join("");
4598     }else if(arguments.length > 1){
4599         cfg = Array.prototype.join.call(arguments, "");
4600     }
4601     
4602     
4603     if (typeof(cfg) == 'object') {
4604         Roo.apply(this,cfg)
4605     } else {
4606         // bc
4607         this.html = cfg;
4608     }
4609     if (this.url) {
4610         this.load();
4611     }
4612     
4613 };
4614 Roo.Template.prototype = {
4615     
4616     /**
4617      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4618      *                    it should be fixed so that template is observable...
4619      */
4620     url : false,
4621     /**
4622      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4623      */
4624     html : '',
4625     /**
4626      * Returns an HTML fragment of this template with the specified values applied.
4627      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4628      * @return {String} The HTML fragment
4629      */
4630     applyTemplate : function(values){
4631         try {
4632            
4633             if(this.compiled){
4634                 return this.compiled(values);
4635             }
4636             var useF = this.disableFormats !== true;
4637             var fm = Roo.util.Format, tpl = this;
4638             var fn = function(m, name, format, args){
4639                 if(format && useF){
4640                     if(format.substr(0, 5) == "this."){
4641                         return tpl.call(format.substr(5), values[name], values);
4642                     }else{
4643                         if(args){
4644                             // quoted values are required for strings in compiled templates, 
4645                             // but for non compiled we need to strip them
4646                             // quoted reversed for jsmin
4647                             var re = /^\s*['"](.*)["']\s*$/;
4648                             args = args.split(',');
4649                             for(var i = 0, len = args.length; i < len; i++){
4650                                 args[i] = args[i].replace(re, "$1");
4651                             }
4652                             args = [values[name]].concat(args);
4653                         }else{
4654                             args = [values[name]];
4655                         }
4656                         return fm[format].apply(fm, args);
4657                     }
4658                 }else{
4659                     return values[name] !== undefined ? values[name] : "";
4660                 }
4661             };
4662             return this.html.replace(this.re, fn);
4663         } catch (e) {
4664             Roo.log(e);
4665             throw e;
4666         }
4667          
4668     },
4669     
4670     loading : false,
4671       
4672     load : function ()
4673     {
4674          
4675         if (this.loading) {
4676             return;
4677         }
4678         var _t = this;
4679         
4680         this.loading = true;
4681         this.compiled = false;
4682         
4683         var cx = new Roo.data.Connection();
4684         cx.request({
4685             url : this.url,
4686             method : 'GET',
4687             success : function (response) {
4688                 _t.loading = false;
4689                 _t.html = response.responseText;
4690                 _t.url = false;
4691                 _t.compile();
4692              },
4693             failure : function(response) {
4694                 Roo.log("Template failed to load from " + _t.url);
4695                 _t.loading = false;
4696             }
4697         });
4698     },
4699
4700     /**
4701      * Sets the HTML used as the template and optionally compiles it.
4702      * @param {String} html
4703      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4704      * @return {Roo.Template} this
4705      */
4706     set : function(html, compile){
4707         this.html = html;
4708         this.compiled = null;
4709         if(compile){
4710             this.compile();
4711         }
4712         return this;
4713     },
4714     
4715     /**
4716      * True to disable format functions (defaults to false)
4717      * @type Boolean
4718      */
4719     disableFormats : false,
4720     
4721     /**
4722     * The regular expression used to match template variables 
4723     * @type RegExp
4724     * @property 
4725     */
4726     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4727     
4728     /**
4729      * Compiles the template into an internal function, eliminating the RegEx overhead.
4730      * @return {Roo.Template} this
4731      */
4732     compile : function(){
4733         var fm = Roo.util.Format;
4734         var useF = this.disableFormats !== true;
4735         var sep = Roo.isGecko ? "+" : ",";
4736         var fn = function(m, name, format, args){
4737             if(format && useF){
4738                 args = args ? ',' + args : "";
4739                 if(format.substr(0, 5) != "this."){
4740                     format = "fm." + format + '(';
4741                 }else{
4742                     format = 'this.call("'+ format.substr(5) + '", ';
4743                     args = ", values";
4744                 }
4745             }else{
4746                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4747             }
4748             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4749         };
4750         var body;
4751         // branched to use + in gecko and [].join() in others
4752         if(Roo.isGecko){
4753             body = "this.compiled = function(values){ return '" +
4754                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4755                     "';};";
4756         }else{
4757             body = ["this.compiled = function(values){ return ['"];
4758             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4759             body.push("'].join('');};");
4760             body = body.join('');
4761         }
4762         /**
4763          * eval:var:values
4764          * eval:var:fm
4765          */
4766         eval(body);
4767         return this;
4768     },
4769     
4770     // private function used to call members
4771     call : function(fnName, value, allValues){
4772         return this[fnName](value, allValues);
4773     },
4774     
4775     /**
4776      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4777      * @param {String/HTMLElement/Roo.Element} el The context element
4778      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4779      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4780      * @return {HTMLElement/Roo.Element} The new node or Element
4781      */
4782     insertFirst: function(el, values, returnElement){
4783         return this.doInsert('afterBegin', el, values, returnElement);
4784     },
4785
4786     /**
4787      * Applies the supplied values to the template and inserts the new node(s) before el.
4788      * @param {String/HTMLElement/Roo.Element} el The context element
4789      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4790      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4791      * @return {HTMLElement/Roo.Element} The new node or Element
4792      */
4793     insertBefore: function(el, values, returnElement){
4794         return this.doInsert('beforeBegin', el, values, returnElement);
4795     },
4796
4797     /**
4798      * Applies the supplied values to the template and inserts the new node(s) after el.
4799      * @param {String/HTMLElement/Roo.Element} el The context element
4800      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4801      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4802      * @return {HTMLElement/Roo.Element} The new node or Element
4803      */
4804     insertAfter : function(el, values, returnElement){
4805         return this.doInsert('afterEnd', el, values, returnElement);
4806     },
4807     
4808     /**
4809      * Applies the supplied values to the template and appends the new node(s) to el.
4810      * @param {String/HTMLElement/Roo.Element} el The context element
4811      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4812      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4813      * @return {HTMLElement/Roo.Element} The new node or Element
4814      */
4815     append : function(el, values, returnElement){
4816         return this.doInsert('beforeEnd', el, values, returnElement);
4817     },
4818
4819     doInsert : function(where, el, values, returnEl){
4820         el = Roo.getDom(el);
4821         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4822         return returnEl ? Roo.get(newNode, true) : newNode;
4823     },
4824
4825     /**
4826      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4827      * @param {String/HTMLElement/Roo.Element} el The context element
4828      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4829      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4830      * @return {HTMLElement/Roo.Element} The new node or Element
4831      */
4832     overwrite : function(el, values, returnElement){
4833         el = Roo.getDom(el);
4834         el.innerHTML = this.applyTemplate(values);
4835         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4836     }
4837 };
4838 /**
4839  * Alias for {@link #applyTemplate}
4840  * @method
4841  */
4842 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4843
4844 // backwards compat
4845 Roo.DomHelper.Template = Roo.Template;
4846
4847 /**
4848  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4849  * @param {String/HTMLElement} el A DOM element or its id
4850  * @returns {Roo.Template} The created template
4851  * @static
4852  */
4853 Roo.Template.from = function(el){
4854     el = Roo.getDom(el);
4855     return new Roo.Template(el.value || el.innerHTML);
4856 };/*
4857  * Based on:
4858  * Ext JS Library 1.1.1
4859  * Copyright(c) 2006-2007, Ext JS, LLC.
4860  *
4861  * Originally Released Under LGPL - original licence link has changed is not relivant.
4862  *
4863  * Fork - LGPL
4864  * <script type="text/javascript">
4865  */
4866  
4867
4868 /*
4869  * This is code is also distributed under MIT license for use
4870  * with jQuery and prototype JavaScript libraries.
4871  */
4872 /**
4873  * @class Roo.DomQuery
4874 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4875 <p>
4876 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4877
4878 <p>
4879 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4880 </p>
4881 <h4>Element Selectors:</h4>
4882 <ul class="list">
4883     <li> <b>*</b> any element</li>
4884     <li> <b>E</b> an element with the tag E</li>
4885     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4886     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4887     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4888     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4889 </ul>
4890 <h4>Attribute Selectors:</h4>
4891 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4892 <ul class="list">
4893     <li> <b>E[foo]</b> has an attribute "foo"</li>
4894     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4895     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4896     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4897     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4898     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4899     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4900 </ul>
4901 <h4>Pseudo Classes:</h4>
4902 <ul class="list">
4903     <li> <b>E:first-child</b> E is the first child of its parent</li>
4904     <li> <b>E:last-child</b> E is the last child of its parent</li>
4905     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4906     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4907     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4908     <li> <b>E:only-child</b> E is the only child of its parent</li>
4909     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4910     <li> <b>E:first</b> the first E in the resultset</li>
4911     <li> <b>E:last</b> the last E in the resultset</li>
4912     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4913     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4914     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4915     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4916     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4917     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4918     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4919     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4920     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4921 </ul>
4922 <h4>CSS Value Selectors:</h4>
4923 <ul class="list">
4924     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4925     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4926     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4927     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4928     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4929     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4930 </ul>
4931  * @singleton
4932  */
4933 Roo.DomQuery = function(){
4934     var cache = {}, simpleCache = {}, valueCache = {};
4935     var nonSpace = /\S/;
4936     var trimRe = /^\s+|\s+$/g;
4937     var tplRe = /\{(\d+)\}/g;
4938     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4939     var tagTokenRe = /^(#)?([\w-\*]+)/;
4940     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4941
4942     function child(p, index){
4943         var i = 0;
4944         var n = p.firstChild;
4945         while(n){
4946             if(n.nodeType == 1){
4947                if(++i == index){
4948                    return n;
4949                }
4950             }
4951             n = n.nextSibling;
4952         }
4953         return null;
4954     };
4955
4956     function next(n){
4957         while((n = n.nextSibling) && n.nodeType != 1);
4958         return n;
4959     };
4960
4961     function prev(n){
4962         while((n = n.previousSibling) && n.nodeType != 1);
4963         return n;
4964     };
4965
4966     function children(d){
4967         var n = d.firstChild, ni = -1;
4968             while(n){
4969                 var nx = n.nextSibling;
4970                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4971                     d.removeChild(n);
4972                 }else{
4973                     n.nodeIndex = ++ni;
4974                 }
4975                 n = nx;
4976             }
4977             return this;
4978         };
4979
4980     function byClassName(c, a, v){
4981         if(!v){
4982             return c;
4983         }
4984         var r = [], ri = -1, cn;
4985         for(var i = 0, ci; ci = c[i]; i++){
4986             if((' '+ci.className+' ').indexOf(v) != -1){
4987                 r[++ri] = ci;
4988             }
4989         }
4990         return r;
4991     };
4992
4993     function attrValue(n, attr){
4994         if(!n.tagName && typeof n.length != "undefined"){
4995             n = n[0];
4996         }
4997         if(!n){
4998             return null;
4999         }
5000         if(attr == "for"){
5001             return n.htmlFor;
5002         }
5003         if(attr == "class" || attr == "className"){
5004             return n.className;
5005         }
5006         return n.getAttribute(attr) || n[attr];
5007
5008     };
5009
5010     function getNodes(ns, mode, tagName){
5011         var result = [], ri = -1, cs;
5012         if(!ns){
5013             return result;
5014         }
5015         tagName = tagName || "*";
5016         if(typeof ns.getElementsByTagName != "undefined"){
5017             ns = [ns];
5018         }
5019         if(!mode){
5020             for(var i = 0, ni; ni = ns[i]; i++){
5021                 cs = ni.getElementsByTagName(tagName);
5022                 for(var j = 0, ci; ci = cs[j]; j++){
5023                     result[++ri] = ci;
5024                 }
5025             }
5026         }else if(mode == "/" || mode == ">"){
5027             var utag = tagName.toUpperCase();
5028             for(var i = 0, ni, cn; ni = ns[i]; i++){
5029                 cn = ni.children || ni.childNodes;
5030                 for(var j = 0, cj; cj = cn[j]; j++){
5031                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5032                         result[++ri] = cj;
5033                     }
5034                 }
5035             }
5036         }else if(mode == "+"){
5037             var utag = tagName.toUpperCase();
5038             for(var i = 0, n; n = ns[i]; i++){
5039                 while((n = n.nextSibling) && n.nodeType != 1);
5040                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5041                     result[++ri] = n;
5042                 }
5043             }
5044         }else if(mode == "~"){
5045             for(var i = 0, n; n = ns[i]; i++){
5046                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5047                 if(n){
5048                     result[++ri] = n;
5049                 }
5050             }
5051         }
5052         return result;
5053     };
5054
5055     function concat(a, b){
5056         if(b.slice){
5057             return a.concat(b);
5058         }
5059         for(var i = 0, l = b.length; i < l; i++){
5060             a[a.length] = b[i];
5061         }
5062         return a;
5063     }
5064
5065     function byTag(cs, tagName){
5066         if(cs.tagName || cs == document){
5067             cs = [cs];
5068         }
5069         if(!tagName){
5070             return cs;
5071         }
5072         var r = [], ri = -1;
5073         tagName = tagName.toLowerCase();
5074         for(var i = 0, ci; ci = cs[i]; i++){
5075             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5076                 r[++ri] = ci;
5077             }
5078         }
5079         return r;
5080     };
5081
5082     function byId(cs, attr, id){
5083         if(cs.tagName || cs == document){
5084             cs = [cs];
5085         }
5086         if(!id){
5087             return cs;
5088         }
5089         var r = [], ri = -1;
5090         for(var i = 0,ci; ci = cs[i]; i++){
5091             if(ci && ci.id == id){
5092                 r[++ri] = ci;
5093                 return r;
5094             }
5095         }
5096         return r;
5097     };
5098
5099     function byAttribute(cs, attr, value, op, custom){
5100         var r = [], ri = -1, st = custom=="{";
5101         var f = Roo.DomQuery.operators[op];
5102         for(var i = 0, ci; ci = cs[i]; i++){
5103             var a;
5104             if(st){
5105                 a = Roo.DomQuery.getStyle(ci, attr);
5106             }
5107             else if(attr == "class" || attr == "className"){
5108                 a = ci.className;
5109             }else if(attr == "for"){
5110                 a = ci.htmlFor;
5111             }else if(attr == "href"){
5112                 a = ci.getAttribute("href", 2);
5113             }else{
5114                 a = ci.getAttribute(attr);
5115             }
5116             if((f && f(a, value)) || (!f && a)){
5117                 r[++ri] = ci;
5118             }
5119         }
5120         return r;
5121     };
5122
5123     function byPseudo(cs, name, value){
5124         return Roo.DomQuery.pseudos[name](cs, value);
5125     };
5126
5127     // This is for IE MSXML which does not support expandos.
5128     // IE runs the same speed using setAttribute, however FF slows way down
5129     // and Safari completely fails so they need to continue to use expandos.
5130     var isIE = window.ActiveXObject ? true : false;
5131
5132     // this eval is stop the compressor from
5133     // renaming the variable to something shorter
5134     
5135     /** eval:var:batch */
5136     var batch = 30803; 
5137
5138     var key = 30803;
5139
5140     function nodupIEXml(cs){
5141         var d = ++key;
5142         cs[0].setAttribute("_nodup", d);
5143         var r = [cs[0]];
5144         for(var i = 1, len = cs.length; i < len; i++){
5145             var c = cs[i];
5146             if(!c.getAttribute("_nodup") != d){
5147                 c.setAttribute("_nodup", d);
5148                 r[r.length] = c;
5149             }
5150         }
5151         for(var i = 0, len = cs.length; i < len; i++){
5152             cs[i].removeAttribute("_nodup");
5153         }
5154         return r;
5155     }
5156
5157     function nodup(cs){
5158         if(!cs){
5159             return [];
5160         }
5161         var len = cs.length, c, i, r = cs, cj, ri = -1;
5162         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5163             return cs;
5164         }
5165         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5166             return nodupIEXml(cs);
5167         }
5168         var d = ++key;
5169         cs[0]._nodup = d;
5170         for(i = 1; c = cs[i]; i++){
5171             if(c._nodup != d){
5172                 c._nodup = d;
5173             }else{
5174                 r = [];
5175                 for(var j = 0; j < i; j++){
5176                     r[++ri] = cs[j];
5177                 }
5178                 for(j = i+1; cj = cs[j]; j++){
5179                     if(cj._nodup != d){
5180                         cj._nodup = d;
5181                         r[++ri] = cj;
5182                     }
5183                 }
5184                 return r;
5185             }
5186         }
5187         return r;
5188     }
5189
5190     function quickDiffIEXml(c1, c2){
5191         var d = ++key;
5192         for(var i = 0, len = c1.length; i < len; i++){
5193             c1[i].setAttribute("_qdiff", d);
5194         }
5195         var r = [];
5196         for(var i = 0, len = c2.length; i < len; i++){
5197             if(c2[i].getAttribute("_qdiff") != d){
5198                 r[r.length] = c2[i];
5199             }
5200         }
5201         for(var i = 0, len = c1.length; i < len; i++){
5202            c1[i].removeAttribute("_qdiff");
5203         }
5204         return r;
5205     }
5206
5207     function quickDiff(c1, c2){
5208         var len1 = c1.length;
5209         if(!len1){
5210             return c2;
5211         }
5212         if(isIE && c1[0].selectSingleNode){
5213             return quickDiffIEXml(c1, c2);
5214         }
5215         var d = ++key;
5216         for(var i = 0; i < len1; i++){
5217             c1[i]._qdiff = d;
5218         }
5219         var r = [];
5220         for(var i = 0, len = c2.length; i < len; i++){
5221             if(c2[i]._qdiff != d){
5222                 r[r.length] = c2[i];
5223             }
5224         }
5225         return r;
5226     }
5227
5228     function quickId(ns, mode, root, id){
5229         if(ns == root){
5230            var d = root.ownerDocument || root;
5231            return d.getElementById(id);
5232         }
5233         ns = getNodes(ns, mode, "*");
5234         return byId(ns, null, id);
5235     }
5236
5237     return {
5238         getStyle : function(el, name){
5239             return Roo.fly(el).getStyle(name);
5240         },
5241         /**
5242          * Compiles a selector/xpath query into a reusable function. The returned function
5243          * takes one parameter "root" (optional), which is the context node from where the query should start.
5244          * @param {String} selector The selector/xpath query
5245          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5246          * @return {Function}
5247          */
5248         compile : function(path, type){
5249             type = type || "select";
5250             
5251             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5252             var q = path, mode, lq;
5253             var tk = Roo.DomQuery.matchers;
5254             var tklen = tk.length;
5255             var mm;
5256
5257             // accept leading mode switch
5258             var lmode = q.match(modeRe);
5259             if(lmode && lmode[1]){
5260                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5261                 q = q.replace(lmode[1], "");
5262             }
5263             // strip leading slashes
5264             while(path.substr(0, 1)=="/"){
5265                 path = path.substr(1);
5266             }
5267
5268             while(q && lq != q){
5269                 lq = q;
5270                 var tm = q.match(tagTokenRe);
5271                 if(type == "select"){
5272                     if(tm){
5273                         if(tm[1] == "#"){
5274                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5275                         }else{
5276                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5277                         }
5278                         q = q.replace(tm[0], "");
5279                     }else if(q.substr(0, 1) != '@'){
5280                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5281                     }
5282                 }else{
5283                     if(tm){
5284                         if(tm[1] == "#"){
5285                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5286                         }else{
5287                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5288                         }
5289                         q = q.replace(tm[0], "");
5290                     }
5291                 }
5292                 while(!(mm = q.match(modeRe))){
5293                     var matched = false;
5294                     for(var j = 0; j < tklen; j++){
5295                         var t = tk[j];
5296                         var m = q.match(t.re);
5297                         if(m){
5298                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5299                                                     return m[i];
5300                                                 });
5301                             q = q.replace(m[0], "");
5302                             matched = true;
5303                             break;
5304                         }
5305                     }
5306                     // prevent infinite loop on bad selector
5307                     if(!matched){
5308                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5309                     }
5310                 }
5311                 if(mm[1]){
5312                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5313                     q = q.replace(mm[1], "");
5314                 }
5315             }
5316             fn[fn.length] = "return nodup(n);\n}";
5317             
5318              /** 
5319               * list of variables that need from compression as they are used by eval.
5320              *  eval:var:batch 
5321              *  eval:var:nodup
5322              *  eval:var:byTag
5323              *  eval:var:ById
5324              *  eval:var:getNodes
5325              *  eval:var:quickId
5326              *  eval:var:mode
5327              *  eval:var:root
5328              *  eval:var:n
5329              *  eval:var:byClassName
5330              *  eval:var:byPseudo
5331              *  eval:var:byAttribute
5332              *  eval:var:attrValue
5333              * 
5334              **/ 
5335             eval(fn.join(""));
5336             return f;
5337         },
5338
5339         /**
5340          * Selects a group of elements.
5341          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5342          * @param {Node} root (optional) The start of the query (defaults to document).
5343          * @return {Array}
5344          */
5345         select : function(path, root, type){
5346             if(!root || root == document){
5347                 root = document;
5348             }
5349             if(typeof root == "string"){
5350                 root = document.getElementById(root);
5351             }
5352             var paths = path.split(",");
5353             var results = [];
5354             for(var i = 0, len = paths.length; i < len; i++){
5355                 var p = paths[i].replace(trimRe, "");
5356                 if(!cache[p]){
5357                     cache[p] = Roo.DomQuery.compile(p);
5358                     if(!cache[p]){
5359                         throw p + " is not a valid selector";
5360                     }
5361                 }
5362                 var result = cache[p](root);
5363                 if(result && result != document){
5364                     results = results.concat(result);
5365                 }
5366             }
5367             if(paths.length > 1){
5368                 return nodup(results);
5369             }
5370             return results;
5371         },
5372
5373         /**
5374          * Selects a single element.
5375          * @param {String} selector The selector/xpath query
5376          * @param {Node} root (optional) The start of the query (defaults to document).
5377          * @return {Element}
5378          */
5379         selectNode : function(path, root){
5380             return Roo.DomQuery.select(path, root)[0];
5381         },
5382
5383         /**
5384          * Selects the value of a node, optionally replacing null with the defaultValue.
5385          * @param {String} selector The selector/xpath query
5386          * @param {Node} root (optional) The start of the query (defaults to document).
5387          * @param {String} defaultValue
5388          */
5389         selectValue : function(path, root, defaultValue){
5390             path = path.replace(trimRe, "");
5391             if(!valueCache[path]){
5392                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5393             }
5394             var n = valueCache[path](root);
5395             n = n[0] ? n[0] : n;
5396             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5397             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5398         },
5399
5400         /**
5401          * Selects the value of a node, parsing integers and floats.
5402          * @param {String} selector The selector/xpath query
5403          * @param {Node} root (optional) The start of the query (defaults to document).
5404          * @param {Number} defaultValue
5405          * @return {Number}
5406          */
5407         selectNumber : function(path, root, defaultValue){
5408             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5409             return parseFloat(v);
5410         },
5411
5412         /**
5413          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5414          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5415          * @param {String} selector The simple selector to test
5416          * @return {Boolean}
5417          */
5418         is : function(el, ss){
5419             if(typeof el == "string"){
5420                 el = document.getElementById(el);
5421             }
5422             var isArray = (el instanceof Array);
5423             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5424             return isArray ? (result.length == el.length) : (result.length > 0);
5425         },
5426
5427         /**
5428          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5429          * @param {Array} el An array of elements to filter
5430          * @param {String} selector The simple selector to test
5431          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5432          * the selector instead of the ones that match
5433          * @return {Array}
5434          */
5435         filter : function(els, ss, nonMatches){
5436             ss = ss.replace(trimRe, "");
5437             if(!simpleCache[ss]){
5438                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5439             }
5440             var result = simpleCache[ss](els);
5441             return nonMatches ? quickDiff(result, els) : result;
5442         },
5443
5444         /**
5445          * Collection of matching regular expressions and code snippets.
5446          */
5447         matchers : [{
5448                 re: /^\.([\w-]+)/,
5449                 select: 'n = byClassName(n, null, " {1} ");'
5450             }, {
5451                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5452                 select: 'n = byPseudo(n, "{1}", "{2}");'
5453             },{
5454                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5455                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5456             }, {
5457                 re: /^#([\w-]+)/,
5458                 select: 'n = byId(n, null, "{1}");'
5459             },{
5460                 re: /^@([\w-]+)/,
5461                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5462             }
5463         ],
5464
5465         /**
5466          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5467          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5468          */
5469         operators : {
5470             "=" : function(a, v){
5471                 return a == v;
5472             },
5473             "!=" : function(a, v){
5474                 return a != v;
5475             },
5476             "^=" : function(a, v){
5477                 return a && a.substr(0, v.length) == v;
5478             },
5479             "$=" : function(a, v){
5480                 return a && a.substr(a.length-v.length) == v;
5481             },
5482             "*=" : function(a, v){
5483                 return a && a.indexOf(v) !== -1;
5484             },
5485             "%=" : function(a, v){
5486                 return (a % v) == 0;
5487             },
5488             "|=" : function(a, v){
5489                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5490             },
5491             "~=" : function(a, v){
5492                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5493             }
5494         },
5495
5496         /**
5497          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5498          * and the argument (if any) supplied in the selector.
5499          */
5500         pseudos : {
5501             "first-child" : function(c){
5502                 var r = [], ri = -1, n;
5503                 for(var i = 0, ci; ci = n = c[i]; i++){
5504                     while((n = n.previousSibling) && n.nodeType != 1);
5505                     if(!n){
5506                         r[++ri] = ci;
5507                     }
5508                 }
5509                 return r;
5510             },
5511
5512             "last-child" : function(c){
5513                 var r = [], ri = -1, n;
5514                 for(var i = 0, ci; ci = n = c[i]; i++){
5515                     while((n = n.nextSibling) && n.nodeType != 1);
5516                     if(!n){
5517                         r[++ri] = ci;
5518                     }
5519                 }
5520                 return r;
5521             },
5522
5523             "nth-child" : function(c, a) {
5524                 var r = [], ri = -1;
5525                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5526                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5527                 for(var i = 0, n; n = c[i]; i++){
5528                     var pn = n.parentNode;
5529                     if (batch != pn._batch) {
5530                         var j = 0;
5531                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5532                             if(cn.nodeType == 1){
5533                                cn.nodeIndex = ++j;
5534                             }
5535                         }
5536                         pn._batch = batch;
5537                     }
5538                     if (f == 1) {
5539                         if (l == 0 || n.nodeIndex == l){
5540                             r[++ri] = n;
5541                         }
5542                     } else if ((n.nodeIndex + l) % f == 0){
5543                         r[++ri] = n;
5544                     }
5545                 }
5546
5547                 return r;
5548             },
5549
5550             "only-child" : function(c){
5551                 var r = [], ri = -1;;
5552                 for(var i = 0, ci; ci = c[i]; i++){
5553                     if(!prev(ci) && !next(ci)){
5554                         r[++ri] = ci;
5555                     }
5556                 }
5557                 return r;
5558             },
5559
5560             "empty" : function(c){
5561                 var r = [], ri = -1;
5562                 for(var i = 0, ci; ci = c[i]; i++){
5563                     var cns = ci.childNodes, j = 0, cn, empty = true;
5564                     while(cn = cns[j]){
5565                         ++j;
5566                         if(cn.nodeType == 1 || cn.nodeType == 3){
5567                             empty = false;
5568                             break;
5569                         }
5570                     }
5571                     if(empty){
5572                         r[++ri] = ci;
5573                     }
5574                 }
5575                 return r;
5576             },
5577
5578             "contains" : function(c, v){
5579                 var r = [], ri = -1;
5580                 for(var i = 0, ci; ci = c[i]; i++){
5581                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5582                         r[++ri] = ci;
5583                     }
5584                 }
5585                 return r;
5586             },
5587
5588             "nodeValue" : function(c, v){
5589                 var r = [], ri = -1;
5590                 for(var i = 0, ci; ci = c[i]; i++){
5591                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5592                         r[++ri] = ci;
5593                     }
5594                 }
5595                 return r;
5596             },
5597
5598             "checked" : function(c){
5599                 var r = [], ri = -1;
5600                 for(var i = 0, ci; ci = c[i]; i++){
5601                     if(ci.checked == true){
5602                         r[++ri] = ci;
5603                     }
5604                 }
5605                 return r;
5606             },
5607
5608             "not" : function(c, ss){
5609                 return Roo.DomQuery.filter(c, ss, true);
5610             },
5611
5612             "odd" : function(c){
5613                 return this["nth-child"](c, "odd");
5614             },
5615
5616             "even" : function(c){
5617                 return this["nth-child"](c, "even");
5618             },
5619
5620             "nth" : function(c, a){
5621                 return c[a-1] || [];
5622             },
5623
5624             "first" : function(c){
5625                 return c[0] || [];
5626             },
5627
5628             "last" : function(c){
5629                 return c[c.length-1] || [];
5630             },
5631
5632             "has" : function(c, ss){
5633                 var s = Roo.DomQuery.select;
5634                 var r = [], ri = -1;
5635                 for(var i = 0, ci; ci = c[i]; i++){
5636                     if(s(ss, ci).length > 0){
5637                         r[++ri] = ci;
5638                     }
5639                 }
5640                 return r;
5641             },
5642
5643             "next" : function(c, ss){
5644                 var is = Roo.DomQuery.is;
5645                 var r = [], ri = -1;
5646                 for(var i = 0, ci; ci = c[i]; i++){
5647                     var n = next(ci);
5648                     if(n && is(n, ss)){
5649                         r[++ri] = ci;
5650                     }
5651                 }
5652                 return r;
5653             },
5654
5655             "prev" : function(c, ss){
5656                 var is = Roo.DomQuery.is;
5657                 var r = [], ri = -1;
5658                 for(var i = 0, ci; ci = c[i]; i++){
5659                     var n = prev(ci);
5660                     if(n && is(n, ss)){
5661                         r[++ri] = ci;
5662                     }
5663                 }
5664                 return r;
5665             }
5666         }
5667     };
5668 }();
5669
5670 /**
5671  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5672  * @param {String} path The selector/xpath query
5673  * @param {Node} root (optional) The start of the query (defaults to document).
5674  * @return {Array}
5675  * @member Roo
5676  * @method query
5677  */
5678 Roo.query = Roo.DomQuery.select;
5679 /*
5680  * Based on:
5681  * Ext JS Library 1.1.1
5682  * Copyright(c) 2006-2007, Ext JS, LLC.
5683  *
5684  * Originally Released Under LGPL - original licence link has changed is not relivant.
5685  *
5686  * Fork - LGPL
5687  * <script type="text/javascript">
5688  */
5689
5690 /**
5691  * @class Roo.util.Observable
5692  * Base class that provides a common interface for publishing events. Subclasses are expected to
5693  * to have a property "events" with all the events defined.<br>
5694  * For example:
5695  * <pre><code>
5696  Employee = function(name){
5697     this.name = name;
5698     this.addEvents({
5699         "fired" : true,
5700         "quit" : true
5701     });
5702  }
5703  Roo.extend(Employee, Roo.util.Observable);
5704 </code></pre>
5705  * @param {Object} config properties to use (incuding events / listeners)
5706  */
5707
5708 Roo.util.Observable = function(cfg){
5709     
5710     cfg = cfg|| {};
5711     this.addEvents(cfg.events || {});
5712     if (cfg.events) {
5713         delete cfg.events; // make sure
5714     }
5715      
5716     Roo.apply(this, cfg);
5717     
5718     if(this.listeners){
5719         this.on(this.listeners);
5720         delete this.listeners;
5721     }
5722 };
5723 Roo.util.Observable.prototype = {
5724     /** 
5725  * @cfg {Object} listeners  list of events and functions to call for this object, 
5726  * For example :
5727  * <pre><code>
5728     listeners :  { 
5729        'click' : function(e) {
5730            ..... 
5731         } ,
5732         .... 
5733     } 
5734   </code></pre>
5735  */
5736     
5737     
5738     /**
5739      * Fires the specified event with the passed parameters (minus the event name).
5740      * @param {String} eventName
5741      * @param {Object...} args Variable number of parameters are passed to handlers
5742      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5743      */
5744     fireEvent : function(){
5745         var ce = this.events[arguments[0].toLowerCase()];
5746         if(typeof ce == "object"){
5747             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5748         }else{
5749             return true;
5750         }
5751     },
5752
5753     // private
5754     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5755
5756     /**
5757      * Appends an event handler to this component
5758      * @param {String}   eventName The type of event to listen for
5759      * @param {Function} handler The method the event invokes
5760      * @param {Object}   scope (optional) The scope in which to execute the handler
5761      * function. The handler function's "this" context.
5762      * @param {Object}   options (optional) An object containing handler configuration
5763      * properties. This may contain any of the following properties:<ul>
5764      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5765      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5766      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5767      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5768      * by the specified number of milliseconds. If the event fires again within that time, the original
5769      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5770      * </ul><br>
5771      * <p>
5772      * <b>Combining Options</b><br>
5773      * Using the options argument, it is possible to combine different types of listeners:<br>
5774      * <br>
5775      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5776                 <pre><code>
5777                 el.on('click', this.onClick, this, {
5778                         single: true,
5779                 delay: 100,
5780                 forumId: 4
5781                 });
5782                 </code></pre>
5783      * <p>
5784      * <b>Attaching multiple handlers in 1 call</b><br>
5785      * The method also allows for a single argument to be passed which is a config object containing properties
5786      * which specify multiple handlers.
5787      * <pre><code>
5788                 el.on({
5789                         'click': {
5790                         fn: this.onClick,
5791                         scope: this,
5792                         delay: 100
5793                 }, 
5794                 'mouseover': {
5795                         fn: this.onMouseOver,
5796                         scope: this
5797                 },
5798                 'mouseout': {
5799                         fn: this.onMouseOut,
5800                         scope: this
5801                 }
5802                 });
5803                 </code></pre>
5804      * <p>
5805      * Or a shorthand syntax which passes the same scope object to all handlers:
5806         <pre><code>
5807                 el.on({
5808                         'click': this.onClick,
5809                 'mouseover': this.onMouseOver,
5810                 'mouseout': this.onMouseOut,
5811                 scope: this
5812                 });
5813                 </code></pre>
5814      */
5815     addListener : function(eventName, fn, scope, o){
5816         if(typeof eventName == "object"){
5817             o = eventName;
5818             for(var e in o){
5819                 if(this.filterOptRe.test(e)){
5820                     continue;
5821                 }
5822                 if(typeof o[e] == "function"){
5823                     // shared options
5824                     this.addListener(e, o[e], o.scope,  o);
5825                 }else{
5826                     // individual options
5827                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5828                 }
5829             }
5830             return;
5831         }
5832         o = (!o || typeof o == "boolean") ? {} : o;
5833         eventName = eventName.toLowerCase();
5834         var ce = this.events[eventName] || true;
5835         if(typeof ce == "boolean"){
5836             ce = new Roo.util.Event(this, eventName);
5837             this.events[eventName] = ce;
5838         }
5839         ce.addListener(fn, scope, o);
5840     },
5841
5842     /**
5843      * Removes a listener
5844      * @param {String}   eventName     The type of event to listen for
5845      * @param {Function} handler        The handler to remove
5846      * @param {Object}   scope  (optional) The scope (this object) for the handler
5847      */
5848     removeListener : function(eventName, fn, scope){
5849         var ce = this.events[eventName.toLowerCase()];
5850         if(typeof ce == "object"){
5851             ce.removeListener(fn, scope);
5852         }
5853     },
5854
5855     /**
5856      * Removes all listeners for this object
5857      */
5858     purgeListeners : function(){
5859         for(var evt in this.events){
5860             if(typeof this.events[evt] == "object"){
5861                  this.events[evt].clearListeners();
5862             }
5863         }
5864     },
5865
5866     relayEvents : function(o, events){
5867         var createHandler = function(ename){
5868             return function(){
5869                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5870             };
5871         };
5872         for(var i = 0, len = events.length; i < len; i++){
5873             var ename = events[i];
5874             if(!this.events[ename]){ this.events[ename] = true; };
5875             o.on(ename, createHandler(ename), this);
5876         }
5877     },
5878
5879     /**
5880      * Used to define events on this Observable
5881      * @param {Object} object The object with the events defined
5882      */
5883     addEvents : function(o){
5884         if(!this.events){
5885             this.events = {};
5886         }
5887         Roo.applyIf(this.events, o);
5888     },
5889
5890     /**
5891      * Checks to see if this object has any listeners for a specified event
5892      * @param {String} eventName The name of the event to check for
5893      * @return {Boolean} True if the event is being listened for, else false
5894      */
5895     hasListener : function(eventName){
5896         var e = this.events[eventName];
5897         return typeof e == "object" && e.listeners.length > 0;
5898     }
5899 };
5900 /**
5901  * Appends an event handler to this element (shorthand for addListener)
5902  * @param {String}   eventName     The type of event to listen for
5903  * @param {Function} handler        The method the event invokes
5904  * @param {Object}   scope (optional) The scope in which to execute the handler
5905  * function. The handler function's "this" context.
5906  * @param {Object}   options  (optional)
5907  * @method
5908  */
5909 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5910 /**
5911  * Removes a listener (shorthand for removeListener)
5912  * @param {String}   eventName     The type of event to listen for
5913  * @param {Function} handler        The handler to remove
5914  * @param {Object}   scope  (optional) The scope (this object) for the handler
5915  * @method
5916  */
5917 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5918
5919 /**
5920  * Starts capture on the specified Observable. All events will be passed
5921  * to the supplied function with the event name + standard signature of the event
5922  * <b>before</b> the event is fired. If the supplied function returns false,
5923  * the event will not fire.
5924  * @param {Observable} o The Observable to capture
5925  * @param {Function} fn The function to call
5926  * @param {Object} scope (optional) The scope (this object) for the fn
5927  * @static
5928  */
5929 Roo.util.Observable.capture = function(o, fn, scope){
5930     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5931 };
5932
5933 /**
5934  * Removes <b>all</b> added captures from the Observable.
5935  * @param {Observable} o The Observable to release
5936  * @static
5937  */
5938 Roo.util.Observable.releaseCapture = function(o){
5939     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5940 };
5941
5942 (function(){
5943
5944     var createBuffered = function(h, o, scope){
5945         var task = new Roo.util.DelayedTask();
5946         return function(){
5947             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5948         };
5949     };
5950
5951     var createSingle = function(h, e, fn, scope){
5952         return function(){
5953             e.removeListener(fn, scope);
5954             return h.apply(scope, arguments);
5955         };
5956     };
5957
5958     var createDelayed = function(h, o, scope){
5959         return function(){
5960             var args = Array.prototype.slice.call(arguments, 0);
5961             setTimeout(function(){
5962                 h.apply(scope, args);
5963             }, o.delay || 10);
5964         };
5965     };
5966
5967     Roo.util.Event = function(obj, name){
5968         this.name = name;
5969         this.obj = obj;
5970         this.listeners = [];
5971     };
5972
5973     Roo.util.Event.prototype = {
5974         addListener : function(fn, scope, options){
5975             var o = options || {};
5976             scope = scope || this.obj;
5977             if(!this.isListening(fn, scope)){
5978                 var l = {fn: fn, scope: scope, options: o};
5979                 var h = fn;
5980                 if(o.delay){
5981                     h = createDelayed(h, o, scope);
5982                 }
5983                 if(o.single){
5984                     h = createSingle(h, this, fn, scope);
5985                 }
5986                 if(o.buffer){
5987                     h = createBuffered(h, o, scope);
5988                 }
5989                 l.fireFn = h;
5990                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5991                     this.listeners.push(l);
5992                 }else{
5993                     this.listeners = this.listeners.slice(0);
5994                     this.listeners.push(l);
5995                 }
5996             }
5997         },
5998
5999         findListener : function(fn, scope){
6000             scope = scope || this.obj;
6001             var ls = this.listeners;
6002             for(var i = 0, len = ls.length; i < len; i++){
6003                 var l = ls[i];
6004                 if(l.fn == fn && l.scope == scope){
6005                     return i;
6006                 }
6007             }
6008             return -1;
6009         },
6010
6011         isListening : function(fn, scope){
6012             return this.findListener(fn, scope) != -1;
6013         },
6014
6015         removeListener : function(fn, scope){
6016             var index;
6017             if((index = this.findListener(fn, scope)) != -1){
6018                 if(!this.firing){
6019                     this.listeners.splice(index, 1);
6020                 }else{
6021                     this.listeners = this.listeners.slice(0);
6022                     this.listeners.splice(index, 1);
6023                 }
6024                 return true;
6025             }
6026             return false;
6027         },
6028
6029         clearListeners : function(){
6030             this.listeners = [];
6031         },
6032
6033         fire : function(){
6034             var ls = this.listeners, scope, len = ls.length;
6035             if(len > 0){
6036                 this.firing = true;
6037                 var args = Array.prototype.slice.call(arguments, 0);
6038                 for(var i = 0; i < len; i++){
6039                     var l = ls[i];
6040                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6041                         this.firing = false;
6042                         return false;
6043                     }
6044                 }
6045                 this.firing = false;
6046             }
6047             return true;
6048         }
6049     };
6050 })();/*
6051  * Based on:
6052  * Ext JS Library 1.1.1
6053  * Copyright(c) 2006-2007, Ext JS, LLC.
6054  *
6055  * Originally Released Under LGPL - original licence link has changed is not relivant.
6056  *
6057  * Fork - LGPL
6058  * <script type="text/javascript">
6059  */
6060
6061 /**
6062  * @class Roo.EventManager
6063  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6064  * several useful events directly.
6065  * See {@link Roo.EventObject} for more details on normalized event objects.
6066  * @singleton
6067  */
6068 Roo.EventManager = function(){
6069     var docReadyEvent, docReadyProcId, docReadyState = false;
6070     var resizeEvent, resizeTask, textEvent, textSize;
6071     var E = Roo.lib.Event;
6072     var D = Roo.lib.Dom;
6073
6074     
6075     
6076
6077     var fireDocReady = function(){
6078         if(!docReadyState){
6079             docReadyState = true;
6080             Roo.isReady = true;
6081             if(docReadyProcId){
6082                 clearInterval(docReadyProcId);
6083             }
6084             if(Roo.isGecko || Roo.isOpera) {
6085                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6086             }
6087             if(Roo.isIE){
6088                 var defer = document.getElementById("ie-deferred-loader");
6089                 if(defer){
6090                     defer.onreadystatechange = null;
6091                     defer.parentNode.removeChild(defer);
6092                 }
6093             }
6094             if(docReadyEvent){
6095                 docReadyEvent.fire();
6096                 docReadyEvent.clearListeners();
6097             }
6098         }
6099     };
6100     
6101     var initDocReady = function(){
6102         docReadyEvent = new Roo.util.Event();
6103         if(Roo.isGecko || Roo.isOpera) {
6104             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6105         }else if(Roo.isIE){
6106             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6107             var defer = document.getElementById("ie-deferred-loader");
6108             defer.onreadystatechange = function(){
6109                 if(this.readyState == "complete"){
6110                     fireDocReady();
6111                 }
6112             };
6113         }else if(Roo.isSafari){ 
6114             docReadyProcId = setInterval(function(){
6115                 var rs = document.readyState;
6116                 if(rs == "complete") {
6117                     fireDocReady();     
6118                  }
6119             }, 10);
6120         }
6121         // no matter what, make sure it fires on load
6122         E.on(window, "load", fireDocReady);
6123     };
6124
6125     var createBuffered = function(h, o){
6126         var task = new Roo.util.DelayedTask(h);
6127         return function(e){
6128             // create new event object impl so new events don't wipe out properties
6129             e = new Roo.EventObjectImpl(e);
6130             task.delay(o.buffer, h, null, [e]);
6131         };
6132     };
6133
6134     var createSingle = function(h, el, ename, fn){
6135         return function(e){
6136             Roo.EventManager.removeListener(el, ename, fn);
6137             h(e);
6138         };
6139     };
6140
6141     var createDelayed = function(h, o){
6142         return function(e){
6143             // create new event object impl so new events don't wipe out properties
6144             e = new Roo.EventObjectImpl(e);
6145             setTimeout(function(){
6146                 h(e);
6147             }, o.delay || 10);
6148         };
6149     };
6150     var transitionEndVal = false;
6151     
6152     var transitionEnd = function()
6153     {
6154         if (transitionEndVal) {
6155             return transitionEndVal;
6156         }
6157         var el = document.createElement('div');
6158
6159         var transEndEventNames = {
6160             WebkitTransition : 'webkitTransitionEnd',
6161             MozTransition    : 'transitionend',
6162             OTransition      : 'oTransitionEnd otransitionend',
6163             transition       : 'transitionend'
6164         };
6165     
6166         for (var name in transEndEventNames) {
6167             if (el.style[name] !== undefined) {
6168                 transitionEndVal = transEndEventNames[name];
6169                 return  transitionEndVal ;
6170             }
6171         }
6172     }
6173     
6174
6175     var listen = function(element, ename, opt, fn, scope){
6176         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6177         fn = fn || o.fn; scope = scope || o.scope;
6178         var el = Roo.getDom(element);
6179         
6180         
6181         if(!el){
6182             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6183         }
6184         
6185         if (ename == 'transitionend') {
6186             ename = transitionEnd();
6187         }
6188         var h = function(e){
6189             e = Roo.EventObject.setEvent(e);
6190             var t;
6191             if(o.delegate){
6192                 t = e.getTarget(o.delegate, el);
6193                 if(!t){
6194                     return;
6195                 }
6196             }else{
6197                 t = e.target;
6198             }
6199             if(o.stopEvent === true){
6200                 e.stopEvent();
6201             }
6202             if(o.preventDefault === true){
6203                e.preventDefault();
6204             }
6205             if(o.stopPropagation === true){
6206                 e.stopPropagation();
6207             }
6208
6209             if(o.normalized === false){
6210                 e = e.browserEvent;
6211             }
6212
6213             fn.call(scope || el, e, t, o);
6214         };
6215         if(o.delay){
6216             h = createDelayed(h, o);
6217         }
6218         if(o.single){
6219             h = createSingle(h, el, ename, fn);
6220         }
6221         if(o.buffer){
6222             h = createBuffered(h, o);
6223         }
6224         fn._handlers = fn._handlers || [];
6225         
6226         
6227         fn._handlers.push([Roo.id(el), ename, h]);
6228         
6229         
6230          
6231         E.on(el, ename, h);
6232         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6233             el.addEventListener("DOMMouseScroll", h, false);
6234             E.on(window, 'unload', function(){
6235                 el.removeEventListener("DOMMouseScroll", h, false);
6236             });
6237         }
6238         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6239             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6240         }
6241         return h;
6242     };
6243
6244     var stopListening = function(el, ename, fn){
6245         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6246         if(hds){
6247             for(var i = 0, len = hds.length; i < len; i++){
6248                 var h = hds[i];
6249                 if(h[0] == id && h[1] == ename){
6250                     hd = h[2];
6251                     hds.splice(i, 1);
6252                     break;
6253                 }
6254             }
6255         }
6256         E.un(el, ename, hd);
6257         el = Roo.getDom(el);
6258         if(ename == "mousewheel" && el.addEventListener){
6259             el.removeEventListener("DOMMouseScroll", hd, false);
6260         }
6261         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6262             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6263         }
6264     };
6265
6266     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6267     
6268     var pub = {
6269         
6270         
6271         /** 
6272          * Fix for doc tools
6273          * @scope Roo.EventManager
6274          */
6275         
6276         
6277         /** 
6278          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6279          * object with a Roo.EventObject
6280          * @param {Function} fn        The method the event invokes
6281          * @param {Object}   scope    An object that becomes the scope of the handler
6282          * @param {boolean}  override If true, the obj passed in becomes
6283          *                             the execution scope of the listener
6284          * @return {Function} The wrapped function
6285          * @deprecated
6286          */
6287         wrap : function(fn, scope, override){
6288             return function(e){
6289                 Roo.EventObject.setEvent(e);
6290                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6291             };
6292         },
6293         
6294         /**
6295      * Appends an event handler to an element (shorthand for addListener)
6296      * @param {String/HTMLElement}   element        The html element or id to assign the
6297      * @param {String}   eventName The type of event to listen for
6298      * @param {Function} handler The method the event invokes
6299      * @param {Object}   scope (optional) The scope in which to execute the handler
6300      * function. The handler function's "this" context.
6301      * @param {Object}   options (optional) An object containing handler configuration
6302      * properties. This may contain any of the following properties:<ul>
6303      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6304      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6305      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6306      * <li>preventDefault {Boolean} True to prevent the default action</li>
6307      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6308      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6309      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6310      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6311      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6312      * by the specified number of milliseconds. If the event fires again within that time, the original
6313      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6314      * </ul><br>
6315      * <p>
6316      * <b>Combining Options</b><br>
6317      * Using the options argument, it is possible to combine different types of listeners:<br>
6318      * <br>
6319      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6320      * Code:<pre><code>
6321 el.on('click', this.onClick, this, {
6322     single: true,
6323     delay: 100,
6324     stopEvent : true,
6325     forumId: 4
6326 });</code></pre>
6327      * <p>
6328      * <b>Attaching multiple handlers in 1 call</b><br>
6329       * The method also allows for a single argument to be passed which is a config object containing properties
6330      * which specify multiple handlers.
6331      * <p>
6332      * Code:<pre><code>
6333 el.on({
6334     'click' : {
6335         fn: this.onClick
6336         scope: this,
6337         delay: 100
6338     },
6339     'mouseover' : {
6340         fn: this.onMouseOver
6341         scope: this
6342     },
6343     'mouseout' : {
6344         fn: this.onMouseOut
6345         scope: this
6346     }
6347 });</code></pre>
6348      * <p>
6349      * Or a shorthand syntax:<br>
6350      * Code:<pre><code>
6351 el.on({
6352     'click' : this.onClick,
6353     'mouseover' : this.onMouseOver,
6354     'mouseout' : this.onMouseOut
6355     scope: this
6356 });</code></pre>
6357      */
6358         addListener : function(element, eventName, fn, scope, options){
6359             if(typeof eventName == "object"){
6360                 var o = eventName;
6361                 for(var e in o){
6362                     if(propRe.test(e)){
6363                         continue;
6364                     }
6365                     if(typeof o[e] == "function"){
6366                         // shared options
6367                         listen(element, e, o, o[e], o.scope);
6368                     }else{
6369                         // individual options
6370                         listen(element, e, o[e]);
6371                     }
6372                 }
6373                 return;
6374             }
6375             return listen(element, eventName, options, fn, scope);
6376         },
6377         
6378         /**
6379          * Removes an event handler
6380          *
6381          * @param {String/HTMLElement}   element        The id or html element to remove the 
6382          *                             event from
6383          * @param {String}   eventName     The type of event
6384          * @param {Function} fn
6385          * @return {Boolean} True if a listener was actually removed
6386          */
6387         removeListener : function(element, eventName, fn){
6388             return stopListening(element, eventName, fn);
6389         },
6390         
6391         /**
6392          * Fires when the document is ready (before onload and before images are loaded). Can be 
6393          * accessed shorthanded Roo.onReady().
6394          * @param {Function} fn        The method the event invokes
6395          * @param {Object}   scope    An  object that becomes the scope of the handler
6396          * @param {boolean}  options
6397          */
6398         onDocumentReady : function(fn, scope, options){
6399             if(docReadyState){ // if it already fired
6400                 docReadyEvent.addListener(fn, scope, options);
6401                 docReadyEvent.fire();
6402                 docReadyEvent.clearListeners();
6403                 return;
6404             }
6405             if(!docReadyEvent){
6406                 initDocReady();
6407             }
6408             docReadyEvent.addListener(fn, scope, options);
6409         },
6410         
6411         /**
6412          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6413          * @param {Function} fn        The method the event invokes
6414          * @param {Object}   scope    An object that becomes the scope of the handler
6415          * @param {boolean}  options
6416          */
6417         onWindowResize : function(fn, scope, options){
6418             if(!resizeEvent){
6419                 resizeEvent = new Roo.util.Event();
6420                 resizeTask = new Roo.util.DelayedTask(function(){
6421                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6422                 });
6423                 E.on(window, "resize", function(){
6424                     if(Roo.isIE){
6425                         resizeTask.delay(50);
6426                     }else{
6427                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6428                     }
6429                 });
6430             }
6431             resizeEvent.addListener(fn, scope, options);
6432         },
6433
6434         /**
6435          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6436          * @param {Function} fn        The method the event invokes
6437          * @param {Object}   scope    An object that becomes the scope of the handler
6438          * @param {boolean}  options
6439          */
6440         onTextResize : function(fn, scope, options){
6441             if(!textEvent){
6442                 textEvent = new Roo.util.Event();
6443                 var textEl = new Roo.Element(document.createElement('div'));
6444                 textEl.dom.className = 'x-text-resize';
6445                 textEl.dom.innerHTML = 'X';
6446                 textEl.appendTo(document.body);
6447                 textSize = textEl.dom.offsetHeight;
6448                 setInterval(function(){
6449                     if(textEl.dom.offsetHeight != textSize){
6450                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6451                     }
6452                 }, this.textResizeInterval);
6453             }
6454             textEvent.addListener(fn, scope, options);
6455         },
6456
6457         /**
6458          * Removes the passed window resize listener.
6459          * @param {Function} fn        The method the event invokes
6460          * @param {Object}   scope    The scope of handler
6461          */
6462         removeResizeListener : function(fn, scope){
6463             if(resizeEvent){
6464                 resizeEvent.removeListener(fn, scope);
6465             }
6466         },
6467
6468         // private
6469         fireResize : function(){
6470             if(resizeEvent){
6471                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6472             }   
6473         },
6474         /**
6475          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6476          */
6477         ieDeferSrc : false,
6478         /**
6479          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6480          */
6481         textResizeInterval : 50
6482     };
6483     
6484     /**
6485      * Fix for doc tools
6486      * @scopeAlias pub=Roo.EventManager
6487      */
6488     
6489      /**
6490      * Appends an event handler to an element (shorthand for addListener)
6491      * @param {String/HTMLElement}   element        The html element or id to assign the
6492      * @param {String}   eventName The type of event to listen for
6493      * @param {Function} handler The method the event invokes
6494      * @param {Object}   scope (optional) The scope in which to execute the handler
6495      * function. The handler function's "this" context.
6496      * @param {Object}   options (optional) An object containing handler configuration
6497      * properties. This may contain any of the following properties:<ul>
6498      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6499      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6500      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6501      * <li>preventDefault {Boolean} True to prevent the default action</li>
6502      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6503      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6504      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6505      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6506      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6507      * by the specified number of milliseconds. If the event fires again within that time, the original
6508      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6509      * </ul><br>
6510      * <p>
6511      * <b>Combining Options</b><br>
6512      * Using the options argument, it is possible to combine different types of listeners:<br>
6513      * <br>
6514      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6515      * Code:<pre><code>
6516 el.on('click', this.onClick, this, {
6517     single: true,
6518     delay: 100,
6519     stopEvent : true,
6520     forumId: 4
6521 });</code></pre>
6522      * <p>
6523      * <b>Attaching multiple handlers in 1 call</b><br>
6524       * The method also allows for a single argument to be passed which is a config object containing properties
6525      * which specify multiple handlers.
6526      * <p>
6527      * Code:<pre><code>
6528 el.on({
6529     'click' : {
6530         fn: this.onClick
6531         scope: this,
6532         delay: 100
6533     },
6534     'mouseover' : {
6535         fn: this.onMouseOver
6536         scope: this
6537     },
6538     'mouseout' : {
6539         fn: this.onMouseOut
6540         scope: this
6541     }
6542 });</code></pre>
6543      * <p>
6544      * Or a shorthand syntax:<br>
6545      * Code:<pre><code>
6546 el.on({
6547     'click' : this.onClick,
6548     'mouseover' : this.onMouseOver,
6549     'mouseout' : this.onMouseOut
6550     scope: this
6551 });</code></pre>
6552      */
6553     pub.on = pub.addListener;
6554     pub.un = pub.removeListener;
6555
6556     pub.stoppedMouseDownEvent = new Roo.util.Event();
6557     return pub;
6558 }();
6559 /**
6560   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6561   * @param {Function} fn        The method the event invokes
6562   * @param {Object}   scope    An  object that becomes the scope of the handler
6563   * @param {boolean}  override If true, the obj passed in becomes
6564   *                             the execution scope of the listener
6565   * @member Roo
6566   * @method onReady
6567  */
6568 Roo.onReady = Roo.EventManager.onDocumentReady;
6569
6570 Roo.onReady(function(){
6571     var bd = Roo.get(document.body);
6572     if(!bd){ return; }
6573
6574     var cls = [
6575             Roo.isIE ? "roo-ie"
6576             : Roo.isGecko ? "roo-gecko"
6577             : Roo.isOpera ? "roo-opera"
6578             : Roo.isSafari ? "roo-safari" : ""];
6579
6580     if(Roo.isMac){
6581         cls.push("roo-mac");
6582     }
6583     if(Roo.isLinux){
6584         cls.push("roo-linux");
6585     }
6586     if(Roo.isIOS){
6587         cls.push("roo-ios");
6588     }
6589     if(Roo.isTouch){
6590         cls.push("roo-touch");
6591     }
6592     if(Roo.isBorderBox){
6593         cls.push('roo-border-box');
6594     }
6595     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6596         var p = bd.dom.parentNode;
6597         if(p){
6598             p.className += ' roo-strict';
6599         }
6600     }
6601     bd.addClass(cls.join(' '));
6602 });
6603
6604 /**
6605  * @class Roo.EventObject
6606  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6607  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6608  * Example:
6609  * <pre><code>
6610  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6611     e.preventDefault();
6612     var target = e.getTarget();
6613     ...
6614  }
6615  var myDiv = Roo.get("myDiv");
6616  myDiv.on("click", handleClick);
6617  //or
6618  Roo.EventManager.on("myDiv", 'click', handleClick);
6619  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6620  </code></pre>
6621  * @singleton
6622  */
6623 Roo.EventObject = function(){
6624     
6625     var E = Roo.lib.Event;
6626     
6627     // safari keypress events for special keys return bad keycodes
6628     var safariKeys = {
6629         63234 : 37, // left
6630         63235 : 39, // right
6631         63232 : 38, // up
6632         63233 : 40, // down
6633         63276 : 33, // page up
6634         63277 : 34, // page down
6635         63272 : 46, // delete
6636         63273 : 36, // home
6637         63275 : 35  // end
6638     };
6639
6640     // normalize button clicks
6641     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6642                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6643
6644     Roo.EventObjectImpl = function(e){
6645         if(e){
6646             this.setEvent(e.browserEvent || e);
6647         }
6648     };
6649     Roo.EventObjectImpl.prototype = {
6650         /**
6651          * Used to fix doc tools.
6652          * @scope Roo.EventObject.prototype
6653          */
6654             
6655
6656         
6657         
6658         /** The normal browser event */
6659         browserEvent : null,
6660         /** The button pressed in a mouse event */
6661         button : -1,
6662         /** True if the shift key was down during the event */
6663         shiftKey : false,
6664         /** True if the control key was down during the event */
6665         ctrlKey : false,
6666         /** True if the alt key was down during the event */
6667         altKey : false,
6668
6669         /** Key constant 
6670         * @type Number */
6671         BACKSPACE : 8,
6672         /** Key constant 
6673         * @type Number */
6674         TAB : 9,
6675         /** Key constant 
6676         * @type Number */
6677         RETURN : 13,
6678         /** Key constant 
6679         * @type Number */
6680         ENTER : 13,
6681         /** Key constant 
6682         * @type Number */
6683         SHIFT : 16,
6684         /** Key constant 
6685         * @type Number */
6686         CONTROL : 17,
6687         /** Key constant 
6688         * @type Number */
6689         ESC : 27,
6690         /** Key constant 
6691         * @type Number */
6692         SPACE : 32,
6693         /** Key constant 
6694         * @type Number */
6695         PAGEUP : 33,
6696         /** Key constant 
6697         * @type Number */
6698         PAGEDOWN : 34,
6699         /** Key constant 
6700         * @type Number */
6701         END : 35,
6702         /** Key constant 
6703         * @type Number */
6704         HOME : 36,
6705         /** Key constant 
6706         * @type Number */
6707         LEFT : 37,
6708         /** Key constant 
6709         * @type Number */
6710         UP : 38,
6711         /** Key constant 
6712         * @type Number */
6713         RIGHT : 39,
6714         /** Key constant 
6715         * @type Number */
6716         DOWN : 40,
6717         /** Key constant 
6718         * @type Number */
6719         DELETE : 46,
6720         /** Key constant 
6721         * @type Number */
6722         F5 : 116,
6723
6724            /** @private */
6725         setEvent : function(e){
6726             if(e == this || (e && e.browserEvent)){ // already wrapped
6727                 return e;
6728             }
6729             this.browserEvent = e;
6730             if(e){
6731                 // normalize buttons
6732                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6733                 if(e.type == 'click' && this.button == -1){
6734                     this.button = 0;
6735                 }
6736                 this.type = e.type;
6737                 this.shiftKey = e.shiftKey;
6738                 // mac metaKey behaves like ctrlKey
6739                 this.ctrlKey = e.ctrlKey || e.metaKey;
6740                 this.altKey = e.altKey;
6741                 // in getKey these will be normalized for the mac
6742                 this.keyCode = e.keyCode;
6743                 // keyup warnings on firefox.
6744                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6745                 // cache the target for the delayed and or buffered events
6746                 this.target = E.getTarget(e);
6747                 // same for XY
6748                 this.xy = E.getXY(e);
6749             }else{
6750                 this.button = -1;
6751                 this.shiftKey = false;
6752                 this.ctrlKey = false;
6753                 this.altKey = false;
6754                 this.keyCode = 0;
6755                 this.charCode =0;
6756                 this.target = null;
6757                 this.xy = [0, 0];
6758             }
6759             return this;
6760         },
6761
6762         /**
6763          * Stop the event (preventDefault and stopPropagation)
6764          */
6765         stopEvent : function(){
6766             if(this.browserEvent){
6767                 if(this.browserEvent.type == 'mousedown'){
6768                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6769                 }
6770                 E.stopEvent(this.browserEvent);
6771             }
6772         },
6773
6774         /**
6775          * Prevents the browsers default handling of the event.
6776          */
6777         preventDefault : function(){
6778             if(this.browserEvent){
6779                 E.preventDefault(this.browserEvent);
6780             }
6781         },
6782
6783         /** @private */
6784         isNavKeyPress : function(){
6785             var k = this.keyCode;
6786             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6787             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6788         },
6789
6790         isSpecialKey : function(){
6791             var k = this.keyCode;
6792             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6793             (k == 16) || (k == 17) ||
6794             (k >= 18 && k <= 20) ||
6795             (k >= 33 && k <= 35) ||
6796             (k >= 36 && k <= 39) ||
6797             (k >= 44 && k <= 45);
6798         },
6799         /**
6800          * Cancels bubbling of the event.
6801          */
6802         stopPropagation : function(){
6803             if(this.browserEvent){
6804                 if(this.type == 'mousedown'){
6805                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6806                 }
6807                 E.stopPropagation(this.browserEvent);
6808             }
6809         },
6810
6811         /**
6812          * Gets the key code for the event.
6813          * @return {Number}
6814          */
6815         getCharCode : function(){
6816             return this.charCode || this.keyCode;
6817         },
6818
6819         /**
6820          * Returns a normalized keyCode for the event.
6821          * @return {Number} The key code
6822          */
6823         getKey : function(){
6824             var k = this.keyCode || this.charCode;
6825             return Roo.isSafari ? (safariKeys[k] || k) : k;
6826         },
6827
6828         /**
6829          * Gets the x coordinate of the event.
6830          * @return {Number}
6831          */
6832         getPageX : function(){
6833             return this.xy[0];
6834         },
6835
6836         /**
6837          * Gets the y coordinate of the event.
6838          * @return {Number}
6839          */
6840         getPageY : function(){
6841             return this.xy[1];
6842         },
6843
6844         /**
6845          * Gets the time of the event.
6846          * @return {Number}
6847          */
6848         getTime : function(){
6849             if(this.browserEvent){
6850                 return E.getTime(this.browserEvent);
6851             }
6852             return null;
6853         },
6854
6855         /**
6856          * Gets the page coordinates of the event.
6857          * @return {Array} The xy values like [x, y]
6858          */
6859         getXY : function(){
6860             return this.xy;
6861         },
6862
6863         /**
6864          * Gets the target for the event.
6865          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6866          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6867                 search as a number or element (defaults to 10 || document.body)
6868          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6869          * @return {HTMLelement}
6870          */
6871         getTarget : function(selector, maxDepth, returnEl){
6872             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6873         },
6874         /**
6875          * Gets the related target.
6876          * @return {HTMLElement}
6877          */
6878         getRelatedTarget : function(){
6879             if(this.browserEvent){
6880                 return E.getRelatedTarget(this.browserEvent);
6881             }
6882             return null;
6883         },
6884
6885         /**
6886          * Normalizes mouse wheel delta across browsers
6887          * @return {Number} The delta
6888          */
6889         getWheelDelta : function(){
6890             var e = this.browserEvent;
6891             var delta = 0;
6892             if(e.wheelDelta){ /* IE/Opera. */
6893                 delta = e.wheelDelta/120;
6894             }else if(e.detail){ /* Mozilla case. */
6895                 delta = -e.detail/3;
6896             }
6897             return delta;
6898         },
6899
6900         /**
6901          * Returns true if the control, meta, shift or alt key was pressed during this event.
6902          * @return {Boolean}
6903          */
6904         hasModifier : function(){
6905             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6906         },
6907
6908         /**
6909          * Returns true if the target of this event equals el or is a child of el
6910          * @param {String/HTMLElement/Element} el
6911          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6912          * @return {Boolean}
6913          */
6914         within : function(el, related){
6915             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6916             return t && Roo.fly(el).contains(t);
6917         },
6918
6919         getPoint : function(){
6920             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6921         }
6922     };
6923
6924     return new Roo.EventObjectImpl();
6925 }();
6926             
6927     /*
6928  * Based on:
6929  * Ext JS Library 1.1.1
6930  * Copyright(c) 2006-2007, Ext JS, LLC.
6931  *
6932  * Originally Released Under LGPL - original licence link has changed is not relivant.
6933  *
6934  * Fork - LGPL
6935  * <script type="text/javascript">
6936  */
6937
6938  
6939 // was in Composite Element!??!?!
6940  
6941 (function(){
6942     var D = Roo.lib.Dom;
6943     var E = Roo.lib.Event;
6944     var A = Roo.lib.Anim;
6945
6946     // local style camelizing for speed
6947     var propCache = {};
6948     var camelRe = /(-[a-z])/gi;
6949     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6950     var view = document.defaultView;
6951
6952 /**
6953  * @class Roo.Element
6954  * Represents an Element in the DOM.<br><br>
6955  * Usage:<br>
6956 <pre><code>
6957 var el = Roo.get("my-div");
6958
6959 // or with getEl
6960 var el = getEl("my-div");
6961
6962 // or with a DOM element
6963 var el = Roo.get(myDivElement);
6964 </code></pre>
6965  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6966  * each call instead of constructing a new one.<br><br>
6967  * <b>Animations</b><br />
6968  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6969  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6970 <pre>
6971 Option    Default   Description
6972 --------- --------  ---------------------------------------------
6973 duration  .35       The duration of the animation in seconds
6974 easing    easeOut   The YUI easing method
6975 callback  none      A function to execute when the anim completes
6976 scope     this      The scope (this) of the callback function
6977 </pre>
6978 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6979 * manipulate the animation. Here's an example:
6980 <pre><code>
6981 var el = Roo.get("my-div");
6982
6983 // no animation
6984 el.setWidth(100);
6985
6986 // default animation
6987 el.setWidth(100, true);
6988
6989 // animation with some options set
6990 el.setWidth(100, {
6991     duration: 1,
6992     callback: this.foo,
6993     scope: this
6994 });
6995
6996 // using the "anim" property to get the Anim object
6997 var opt = {
6998     duration: 1,
6999     callback: this.foo,
7000     scope: this
7001 };
7002 el.setWidth(100, opt);
7003 ...
7004 if(opt.anim.isAnimated()){
7005     opt.anim.stop();
7006 }
7007 </code></pre>
7008 * <b> Composite (Collections of) Elements</b><br />
7009  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7010  * @constructor Create a new Element directly.
7011  * @param {String/HTMLElement} element
7012  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
7013  */
7014     Roo.Element = function(element, forceNew){
7015         var dom = typeof element == "string" ?
7016                 document.getElementById(element) : element;
7017         if(!dom){ // invalid id/element
7018             return null;
7019         }
7020         var id = dom.id;
7021         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7022             return Roo.Element.cache[id];
7023         }
7024
7025         /**
7026          * The DOM element
7027          * @type HTMLElement
7028          */
7029         this.dom = dom;
7030
7031         /**
7032          * The DOM element ID
7033          * @type String
7034          */
7035         this.id = id || Roo.id(dom);
7036     };
7037
7038     var El = Roo.Element;
7039
7040     El.prototype = {
7041         /**
7042          * The element's default display mode  (defaults to "")
7043          * @type String
7044          */
7045         originalDisplay : "",
7046
7047         visibilityMode : 1,
7048         /**
7049          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7050          * @type String
7051          */
7052         defaultUnit : "px",
7053         
7054         /**
7055          * Sets the element's visibility mode. When setVisible() is called it
7056          * will use this to determine whether to set the visibility or the display property.
7057          * @param visMode Element.VISIBILITY or Element.DISPLAY
7058          * @return {Roo.Element} this
7059          */
7060         setVisibilityMode : function(visMode){
7061             this.visibilityMode = visMode;
7062             return this;
7063         },
7064         /**
7065          * Convenience method for setVisibilityMode(Element.DISPLAY)
7066          * @param {String} display (optional) What to set display to when visible
7067          * @return {Roo.Element} this
7068          */
7069         enableDisplayMode : function(display){
7070             this.setVisibilityMode(El.DISPLAY);
7071             if(typeof display != "undefined") { this.originalDisplay = display; }
7072             return this;
7073         },
7074
7075         /**
7076          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7077          * @param {String} selector The simple selector to test
7078          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7079                 search as a number or element (defaults to 10 || document.body)
7080          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7081          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7082          */
7083         findParent : function(simpleSelector, maxDepth, returnEl){
7084             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7085             maxDepth = maxDepth || 50;
7086             if(typeof maxDepth != "number"){
7087                 stopEl = Roo.getDom(maxDepth);
7088                 maxDepth = 10;
7089             }
7090             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7091                 if(dq.is(p, simpleSelector)){
7092                     return returnEl ? Roo.get(p) : p;
7093                 }
7094                 depth++;
7095                 p = p.parentNode;
7096             }
7097             return null;
7098         },
7099
7100
7101         /**
7102          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7103          * @param {String} selector The simple selector to test
7104          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7105                 search as a number or element (defaults to 10 || document.body)
7106          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7107          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7108          */
7109         findParentNode : function(simpleSelector, maxDepth, returnEl){
7110             var p = Roo.fly(this.dom.parentNode, '_internal');
7111             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7112         },
7113
7114         /**
7115          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7116          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7117          * @param {String} selector The simple selector to test
7118          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7119                 search as a number or element (defaults to 10 || document.body)
7120          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7121          */
7122         up : function(simpleSelector, maxDepth){
7123             return this.findParentNode(simpleSelector, maxDepth, true);
7124         },
7125
7126
7127
7128         /**
7129          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7130          * @param {String} selector The simple selector to test
7131          * @return {Boolean} True if this element matches the selector, else false
7132          */
7133         is : function(simpleSelector){
7134             return Roo.DomQuery.is(this.dom, simpleSelector);
7135         },
7136
7137         /**
7138          * Perform animation on this element.
7139          * @param {Object} args The YUI animation control args
7140          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7141          * @param {Function} onComplete (optional) Function to call when animation completes
7142          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7143          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7144          * @return {Roo.Element} this
7145          */
7146         animate : function(args, duration, onComplete, easing, animType){
7147             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7148             return this;
7149         },
7150
7151         /*
7152          * @private Internal animation call
7153          */
7154         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7155             animType = animType || 'run';
7156             opt = opt || {};
7157             var anim = Roo.lib.Anim[animType](
7158                 this.dom, args,
7159                 (opt.duration || defaultDur) || .35,
7160                 (opt.easing || defaultEase) || 'easeOut',
7161                 function(){
7162                     Roo.callback(cb, this);
7163                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7164                 },
7165                 this
7166             );
7167             opt.anim = anim;
7168             return anim;
7169         },
7170
7171         // private legacy anim prep
7172         preanim : function(a, i){
7173             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7174         },
7175
7176         /**
7177          * Removes worthless text nodes
7178          * @param {Boolean} forceReclean (optional) By default the element
7179          * keeps track if it has been cleaned already so
7180          * you can call this over and over. However, if you update the element and
7181          * need to force a reclean, you can pass true.
7182          */
7183         clean : function(forceReclean){
7184             if(this.isCleaned && forceReclean !== true){
7185                 return this;
7186             }
7187             var ns = /\S/;
7188             var d = this.dom, n = d.firstChild, ni = -1;
7189             while(n){
7190                 var nx = n.nextSibling;
7191                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7192                     d.removeChild(n);
7193                 }else{
7194                     n.nodeIndex = ++ni;
7195                 }
7196                 n = nx;
7197             }
7198             this.isCleaned = true;
7199             return this;
7200         },
7201
7202         // private
7203         calcOffsetsTo : function(el){
7204             el = Roo.get(el);
7205             var d = el.dom;
7206             var restorePos = false;
7207             if(el.getStyle('position') == 'static'){
7208                 el.position('relative');
7209                 restorePos = true;
7210             }
7211             var x = 0, y =0;
7212             var op = this.dom;
7213             while(op && op != d && op.tagName != 'HTML'){
7214                 x+= op.offsetLeft;
7215                 y+= op.offsetTop;
7216                 op = op.offsetParent;
7217             }
7218             if(restorePos){
7219                 el.position('static');
7220             }
7221             return [x, y];
7222         },
7223
7224         /**
7225          * Scrolls this element into view within the passed container.
7226          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7227          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7228          * @return {Roo.Element} this
7229          */
7230         scrollIntoView : function(container, hscroll){
7231             var c = Roo.getDom(container) || document.body;
7232             var el = this.dom;
7233
7234             var o = this.calcOffsetsTo(c),
7235                 l = o[0],
7236                 t = o[1],
7237                 b = t+el.offsetHeight,
7238                 r = l+el.offsetWidth;
7239
7240             var ch = c.clientHeight;
7241             var ct = parseInt(c.scrollTop, 10);
7242             var cl = parseInt(c.scrollLeft, 10);
7243             var cb = ct + ch;
7244             var cr = cl + c.clientWidth;
7245
7246             if(t < ct){
7247                 c.scrollTop = t;
7248             }else if(b > cb){
7249                 c.scrollTop = b-ch;
7250             }
7251
7252             if(hscroll !== false){
7253                 if(l < cl){
7254                     c.scrollLeft = l;
7255                 }else if(r > cr){
7256                     c.scrollLeft = r-c.clientWidth;
7257                 }
7258             }
7259             return this;
7260         },
7261
7262         // private
7263         scrollChildIntoView : function(child, hscroll){
7264             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7265         },
7266
7267         /**
7268          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7269          * the new height may not be available immediately.
7270          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7271          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7272          * @param {Function} onComplete (optional) Function to call when animation completes
7273          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7274          * @return {Roo.Element} this
7275          */
7276         autoHeight : function(animate, duration, onComplete, easing){
7277             var oldHeight = this.getHeight();
7278             this.clip();
7279             this.setHeight(1); // force clipping
7280             setTimeout(function(){
7281                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7282                 if(!animate){
7283                     this.setHeight(height);
7284                     this.unclip();
7285                     if(typeof onComplete == "function"){
7286                         onComplete();
7287                     }
7288                 }else{
7289                     this.setHeight(oldHeight); // restore original height
7290                     this.setHeight(height, animate, duration, function(){
7291                         this.unclip();
7292                         if(typeof onComplete == "function") { onComplete(); }
7293                     }.createDelegate(this), easing);
7294                 }
7295             }.createDelegate(this), 0);
7296             return this;
7297         },
7298
7299         /**
7300          * Returns true if this element is an ancestor of the passed element
7301          * @param {HTMLElement/String} el The element to check
7302          * @return {Boolean} True if this element is an ancestor of el, else false
7303          */
7304         contains : function(el){
7305             if(!el){return false;}
7306             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7307         },
7308
7309         /**
7310          * Checks whether the element is currently visible using both visibility and display properties.
7311          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7312          * @return {Boolean} True if the element is currently visible, else false
7313          */
7314         isVisible : function(deep) {
7315             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7316             if(deep !== true || !vis){
7317                 return vis;
7318             }
7319             var p = this.dom.parentNode;
7320             while(p && p.tagName.toLowerCase() != "body"){
7321                 if(!Roo.fly(p, '_isVisible').isVisible()){
7322                     return false;
7323                 }
7324                 p = p.parentNode;
7325             }
7326             return true;
7327         },
7328
7329         /**
7330          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7331          * @param {String} selector The CSS selector
7332          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7333          * @return {CompositeElement/CompositeElementLite} The composite element
7334          */
7335         select : function(selector, unique){
7336             return El.select(selector, unique, this.dom);
7337         },
7338
7339         /**
7340          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7341          * @param {String} selector The CSS selector
7342          * @return {Array} An array of the matched nodes
7343          */
7344         query : function(selector, unique){
7345             return Roo.DomQuery.select(selector, this.dom);
7346         },
7347
7348         /**
7349          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7350          * @param {String} selector The CSS selector
7351          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7352          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7353          */
7354         child : function(selector, returnDom){
7355             var n = Roo.DomQuery.selectNode(selector, this.dom);
7356             return returnDom ? n : Roo.get(n);
7357         },
7358
7359         /**
7360          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7361          * @param {String} selector The CSS selector
7362          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7363          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7364          */
7365         down : function(selector, returnDom){
7366             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7367             return returnDom ? n : Roo.get(n);
7368         },
7369
7370         /**
7371          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7372          * @param {String} group The group the DD object is member of
7373          * @param {Object} config The DD config object
7374          * @param {Object} overrides An object containing methods to override/implement on the DD object
7375          * @return {Roo.dd.DD} The DD object
7376          */
7377         initDD : function(group, config, overrides){
7378             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7379             return Roo.apply(dd, overrides);
7380         },
7381
7382         /**
7383          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7384          * @param {String} group The group the DDProxy object is member of
7385          * @param {Object} config The DDProxy config object
7386          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7387          * @return {Roo.dd.DDProxy} The DDProxy object
7388          */
7389         initDDProxy : function(group, config, overrides){
7390             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7391             return Roo.apply(dd, overrides);
7392         },
7393
7394         /**
7395          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7396          * @param {String} group The group the DDTarget object is member of
7397          * @param {Object} config The DDTarget config object
7398          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7399          * @return {Roo.dd.DDTarget} The DDTarget object
7400          */
7401         initDDTarget : function(group, config, overrides){
7402             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7403             return Roo.apply(dd, overrides);
7404         },
7405
7406         /**
7407          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7408          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7409          * @param {Boolean} visible Whether the element is visible
7410          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7411          * @return {Roo.Element} this
7412          */
7413          setVisible : function(visible, animate){
7414             if(!animate || !A){
7415                 if(this.visibilityMode == El.DISPLAY){
7416                     this.setDisplayed(visible);
7417                 }else{
7418                     this.fixDisplay();
7419                     this.dom.style.visibility = visible ? "visible" : "hidden";
7420                 }
7421             }else{
7422                 // closure for composites
7423                 var dom = this.dom;
7424                 var visMode = this.visibilityMode;
7425                 if(visible){
7426                     this.setOpacity(.01);
7427                     this.setVisible(true);
7428                 }
7429                 this.anim({opacity: { to: (visible?1:0) }},
7430                       this.preanim(arguments, 1),
7431                       null, .35, 'easeIn', function(){
7432                          if(!visible){
7433                              if(visMode == El.DISPLAY){
7434                                  dom.style.display = "none";
7435                              }else{
7436                                  dom.style.visibility = "hidden";
7437                              }
7438                              Roo.get(dom).setOpacity(1);
7439                          }
7440                      });
7441             }
7442             return this;
7443         },
7444
7445         /**
7446          * Returns true if display is not "none"
7447          * @return {Boolean}
7448          */
7449         isDisplayed : function() {
7450             return this.getStyle("display") != "none";
7451         },
7452
7453         /**
7454          * Toggles the element's visibility or display, depending on visibility mode.
7455          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7456          * @return {Roo.Element} this
7457          */
7458         toggle : function(animate){
7459             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7460             return this;
7461         },
7462
7463         /**
7464          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7465          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7466          * @return {Roo.Element} this
7467          */
7468         setDisplayed : function(value) {
7469             if(typeof value == "boolean"){
7470                value = value ? this.originalDisplay : "none";
7471             }
7472             this.setStyle("display", value);
7473             return this;
7474         },
7475
7476         /**
7477          * Tries to focus the element. Any exceptions are caught and ignored.
7478          * @return {Roo.Element} this
7479          */
7480         focus : function() {
7481             try{
7482                 this.dom.focus();
7483             }catch(e){}
7484             return this;
7485         },
7486
7487         /**
7488          * Tries to blur the element. Any exceptions are caught and ignored.
7489          * @return {Roo.Element} this
7490          */
7491         blur : function() {
7492             try{
7493                 this.dom.blur();
7494             }catch(e){}
7495             return this;
7496         },
7497
7498         /**
7499          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7500          * @param {String/Array} className The CSS class to add, or an array of classes
7501          * @return {Roo.Element} this
7502          */
7503         addClass : function(className){
7504             if(className instanceof Array){
7505                 for(var i = 0, len = className.length; i < len; i++) {
7506                     this.addClass(className[i]);
7507                 }
7508             }else{
7509                 if(className && !this.hasClass(className)){
7510                     this.dom.className = this.dom.className + " " + className;
7511                 }
7512             }
7513             return this;
7514         },
7515
7516         /**
7517          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7518          * @param {String/Array} className The CSS class to add, or an array of classes
7519          * @return {Roo.Element} this
7520          */
7521         radioClass : function(className){
7522             var siblings = this.dom.parentNode.childNodes;
7523             for(var i = 0; i < siblings.length; i++) {
7524                 var s = siblings[i];
7525                 if(s.nodeType == 1){
7526                     Roo.get(s).removeClass(className);
7527                 }
7528             }
7529             this.addClass(className);
7530             return this;
7531         },
7532
7533         /**
7534          * Removes one or more CSS classes from the element.
7535          * @param {String/Array} className The CSS class to remove, or an array of classes
7536          * @return {Roo.Element} this
7537          */
7538         removeClass : function(className){
7539             if(!className || !this.dom.className){
7540                 return this;
7541             }
7542             if(className instanceof Array){
7543                 for(var i = 0, len = className.length; i < len; i++) {
7544                     this.removeClass(className[i]);
7545                 }
7546             }else{
7547                 if(this.hasClass(className)){
7548                     var re = this.classReCache[className];
7549                     if (!re) {
7550                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7551                        this.classReCache[className] = re;
7552                     }
7553                     this.dom.className =
7554                         this.dom.className.replace(re, " ");
7555                 }
7556             }
7557             return this;
7558         },
7559
7560         // private
7561         classReCache: {},
7562
7563         /**
7564          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7565          * @param {String} className The CSS class to toggle
7566          * @return {Roo.Element} this
7567          */
7568         toggleClass : function(className){
7569             if(this.hasClass(className)){
7570                 this.removeClass(className);
7571             }else{
7572                 this.addClass(className);
7573             }
7574             return this;
7575         },
7576
7577         /**
7578          * Checks if the specified CSS class exists on this element's DOM node.
7579          * @param {String} className The CSS class to check for
7580          * @return {Boolean} True if the class exists, else false
7581          */
7582         hasClass : function(className){
7583             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7584         },
7585
7586         /**
7587          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7588          * @param {String} oldClassName The CSS class to replace
7589          * @param {String} newClassName The replacement CSS class
7590          * @return {Roo.Element} this
7591          */
7592         replaceClass : function(oldClassName, newClassName){
7593             this.removeClass(oldClassName);
7594             this.addClass(newClassName);
7595             return this;
7596         },
7597
7598         /**
7599          * Returns an object with properties matching the styles requested.
7600          * For example, el.getStyles('color', 'font-size', 'width') might return
7601          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7602          * @param {String} style1 A style name
7603          * @param {String} style2 A style name
7604          * @param {String} etc.
7605          * @return {Object} The style object
7606          */
7607         getStyles : function(){
7608             var a = arguments, len = a.length, r = {};
7609             for(var i = 0; i < len; i++){
7610                 r[a[i]] = this.getStyle(a[i]);
7611             }
7612             return r;
7613         },
7614
7615         /**
7616          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7617          * @param {String} property The style property whose value is returned.
7618          * @return {String} The current value of the style property for this element.
7619          */
7620         getStyle : function(){
7621             return view && view.getComputedStyle ?
7622                 function(prop){
7623                     var el = this.dom, v, cs, camel;
7624                     if(prop == 'float'){
7625                         prop = "cssFloat";
7626                     }
7627                     if(el.style && (v = el.style[prop])){
7628                         return v;
7629                     }
7630                     if(cs = view.getComputedStyle(el, "")){
7631                         if(!(camel = propCache[prop])){
7632                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7633                         }
7634                         return cs[camel];
7635                     }
7636                     return null;
7637                 } :
7638                 function(prop){
7639                     var el = this.dom, v, cs, camel;
7640                     if(prop == 'opacity'){
7641                         if(typeof el.style.filter == 'string'){
7642                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7643                             if(m){
7644                                 var fv = parseFloat(m[1]);
7645                                 if(!isNaN(fv)){
7646                                     return fv ? fv / 100 : 0;
7647                                 }
7648                             }
7649                         }
7650                         return 1;
7651                     }else if(prop == 'float'){
7652                         prop = "styleFloat";
7653                     }
7654                     if(!(camel = propCache[prop])){
7655                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7656                     }
7657                     if(v = el.style[camel]){
7658                         return v;
7659                     }
7660                     if(cs = el.currentStyle){
7661                         return cs[camel];
7662                     }
7663                     return null;
7664                 };
7665         }(),
7666
7667         /**
7668          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7669          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7670          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7671          * @return {Roo.Element} this
7672          */
7673         setStyle : function(prop, value){
7674             if(typeof prop == "string"){
7675                 
7676                 if (prop == 'float') {
7677                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7678                     return this;
7679                 }
7680                 
7681                 var camel;
7682                 if(!(camel = propCache[prop])){
7683                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7684                 }
7685                 
7686                 if(camel == 'opacity') {
7687                     this.setOpacity(value);
7688                 }else{
7689                     this.dom.style[camel] = value;
7690                 }
7691             }else{
7692                 for(var style in prop){
7693                     if(typeof prop[style] != "function"){
7694                        this.setStyle(style, prop[style]);
7695                     }
7696                 }
7697             }
7698             return this;
7699         },
7700
7701         /**
7702          * More flexible version of {@link #setStyle} for setting style properties.
7703          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7704          * a function which returns such a specification.
7705          * @return {Roo.Element} this
7706          */
7707         applyStyles : function(style){
7708             Roo.DomHelper.applyStyles(this.dom, style);
7709             return this;
7710         },
7711
7712         /**
7713           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7714           * @return {Number} The X position of the element
7715           */
7716         getX : function(){
7717             return D.getX(this.dom);
7718         },
7719
7720         /**
7721           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7722           * @return {Number} The Y position of the element
7723           */
7724         getY : function(){
7725             return D.getY(this.dom);
7726         },
7727
7728         /**
7729           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7730           * @return {Array} The XY position of the element
7731           */
7732         getXY : function(){
7733             return D.getXY(this.dom);
7734         },
7735
7736         /**
7737          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7738          * @param {Number} The X position of the element
7739          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7740          * @return {Roo.Element} this
7741          */
7742         setX : function(x, animate){
7743             if(!animate || !A){
7744                 D.setX(this.dom, x);
7745             }else{
7746                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7747             }
7748             return this;
7749         },
7750
7751         /**
7752          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7753          * @param {Number} The Y position of the element
7754          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7755          * @return {Roo.Element} this
7756          */
7757         setY : function(y, animate){
7758             if(!animate || !A){
7759                 D.setY(this.dom, y);
7760             }else{
7761                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7762             }
7763             return this;
7764         },
7765
7766         /**
7767          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7768          * @param {String} left The left CSS property value
7769          * @return {Roo.Element} this
7770          */
7771         setLeft : function(left){
7772             this.setStyle("left", this.addUnits(left));
7773             return this;
7774         },
7775
7776         /**
7777          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7778          * @param {String} top The top CSS property value
7779          * @return {Roo.Element} this
7780          */
7781         setTop : function(top){
7782             this.setStyle("top", this.addUnits(top));
7783             return this;
7784         },
7785
7786         /**
7787          * Sets the element's CSS right style.
7788          * @param {String} right The right CSS property value
7789          * @return {Roo.Element} this
7790          */
7791         setRight : function(right){
7792             this.setStyle("right", this.addUnits(right));
7793             return this;
7794         },
7795
7796         /**
7797          * Sets the element's CSS bottom style.
7798          * @param {String} bottom The bottom CSS property value
7799          * @return {Roo.Element} this
7800          */
7801         setBottom : function(bottom){
7802             this.setStyle("bottom", this.addUnits(bottom));
7803             return this;
7804         },
7805
7806         /**
7807          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7808          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7809          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7810          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7811          * @return {Roo.Element} this
7812          */
7813         setXY : function(pos, animate){
7814             if(!animate || !A){
7815                 D.setXY(this.dom, pos);
7816             }else{
7817                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7818             }
7819             return this;
7820         },
7821
7822         /**
7823          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7824          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7825          * @param {Number} x X value for new position (coordinates are page-based)
7826          * @param {Number} y Y value for new position (coordinates are page-based)
7827          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7828          * @return {Roo.Element} this
7829          */
7830         setLocation : function(x, y, animate){
7831             this.setXY([x, y], this.preanim(arguments, 2));
7832             return this;
7833         },
7834
7835         /**
7836          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7837          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7838          * @param {Number} x X value for new position (coordinates are page-based)
7839          * @param {Number} y Y value for new position (coordinates are page-based)
7840          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7841          * @return {Roo.Element} this
7842          */
7843         moveTo : function(x, y, animate){
7844             this.setXY([x, y], this.preanim(arguments, 2));
7845             return this;
7846         },
7847
7848         /**
7849          * Returns the region of the given element.
7850          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7851          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7852          */
7853         getRegion : function(){
7854             return D.getRegion(this.dom);
7855         },
7856
7857         /**
7858          * Returns the offset height of the element
7859          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7860          * @return {Number} The element's height
7861          */
7862         getHeight : function(contentHeight){
7863             var h = this.dom.offsetHeight || 0;
7864             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7865         },
7866
7867         /**
7868          * Returns the offset width of the element
7869          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7870          * @return {Number} The element's width
7871          */
7872         getWidth : function(contentWidth){
7873             var w = this.dom.offsetWidth || 0;
7874             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7875         },
7876
7877         /**
7878          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7879          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7880          * if a height has not been set using CSS.
7881          * @return {Number}
7882          */
7883         getComputedHeight : function(){
7884             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7885             if(!h){
7886                 h = parseInt(this.getStyle('height'), 10) || 0;
7887                 if(!this.isBorderBox()){
7888                     h += this.getFrameWidth('tb');
7889                 }
7890             }
7891             return h;
7892         },
7893
7894         /**
7895          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7896          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7897          * if a width has not been set using CSS.
7898          * @return {Number}
7899          */
7900         getComputedWidth : function(){
7901             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7902             if(!w){
7903                 w = parseInt(this.getStyle('width'), 10) || 0;
7904                 if(!this.isBorderBox()){
7905                     w += this.getFrameWidth('lr');
7906                 }
7907             }
7908             return w;
7909         },
7910
7911         /**
7912          * Returns the size of the element.
7913          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7914          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7915          */
7916         getSize : function(contentSize){
7917             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7918         },
7919
7920         /**
7921          * Returns the width and height of the viewport.
7922          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7923          */
7924         getViewSize : function(){
7925             var d = this.dom, doc = document, aw = 0, ah = 0;
7926             if(d == doc || d == doc.body){
7927                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7928             }else{
7929                 return {
7930                     width : d.clientWidth,
7931                     height: d.clientHeight
7932                 };
7933             }
7934         },
7935
7936         /**
7937          * Returns the value of the "value" attribute
7938          * @param {Boolean} asNumber true to parse the value as a number
7939          * @return {String/Number}
7940          */
7941         getValue : function(asNumber){
7942             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7943         },
7944
7945         // private
7946         adjustWidth : function(width){
7947             if(typeof width == "number"){
7948                 if(this.autoBoxAdjust && !this.isBorderBox()){
7949                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7950                 }
7951                 if(width < 0){
7952                     width = 0;
7953                 }
7954             }
7955             return width;
7956         },
7957
7958         // private
7959         adjustHeight : function(height){
7960             if(typeof height == "number"){
7961                if(this.autoBoxAdjust && !this.isBorderBox()){
7962                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7963                }
7964                if(height < 0){
7965                    height = 0;
7966                }
7967             }
7968             return height;
7969         },
7970
7971         /**
7972          * Set the width of the element
7973          * @param {Number} width The new width
7974          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7975          * @return {Roo.Element} this
7976          */
7977         setWidth : function(width, animate){
7978             width = this.adjustWidth(width);
7979             if(!animate || !A){
7980                 this.dom.style.width = this.addUnits(width);
7981             }else{
7982                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7983             }
7984             return this;
7985         },
7986
7987         /**
7988          * Set the height of the element
7989          * @param {Number} height The new height
7990          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7991          * @return {Roo.Element} this
7992          */
7993          setHeight : function(height, animate){
7994             height = this.adjustHeight(height);
7995             if(!animate || !A){
7996                 this.dom.style.height = this.addUnits(height);
7997             }else{
7998                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7999             }
8000             return this;
8001         },
8002
8003         /**
8004          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8005          * @param {Number} width The new width
8006          * @param {Number} height The new height
8007          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8008          * @return {Roo.Element} this
8009          */
8010          setSize : function(width, height, animate){
8011             if(typeof width == "object"){ // in case of object from getSize()
8012                 height = width.height; width = width.width;
8013             }
8014             width = this.adjustWidth(width); height = this.adjustHeight(height);
8015             if(!animate || !A){
8016                 this.dom.style.width = this.addUnits(width);
8017                 this.dom.style.height = this.addUnits(height);
8018             }else{
8019                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8020             }
8021             return this;
8022         },
8023
8024         /**
8025          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8026          * @param {Number} x X value for new position (coordinates are page-based)
8027          * @param {Number} y Y value for new position (coordinates are page-based)
8028          * @param {Number} width The new width
8029          * @param {Number} height The new height
8030          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8031          * @return {Roo.Element} this
8032          */
8033         setBounds : function(x, y, width, height, animate){
8034             if(!animate || !A){
8035                 this.setSize(width, height);
8036                 this.setLocation(x, y);
8037             }else{
8038                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8039                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8040                               this.preanim(arguments, 4), 'motion');
8041             }
8042             return this;
8043         },
8044
8045         /**
8046          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
8047          * @param {Roo.lib.Region} region The region to fill
8048          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8049          * @return {Roo.Element} this
8050          */
8051         setRegion : function(region, animate){
8052             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8053             return this;
8054         },
8055
8056         /**
8057          * Appends an event handler
8058          *
8059          * @param {String}   eventName     The type of event to append
8060          * @param {Function} fn        The method the event invokes
8061          * @param {Object} scope       (optional) The scope (this object) of the fn
8062          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8063          */
8064         addListener : function(eventName, fn, scope, options){
8065             if (this.dom) {
8066                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8067             }
8068         },
8069
8070         /**
8071          * Removes an event handler from this element
8072          * @param {String} eventName the type of event to remove
8073          * @param {Function} fn the method the event invokes
8074          * @return {Roo.Element} this
8075          */
8076         removeListener : function(eventName, fn){
8077             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8078             return this;
8079         },
8080
8081         /**
8082          * Removes all previous added listeners from this element
8083          * @return {Roo.Element} this
8084          */
8085         removeAllListeners : function(){
8086             E.purgeElement(this.dom);
8087             return this;
8088         },
8089
8090         relayEvent : function(eventName, observable){
8091             this.on(eventName, function(e){
8092                 observable.fireEvent(eventName, e);
8093             });
8094         },
8095
8096         /**
8097          * Set the opacity of the element
8098          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8099          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8100          * @return {Roo.Element} this
8101          */
8102          setOpacity : function(opacity, animate){
8103             if(!animate || !A){
8104                 var s = this.dom.style;
8105                 if(Roo.isIE){
8106                     s.zoom = 1;
8107                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8108                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8109                 }else{
8110                     s.opacity = opacity;
8111                 }
8112             }else{
8113                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8114             }
8115             return this;
8116         },
8117
8118         /**
8119          * Gets the left X coordinate
8120          * @param {Boolean} local True to get the local css position instead of page coordinate
8121          * @return {Number}
8122          */
8123         getLeft : function(local){
8124             if(!local){
8125                 return this.getX();
8126             }else{
8127                 return parseInt(this.getStyle("left"), 10) || 0;
8128             }
8129         },
8130
8131         /**
8132          * Gets the right X coordinate of the element (element X position + element width)
8133          * @param {Boolean} local True to get the local css position instead of page coordinate
8134          * @return {Number}
8135          */
8136         getRight : function(local){
8137             if(!local){
8138                 return this.getX() + this.getWidth();
8139             }else{
8140                 return (this.getLeft(true) + this.getWidth()) || 0;
8141             }
8142         },
8143
8144         /**
8145          * Gets the top Y coordinate
8146          * @param {Boolean} local True to get the local css position instead of page coordinate
8147          * @return {Number}
8148          */
8149         getTop : function(local) {
8150             if(!local){
8151                 return this.getY();
8152             }else{
8153                 return parseInt(this.getStyle("top"), 10) || 0;
8154             }
8155         },
8156
8157         /**
8158          * Gets the bottom Y coordinate of the element (element Y position + element height)
8159          * @param {Boolean} local True to get the local css position instead of page coordinate
8160          * @return {Number}
8161          */
8162         getBottom : function(local){
8163             if(!local){
8164                 return this.getY() + this.getHeight();
8165             }else{
8166                 return (this.getTop(true) + this.getHeight()) || 0;
8167             }
8168         },
8169
8170         /**
8171         * Initializes positioning on this element. If a desired position is not passed, it will make the
8172         * the element positioned relative IF it is not already positioned.
8173         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8174         * @param {Number} zIndex (optional) The zIndex to apply
8175         * @param {Number} x (optional) Set the page X position
8176         * @param {Number} y (optional) Set the page Y position
8177         */
8178         position : function(pos, zIndex, x, y){
8179             if(!pos){
8180                if(this.getStyle('position') == 'static'){
8181                    this.setStyle('position', 'relative');
8182                }
8183             }else{
8184                 this.setStyle("position", pos);
8185             }
8186             if(zIndex){
8187                 this.setStyle("z-index", zIndex);
8188             }
8189             if(x !== undefined && y !== undefined){
8190                 this.setXY([x, y]);
8191             }else if(x !== undefined){
8192                 this.setX(x);
8193             }else if(y !== undefined){
8194                 this.setY(y);
8195             }
8196         },
8197
8198         /**
8199         * Clear positioning back to the default when the document was loaded
8200         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8201         * @return {Roo.Element} this
8202          */
8203         clearPositioning : function(value){
8204             value = value ||'';
8205             this.setStyle({
8206                 "left": value,
8207                 "right": value,
8208                 "top": value,
8209                 "bottom": value,
8210                 "z-index": "",
8211                 "position" : "static"
8212             });
8213             return this;
8214         },
8215
8216         /**
8217         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8218         * snapshot before performing an update and then restoring the element.
8219         * @return {Object}
8220         */
8221         getPositioning : function(){
8222             var l = this.getStyle("left");
8223             var t = this.getStyle("top");
8224             return {
8225                 "position" : this.getStyle("position"),
8226                 "left" : l,
8227                 "right" : l ? "" : this.getStyle("right"),
8228                 "top" : t,
8229                 "bottom" : t ? "" : this.getStyle("bottom"),
8230                 "z-index" : this.getStyle("z-index")
8231             };
8232         },
8233
8234         /**
8235          * Gets the width of the border(s) for the specified side(s)
8236          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8237          * passing lr would get the border (l)eft width + the border (r)ight width.
8238          * @return {Number} The width of the sides passed added together
8239          */
8240         getBorderWidth : function(side){
8241             return this.addStyles(side, El.borders);
8242         },
8243
8244         /**
8245          * Gets the width of the padding(s) for the specified side(s)
8246          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8247          * passing lr would get the padding (l)eft + the padding (r)ight.
8248          * @return {Number} The padding of the sides passed added together
8249          */
8250         getPadding : function(side){
8251             return this.addStyles(side, El.paddings);
8252         },
8253
8254         /**
8255         * Set positioning with an object returned by getPositioning().
8256         * @param {Object} posCfg
8257         * @return {Roo.Element} this
8258          */
8259         setPositioning : function(pc){
8260             this.applyStyles(pc);
8261             if(pc.right == "auto"){
8262                 this.dom.style.right = "";
8263             }
8264             if(pc.bottom == "auto"){
8265                 this.dom.style.bottom = "";
8266             }
8267             return this;
8268         },
8269
8270         // private
8271         fixDisplay : function(){
8272             if(this.getStyle("display") == "none"){
8273                 this.setStyle("visibility", "hidden");
8274                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8275                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8276                     this.setStyle("display", "block");
8277                 }
8278             }
8279         },
8280
8281         /**
8282          * Quick set left and top adding default units
8283          * @param {String} left The left CSS property value
8284          * @param {String} top The top CSS property value
8285          * @return {Roo.Element} this
8286          */
8287          setLeftTop : function(left, top){
8288             this.dom.style.left = this.addUnits(left);
8289             this.dom.style.top = this.addUnits(top);
8290             return this;
8291         },
8292
8293         /**
8294          * Move this element relative to its current position.
8295          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8296          * @param {Number} distance How far to move the element in pixels
8297          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8298          * @return {Roo.Element} this
8299          */
8300          move : function(direction, distance, animate){
8301             var xy = this.getXY();
8302             direction = direction.toLowerCase();
8303             switch(direction){
8304                 case "l":
8305                 case "left":
8306                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8307                     break;
8308                case "r":
8309                case "right":
8310                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8311                     break;
8312                case "t":
8313                case "top":
8314                case "up":
8315                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8316                     break;
8317                case "b":
8318                case "bottom":
8319                case "down":
8320                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8321                     break;
8322             }
8323             return this;
8324         },
8325
8326         /**
8327          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8328          * @return {Roo.Element} this
8329          */
8330         clip : function(){
8331             if(!this.isClipped){
8332                this.isClipped = true;
8333                this.originalClip = {
8334                    "o": this.getStyle("overflow"),
8335                    "x": this.getStyle("overflow-x"),
8336                    "y": this.getStyle("overflow-y")
8337                };
8338                this.setStyle("overflow", "hidden");
8339                this.setStyle("overflow-x", "hidden");
8340                this.setStyle("overflow-y", "hidden");
8341             }
8342             return this;
8343         },
8344
8345         /**
8346          *  Return clipping (overflow) to original clipping before clip() was called
8347          * @return {Roo.Element} this
8348          */
8349         unclip : function(){
8350             if(this.isClipped){
8351                 this.isClipped = false;
8352                 var o = this.originalClip;
8353                 if(o.o){this.setStyle("overflow", o.o);}
8354                 if(o.x){this.setStyle("overflow-x", o.x);}
8355                 if(o.y){this.setStyle("overflow-y", o.y);}
8356             }
8357             return this;
8358         },
8359
8360
8361         /**
8362          * Gets the x,y coordinates specified by the anchor position on the element.
8363          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8364          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8365          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8366          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8367          * @return {Array} [x, y] An array containing the element's x and y coordinates
8368          */
8369         getAnchorXY : function(anchor, local, s){
8370             //Passing a different size is useful for pre-calculating anchors,
8371             //especially for anchored animations that change the el size.
8372
8373             var w, h, vp = false;
8374             if(!s){
8375                 var d = this.dom;
8376                 if(d == document.body || d == document){
8377                     vp = true;
8378                     w = D.getViewWidth(); h = D.getViewHeight();
8379                 }else{
8380                     w = this.getWidth(); h = this.getHeight();
8381                 }
8382             }else{
8383                 w = s.width;  h = s.height;
8384             }
8385             var x = 0, y = 0, r = Math.round;
8386             switch((anchor || "tl").toLowerCase()){
8387                 case "c":
8388                     x = r(w*.5);
8389                     y = r(h*.5);
8390                 break;
8391                 case "t":
8392                     x = r(w*.5);
8393                     y = 0;
8394                 break;
8395                 case "l":
8396                     x = 0;
8397                     y = r(h*.5);
8398                 break;
8399                 case "r":
8400                     x = w;
8401                     y = r(h*.5);
8402                 break;
8403                 case "b":
8404                     x = r(w*.5);
8405                     y = h;
8406                 break;
8407                 case "tl":
8408                     x = 0;
8409                     y = 0;
8410                 break;
8411                 case "bl":
8412                     x = 0;
8413                     y = h;
8414                 break;
8415                 case "br":
8416                     x = w;
8417                     y = h;
8418                 break;
8419                 case "tr":
8420                     x = w;
8421                     y = 0;
8422                 break;
8423             }
8424             if(local === true){
8425                 return [x, y];
8426             }
8427             if(vp){
8428                 var sc = this.getScroll();
8429                 return [x + sc.left, y + sc.top];
8430             }
8431             //Add the element's offset xy
8432             var o = this.getXY();
8433             return [x+o[0], y+o[1]];
8434         },
8435
8436         /**
8437          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8438          * supported position values.
8439          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8440          * @param {String} position The position to align to.
8441          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8442          * @return {Array} [x, y]
8443          */
8444         getAlignToXY : function(el, p, o){
8445             el = Roo.get(el);
8446             var d = this.dom;
8447             if(!el.dom){
8448                 throw "Element.alignTo with an element that doesn't exist";
8449             }
8450             var c = false; //constrain to viewport
8451             var p1 = "", p2 = "";
8452             o = o || [0,0];
8453
8454             if(!p){
8455                 p = "tl-bl";
8456             }else if(p == "?"){
8457                 p = "tl-bl?";
8458             }else if(p.indexOf("-") == -1){
8459                 p = "tl-" + p;
8460             }
8461             p = p.toLowerCase();
8462             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8463             if(!m){
8464                throw "Element.alignTo with an invalid alignment " + p;
8465             }
8466             p1 = m[1]; p2 = m[2]; c = !!m[3];
8467
8468             //Subtract the aligned el's internal xy from the target's offset xy
8469             //plus custom offset to get the aligned el's new offset xy
8470             var a1 = this.getAnchorXY(p1, true);
8471             var a2 = el.getAnchorXY(p2, false);
8472             var x = a2[0] - a1[0] + o[0];
8473             var y = a2[1] - a1[1] + o[1];
8474             if(c){
8475                 //constrain the aligned el to viewport if necessary
8476                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8477                 // 5px of margin for ie
8478                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8479
8480                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8481                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8482                 //otherwise swap the aligned el to the opposite border of the target.
8483                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8484                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8485                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8486                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8487
8488                var doc = document;
8489                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8490                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8491
8492                if((x+w) > dw + scrollX){
8493                     x = swapX ? r.left-w : dw+scrollX-w;
8494                 }
8495                if(x < scrollX){
8496                    x = swapX ? r.right : scrollX;
8497                }
8498                if((y+h) > dh + scrollY){
8499                     y = swapY ? r.top-h : dh+scrollY-h;
8500                 }
8501                if (y < scrollY){
8502                    y = swapY ? r.bottom : scrollY;
8503                }
8504             }
8505             return [x,y];
8506         },
8507
8508         // private
8509         getConstrainToXY : function(){
8510             var os = {top:0, left:0, bottom:0, right: 0};
8511
8512             return function(el, local, offsets, proposedXY){
8513                 el = Roo.get(el);
8514                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8515
8516                 var vw, vh, vx = 0, vy = 0;
8517                 if(el.dom == document.body || el.dom == document){
8518                     vw = Roo.lib.Dom.getViewWidth();
8519                     vh = Roo.lib.Dom.getViewHeight();
8520                 }else{
8521                     vw = el.dom.clientWidth;
8522                     vh = el.dom.clientHeight;
8523                     if(!local){
8524                         var vxy = el.getXY();
8525                         vx = vxy[0];
8526                         vy = vxy[1];
8527                     }
8528                 }
8529
8530                 var s = el.getScroll();
8531
8532                 vx += offsets.left + s.left;
8533                 vy += offsets.top + s.top;
8534
8535                 vw -= offsets.right;
8536                 vh -= offsets.bottom;
8537
8538                 var vr = vx+vw;
8539                 var vb = vy+vh;
8540
8541                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8542                 var x = xy[0], y = xy[1];
8543                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8544
8545                 // only move it if it needs it
8546                 var moved = false;
8547
8548                 // first validate right/bottom
8549                 if((x + w) > vr){
8550                     x = vr - w;
8551                     moved = true;
8552                 }
8553                 if((y + h) > vb){
8554                     y = vb - h;
8555                     moved = true;
8556                 }
8557                 // then make sure top/left isn't negative
8558                 if(x < vx){
8559                     x = vx;
8560                     moved = true;
8561                 }
8562                 if(y < vy){
8563                     y = vy;
8564                     moved = true;
8565                 }
8566                 return moved ? [x, y] : false;
8567             };
8568         }(),
8569
8570         // private
8571         adjustForConstraints : function(xy, parent, offsets){
8572             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8573         },
8574
8575         /**
8576          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8577          * document it aligns it to the viewport.
8578          * The position parameter is optional, and can be specified in any one of the following formats:
8579          * <ul>
8580          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8581          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8582          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8583          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8584          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8585          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8586          * </ul>
8587          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8588          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8589          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8590          * that specified in order to enforce the viewport constraints.
8591          * Following are all of the supported anchor positions:
8592     <pre>
8593     Value  Description
8594     -----  -----------------------------
8595     tl     The top left corner (default)
8596     t      The center of the top edge
8597     tr     The top right corner
8598     l      The center of the left edge
8599     c      In the center of the element
8600     r      The center of the right edge
8601     bl     The bottom left corner
8602     b      The center of the bottom edge
8603     br     The bottom right corner
8604     </pre>
8605     Example Usage:
8606     <pre><code>
8607     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8608     el.alignTo("other-el");
8609
8610     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8611     el.alignTo("other-el", "tr?");
8612
8613     // align the bottom right corner of el with the center left edge of other-el
8614     el.alignTo("other-el", "br-l?");
8615
8616     // align the center of el with the bottom left corner of other-el and
8617     // adjust the x position by -6 pixels (and the y position by 0)
8618     el.alignTo("other-el", "c-bl", [-6, 0]);
8619     </code></pre>
8620          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8621          * @param {String} position The position to align to.
8622          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8623          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8624          * @return {Roo.Element} this
8625          */
8626         alignTo : function(element, position, offsets, animate){
8627             var xy = this.getAlignToXY(element, position, offsets);
8628             this.setXY(xy, this.preanim(arguments, 3));
8629             return this;
8630         },
8631
8632         /**
8633          * Anchors an element to another element and realigns it when the window is resized.
8634          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8635          * @param {String} position The position to align to.
8636          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8637          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8638          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8639          * is a number, it is used as the buffer delay (defaults to 50ms).
8640          * @param {Function} callback The function to call after the animation finishes
8641          * @return {Roo.Element} this
8642          */
8643         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8644             var action = function(){
8645                 this.alignTo(el, alignment, offsets, animate);
8646                 Roo.callback(callback, this);
8647             };
8648             Roo.EventManager.onWindowResize(action, this);
8649             var tm = typeof monitorScroll;
8650             if(tm != 'undefined'){
8651                 Roo.EventManager.on(window, 'scroll', action, this,
8652                     {buffer: tm == 'number' ? monitorScroll : 50});
8653             }
8654             action.call(this); // align immediately
8655             return this;
8656         },
8657         /**
8658          * Clears any opacity settings from this element. Required in some cases for IE.
8659          * @return {Roo.Element} this
8660          */
8661         clearOpacity : function(){
8662             if (window.ActiveXObject) {
8663                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8664                     this.dom.style.filter = "";
8665                 }
8666             } else {
8667                 this.dom.style.opacity = "";
8668                 this.dom.style["-moz-opacity"] = "";
8669                 this.dom.style["-khtml-opacity"] = "";
8670             }
8671             return this;
8672         },
8673
8674         /**
8675          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8676          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8677          * @return {Roo.Element} this
8678          */
8679         hide : function(animate){
8680             this.setVisible(false, this.preanim(arguments, 0));
8681             return this;
8682         },
8683
8684         /**
8685         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8686         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8687          * @return {Roo.Element} this
8688          */
8689         show : function(animate){
8690             this.setVisible(true, this.preanim(arguments, 0));
8691             return this;
8692         },
8693
8694         /**
8695          * @private Test if size has a unit, otherwise appends the default
8696          */
8697         addUnits : function(size){
8698             return Roo.Element.addUnits(size, this.defaultUnit);
8699         },
8700
8701         /**
8702          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8703          * @return {Roo.Element} this
8704          */
8705         beginMeasure : function(){
8706             var el = this.dom;
8707             if(el.offsetWidth || el.offsetHeight){
8708                 return this; // offsets work already
8709             }
8710             var changed = [];
8711             var p = this.dom, b = document.body; // start with this element
8712             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8713                 var pe = Roo.get(p);
8714                 if(pe.getStyle('display') == 'none'){
8715                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8716                     p.style.visibility = "hidden";
8717                     p.style.display = "block";
8718                 }
8719                 p = p.parentNode;
8720             }
8721             this._measureChanged = changed;
8722             return this;
8723
8724         },
8725
8726         /**
8727          * Restores displays to before beginMeasure was called
8728          * @return {Roo.Element} this
8729          */
8730         endMeasure : function(){
8731             var changed = this._measureChanged;
8732             if(changed){
8733                 for(var i = 0, len = changed.length; i < len; i++) {
8734                     var r = changed[i];
8735                     r.el.style.visibility = r.visibility;
8736                     r.el.style.display = "none";
8737                 }
8738                 this._measureChanged = null;
8739             }
8740             return this;
8741         },
8742
8743         /**
8744         * Update the innerHTML of this element, optionally searching for and processing scripts
8745         * @param {String} html The new HTML
8746         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8747         * @param {Function} callback For async script loading you can be noticed when the update completes
8748         * @return {Roo.Element} this
8749          */
8750         update : function(html, loadScripts, callback){
8751             if(typeof html == "undefined"){
8752                 html = "";
8753             }
8754             if(loadScripts !== true){
8755                 this.dom.innerHTML = html;
8756                 if(typeof callback == "function"){
8757                     callback();
8758                 }
8759                 return this;
8760             }
8761             var id = Roo.id();
8762             var dom = this.dom;
8763
8764             html += '<span id="' + id + '"></span>';
8765
8766             E.onAvailable(id, function(){
8767                 var hd = document.getElementsByTagName("head")[0];
8768                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8769                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8770                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8771
8772                 var match;
8773                 while(match = re.exec(html)){
8774                     var attrs = match[1];
8775                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8776                     if(srcMatch && srcMatch[2]){
8777                        var s = document.createElement("script");
8778                        s.src = srcMatch[2];
8779                        var typeMatch = attrs.match(typeRe);
8780                        if(typeMatch && typeMatch[2]){
8781                            s.type = typeMatch[2];
8782                        }
8783                        hd.appendChild(s);
8784                     }else if(match[2] && match[2].length > 0){
8785                         if(window.execScript) {
8786                            window.execScript(match[2]);
8787                         } else {
8788                             /**
8789                              * eval:var:id
8790                              * eval:var:dom
8791                              * eval:var:html
8792                              * 
8793                              */
8794                            window.eval(match[2]);
8795                         }
8796                     }
8797                 }
8798                 var el = document.getElementById(id);
8799                 if(el){el.parentNode.removeChild(el);}
8800                 if(typeof callback == "function"){
8801                     callback();
8802                 }
8803             });
8804             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8805             return this;
8806         },
8807
8808         /**
8809          * Direct access to the UpdateManager update() method (takes the same parameters).
8810          * @param {String/Function} url The url for this request or a function to call to get the url
8811          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8812          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8813          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8814          * @return {Roo.Element} this
8815          */
8816         load : function(){
8817             var um = this.getUpdateManager();
8818             um.update.apply(um, arguments);
8819             return this;
8820         },
8821
8822         /**
8823         * Gets this element's UpdateManager
8824         * @return {Roo.UpdateManager} The UpdateManager
8825         */
8826         getUpdateManager : function(){
8827             if(!this.updateManager){
8828                 this.updateManager = new Roo.UpdateManager(this);
8829             }
8830             return this.updateManager;
8831         },
8832
8833         /**
8834          * Disables text selection for this element (normalized across browsers)
8835          * @return {Roo.Element} this
8836          */
8837         unselectable : function(){
8838             this.dom.unselectable = "on";
8839             this.swallowEvent("selectstart", true);
8840             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8841             this.addClass("x-unselectable");
8842             return this;
8843         },
8844
8845         /**
8846         * Calculates the x, y to center this element on the screen
8847         * @return {Array} The x, y values [x, y]
8848         */
8849         getCenterXY : function(){
8850             return this.getAlignToXY(document, 'c-c');
8851         },
8852
8853         /**
8854         * Centers the Element in either the viewport, or another Element.
8855         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8856         */
8857         center : function(centerIn){
8858             this.alignTo(centerIn || document, 'c-c');
8859             return this;
8860         },
8861
8862         /**
8863          * Tests various css rules/browsers to determine if this element uses a border box
8864          * @return {Boolean}
8865          */
8866         isBorderBox : function(){
8867             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8868         },
8869
8870         /**
8871          * Return a box {x, y, width, height} that can be used to set another elements
8872          * size/location to match this element.
8873          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8874          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8875          * @return {Object} box An object in the format {x, y, width, height}
8876          */
8877         getBox : function(contentBox, local){
8878             var xy;
8879             if(!local){
8880                 xy = this.getXY();
8881             }else{
8882                 var left = parseInt(this.getStyle("left"), 10) || 0;
8883                 var top = parseInt(this.getStyle("top"), 10) || 0;
8884                 xy = [left, top];
8885             }
8886             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8887             if(!contentBox){
8888                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8889             }else{
8890                 var l = this.getBorderWidth("l")+this.getPadding("l");
8891                 var r = this.getBorderWidth("r")+this.getPadding("r");
8892                 var t = this.getBorderWidth("t")+this.getPadding("t");
8893                 var b = this.getBorderWidth("b")+this.getPadding("b");
8894                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8895             }
8896             bx.right = bx.x + bx.width;
8897             bx.bottom = bx.y + bx.height;
8898             return bx;
8899         },
8900
8901         /**
8902          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8903          for more information about the sides.
8904          * @param {String} sides
8905          * @return {Number}
8906          */
8907         getFrameWidth : function(sides, onlyContentBox){
8908             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8909         },
8910
8911         /**
8912          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8913          * @param {Object} box The box to fill {x, y, width, height}
8914          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8915          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8916          * @return {Roo.Element} this
8917          */
8918         setBox : function(box, adjust, animate){
8919             var w = box.width, h = box.height;
8920             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8921                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8922                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8923             }
8924             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8925             return this;
8926         },
8927
8928         /**
8929          * Forces the browser to repaint this element
8930          * @return {Roo.Element} this
8931          */
8932          repaint : function(){
8933             var dom = this.dom;
8934             this.addClass("x-repaint");
8935             setTimeout(function(){
8936                 Roo.get(dom).removeClass("x-repaint");
8937             }, 1);
8938             return this;
8939         },
8940
8941         /**
8942          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8943          * then it returns the calculated width of the sides (see getPadding)
8944          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8945          * @return {Object/Number}
8946          */
8947         getMargins : function(side){
8948             if(!side){
8949                 return {
8950                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8951                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8952                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8953                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8954                 };
8955             }else{
8956                 return this.addStyles(side, El.margins);
8957              }
8958         },
8959
8960         // private
8961         addStyles : function(sides, styles){
8962             var val = 0, v, w;
8963             for(var i = 0, len = sides.length; i < len; i++){
8964                 v = this.getStyle(styles[sides.charAt(i)]);
8965                 if(v){
8966                      w = parseInt(v, 10);
8967                      if(w){ val += w; }
8968                 }
8969             }
8970             return val;
8971         },
8972
8973         /**
8974          * Creates a proxy element of this element
8975          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8976          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8977          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8978          * @return {Roo.Element} The new proxy element
8979          */
8980         createProxy : function(config, renderTo, matchBox){
8981             if(renderTo){
8982                 renderTo = Roo.getDom(renderTo);
8983             }else{
8984                 renderTo = document.body;
8985             }
8986             config = typeof config == "object" ?
8987                 config : {tag : "div", cls: config};
8988             var proxy = Roo.DomHelper.append(renderTo, config, true);
8989             if(matchBox){
8990                proxy.setBox(this.getBox());
8991             }
8992             return proxy;
8993         },
8994
8995         /**
8996          * Puts a mask over this element to disable user interaction. Requires core.css.
8997          * This method can only be applied to elements which accept child nodes.
8998          * @param {String} msg (optional) A message to display in the mask
8999          * @param {String} msgCls (optional) A css class to apply to the msg element
9000          * @return {Element} The mask  element
9001          */
9002         mask : function(msg, msgCls)
9003         {
9004             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9005                 this.setStyle("position", "relative");
9006             }
9007             if(!this._mask){
9008                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9009             }
9010             this.addClass("x-masked");
9011             this._mask.setDisplayed(true);
9012             
9013             // we wander
9014             var z = 0;
9015             var dom = this.dom;
9016             while (dom && dom.style) {
9017                 if (!isNaN(parseInt(dom.style.zIndex))) {
9018                     z = Math.max(z, parseInt(dom.style.zIndex));
9019                 }
9020                 dom = dom.parentNode;
9021             }
9022             // if we are masking the body - then it hides everything..
9023             if (this.dom == document.body) {
9024                 z = 1000000;
9025                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9026                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9027             }
9028            
9029             if(typeof msg == 'string'){
9030                 if(!this._maskMsg){
9031                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9032                 }
9033                 var mm = this._maskMsg;
9034                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9035                 if (mm.dom.firstChild) { // weird IE issue?
9036                     mm.dom.firstChild.innerHTML = msg;
9037                 }
9038                 mm.setDisplayed(true);
9039                 mm.center(this);
9040                 mm.setStyle('z-index', z + 102);
9041             }
9042             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9043                 this._mask.setHeight(this.getHeight());
9044             }
9045             this._mask.setStyle('z-index', z + 100);
9046             
9047             return this._mask;
9048         },
9049
9050         /**
9051          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9052          * it is cached for reuse.
9053          */
9054         unmask : function(removeEl){
9055             if(this._mask){
9056                 if(removeEl === true){
9057                     this._mask.remove();
9058                     delete this._mask;
9059                     if(this._maskMsg){
9060                         this._maskMsg.remove();
9061                         delete this._maskMsg;
9062                     }
9063                 }else{
9064                     this._mask.setDisplayed(false);
9065                     if(this._maskMsg){
9066                         this._maskMsg.setDisplayed(false);
9067                     }
9068                 }
9069             }
9070             this.removeClass("x-masked");
9071         },
9072
9073         /**
9074          * Returns true if this element is masked
9075          * @return {Boolean}
9076          */
9077         isMasked : function(){
9078             return this._mask && this._mask.isVisible();
9079         },
9080
9081         /**
9082          * Creates an iframe shim for this element to keep selects and other windowed objects from
9083          * showing through.
9084          * @return {Roo.Element} The new shim element
9085          */
9086         createShim : function(){
9087             var el = document.createElement('iframe');
9088             el.frameBorder = 'no';
9089             el.className = 'roo-shim';
9090             if(Roo.isIE && Roo.isSecure){
9091                 el.src = Roo.SSL_SECURE_URL;
9092             }
9093             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9094             shim.autoBoxAdjust = false;
9095             return shim;
9096         },
9097
9098         /**
9099          * Removes this element from the DOM and deletes it from the cache
9100          */
9101         remove : function(){
9102             if(this.dom.parentNode){
9103                 this.dom.parentNode.removeChild(this.dom);
9104             }
9105             delete El.cache[this.dom.id];
9106         },
9107
9108         /**
9109          * Sets up event handlers to add and remove a css class when the mouse is over this element
9110          * @param {String} className
9111          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9112          * mouseout events for children elements
9113          * @return {Roo.Element} this
9114          */
9115         addClassOnOver : function(className, preventFlicker){
9116             this.on("mouseover", function(){
9117                 Roo.fly(this, '_internal').addClass(className);
9118             }, this.dom);
9119             var removeFn = function(e){
9120                 if(preventFlicker !== true || !e.within(this, true)){
9121                     Roo.fly(this, '_internal').removeClass(className);
9122                 }
9123             };
9124             this.on("mouseout", removeFn, this.dom);
9125             return this;
9126         },
9127
9128         /**
9129          * Sets up event handlers to add and remove a css class when this element has the focus
9130          * @param {String} className
9131          * @return {Roo.Element} this
9132          */
9133         addClassOnFocus : function(className){
9134             this.on("focus", function(){
9135                 Roo.fly(this, '_internal').addClass(className);
9136             }, this.dom);
9137             this.on("blur", function(){
9138                 Roo.fly(this, '_internal').removeClass(className);
9139             }, this.dom);
9140             return this;
9141         },
9142         /**
9143          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
9144          * @param {String} className
9145          * @return {Roo.Element} this
9146          */
9147         addClassOnClick : function(className){
9148             var dom = this.dom;
9149             this.on("mousedown", function(){
9150                 Roo.fly(dom, '_internal').addClass(className);
9151                 var d = Roo.get(document);
9152                 var fn = function(){
9153                     Roo.fly(dom, '_internal').removeClass(className);
9154                     d.removeListener("mouseup", fn);
9155                 };
9156                 d.on("mouseup", fn);
9157             });
9158             return this;
9159         },
9160
9161         /**
9162          * Stops the specified event from bubbling and optionally prevents the default action
9163          * @param {String} eventName
9164          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9165          * @return {Roo.Element} this
9166          */
9167         swallowEvent : function(eventName, preventDefault){
9168             var fn = function(e){
9169                 e.stopPropagation();
9170                 if(preventDefault){
9171                     e.preventDefault();
9172                 }
9173             };
9174             if(eventName instanceof Array){
9175                 for(var i = 0, len = eventName.length; i < len; i++){
9176                      this.on(eventName[i], fn);
9177                 }
9178                 return this;
9179             }
9180             this.on(eventName, fn);
9181             return this;
9182         },
9183
9184         /**
9185          * @private
9186          */
9187       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9188
9189         /**
9190          * Sizes this element to its parent element's dimensions performing
9191          * neccessary box adjustments.
9192          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9193          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9194          * @return {Roo.Element} this
9195          */
9196         fitToParent : function(monitorResize, targetParent) {
9197           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9198           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9199           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9200             return;
9201           }
9202           var p = Roo.get(targetParent || this.dom.parentNode);
9203           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9204           if (monitorResize === true) {
9205             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9206             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9207           }
9208           return this;
9209         },
9210
9211         /**
9212          * Gets the next sibling, skipping text nodes
9213          * @return {HTMLElement} The next sibling or null
9214          */
9215         getNextSibling : function(){
9216             var n = this.dom.nextSibling;
9217             while(n && n.nodeType != 1){
9218                 n = n.nextSibling;
9219             }
9220             return n;
9221         },
9222
9223         /**
9224          * Gets the previous sibling, skipping text nodes
9225          * @return {HTMLElement} The previous sibling or null
9226          */
9227         getPrevSibling : function(){
9228             var n = this.dom.previousSibling;
9229             while(n && n.nodeType != 1){
9230                 n = n.previousSibling;
9231             }
9232             return n;
9233         },
9234
9235
9236         /**
9237          * Appends the passed element(s) to this element
9238          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9239          * @return {Roo.Element} this
9240          */
9241         appendChild: function(el){
9242             el = Roo.get(el);
9243             el.appendTo(this);
9244             return this;
9245         },
9246
9247         /**
9248          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9249          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9250          * automatically generated with the specified attributes.
9251          * @param {HTMLElement} insertBefore (optional) a child element of this element
9252          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9253          * @return {Roo.Element} The new child element
9254          */
9255         createChild: function(config, insertBefore, returnDom){
9256             config = config || {tag:'div'};
9257             if(insertBefore){
9258                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9259             }
9260             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9261         },
9262
9263         /**
9264          * Appends this element to the passed element
9265          * @param {String/HTMLElement/Element} el The new parent element
9266          * @return {Roo.Element} this
9267          */
9268         appendTo: function(el){
9269             el = Roo.getDom(el);
9270             el.appendChild(this.dom);
9271             return this;
9272         },
9273
9274         /**
9275          * Inserts this element before the passed element in the DOM
9276          * @param {String/HTMLElement/Element} el The element to insert before
9277          * @return {Roo.Element} this
9278          */
9279         insertBefore: function(el){
9280             el = Roo.getDom(el);
9281             el.parentNode.insertBefore(this.dom, el);
9282             return this;
9283         },
9284
9285         /**
9286          * Inserts this element after the passed element in the DOM
9287          * @param {String/HTMLElement/Element} el The element to insert after
9288          * @return {Roo.Element} this
9289          */
9290         insertAfter: function(el){
9291             el = Roo.getDom(el);
9292             el.parentNode.insertBefore(this.dom, el.nextSibling);
9293             return this;
9294         },
9295
9296         /**
9297          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9298          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9299          * @return {Roo.Element} The new child
9300          */
9301         insertFirst: function(el, returnDom){
9302             el = el || {};
9303             if(typeof el == 'object' && !el.nodeType){ // dh config
9304                 return this.createChild(el, this.dom.firstChild, returnDom);
9305             }else{
9306                 el = Roo.getDom(el);
9307                 this.dom.insertBefore(el, this.dom.firstChild);
9308                 return !returnDom ? Roo.get(el) : el;
9309             }
9310         },
9311
9312         /**
9313          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9314          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9315          * @param {String} where (optional) 'before' or 'after' defaults to before
9316          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9317          * @return {Roo.Element} the inserted Element
9318          */
9319         insertSibling: function(el, where, returnDom){
9320             where = where ? where.toLowerCase() : 'before';
9321             el = el || {};
9322             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9323
9324             if(typeof el == 'object' && !el.nodeType){ // dh config
9325                 if(where == 'after' && !this.dom.nextSibling){
9326                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9327                 }else{
9328                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9329                 }
9330
9331             }else{
9332                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9333                             where == 'before' ? this.dom : this.dom.nextSibling);
9334                 if(!returnDom){
9335                     rt = Roo.get(rt);
9336                 }
9337             }
9338             return rt;
9339         },
9340
9341         /**
9342          * Creates and wraps this element with another element
9343          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9344          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9345          * @return {HTMLElement/Element} The newly created wrapper element
9346          */
9347         wrap: function(config, returnDom){
9348             if(!config){
9349                 config = {tag: "div"};
9350             }
9351             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9352             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9353             return newEl;
9354         },
9355
9356         /**
9357          * Replaces the passed element with this element
9358          * @param {String/HTMLElement/Element} el The element to replace
9359          * @return {Roo.Element} this
9360          */
9361         replace: function(el){
9362             el = Roo.get(el);
9363             this.insertBefore(el);
9364             el.remove();
9365             return this;
9366         },
9367
9368         /**
9369          * Inserts an html fragment into this element
9370          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9371          * @param {String} html The HTML fragment
9372          * @param {Boolean} returnEl True to return an Roo.Element
9373          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9374          */
9375         insertHtml : function(where, html, returnEl){
9376             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9377             return returnEl ? Roo.get(el) : el;
9378         },
9379
9380         /**
9381          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9382          * @param {Object} o The object with the attributes
9383          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9384          * @return {Roo.Element} this
9385          */
9386         set : function(o, useSet){
9387             var el = this.dom;
9388             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9389             for(var attr in o){
9390                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9391                 if(attr=="cls"){
9392                     el.className = o["cls"];
9393                 }else{
9394                     if(useSet) {
9395                         el.setAttribute(attr, o[attr]);
9396                     } else {
9397                         el[attr] = o[attr];
9398                     }
9399                 }
9400             }
9401             if(o.style){
9402                 Roo.DomHelper.applyStyles(el, o.style);
9403             }
9404             return this;
9405         },
9406
9407         /**
9408          * Convenience method for constructing a KeyMap
9409          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9410          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9411          * @param {Function} fn The function to call
9412          * @param {Object} scope (optional) The scope of the function
9413          * @return {Roo.KeyMap} The KeyMap created
9414          */
9415         addKeyListener : function(key, fn, scope){
9416             var config;
9417             if(typeof key != "object" || key instanceof Array){
9418                 config = {
9419                     key: key,
9420                     fn: fn,
9421                     scope: scope
9422                 };
9423             }else{
9424                 config = {
9425                     key : key.key,
9426                     shift : key.shift,
9427                     ctrl : key.ctrl,
9428                     alt : key.alt,
9429                     fn: fn,
9430                     scope: scope
9431                 };
9432             }
9433             return new Roo.KeyMap(this, config);
9434         },
9435
9436         /**
9437          * Creates a KeyMap for this element
9438          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9439          * @return {Roo.KeyMap} The KeyMap created
9440          */
9441         addKeyMap : function(config){
9442             return new Roo.KeyMap(this, config);
9443         },
9444
9445         /**
9446          * Returns true if this element is scrollable.
9447          * @return {Boolean}
9448          */
9449          isScrollable : function(){
9450             var dom = this.dom;
9451             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9452         },
9453
9454         /**
9455          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9456          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9457          * @param {Number} value The new scroll value
9458          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9459          * @return {Element} this
9460          */
9461
9462         scrollTo : function(side, value, animate){
9463             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9464             if(!animate || !A){
9465                 this.dom[prop] = value;
9466             }else{
9467                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9468                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9469             }
9470             return this;
9471         },
9472
9473         /**
9474          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9475          * within this element's scrollable range.
9476          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9477          * @param {Number} distance How far to scroll the element in pixels
9478          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9479          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9480          * was scrolled as far as it could go.
9481          */
9482          scroll : function(direction, distance, animate){
9483              if(!this.isScrollable()){
9484                  return;
9485              }
9486              var el = this.dom;
9487              var l = el.scrollLeft, t = el.scrollTop;
9488              var w = el.scrollWidth, h = el.scrollHeight;
9489              var cw = el.clientWidth, ch = el.clientHeight;
9490              direction = direction.toLowerCase();
9491              var scrolled = false;
9492              var a = this.preanim(arguments, 2);
9493              switch(direction){
9494                  case "l":
9495                  case "left":
9496                      if(w - l > cw){
9497                          var v = Math.min(l + distance, w-cw);
9498                          this.scrollTo("left", v, a);
9499                          scrolled = true;
9500                      }
9501                      break;
9502                 case "r":
9503                 case "right":
9504                      if(l > 0){
9505                          var v = Math.max(l - distance, 0);
9506                          this.scrollTo("left", v, a);
9507                          scrolled = true;
9508                      }
9509                      break;
9510                 case "t":
9511                 case "top":
9512                 case "up":
9513                      if(t > 0){
9514                          var v = Math.max(t - distance, 0);
9515                          this.scrollTo("top", v, a);
9516                          scrolled = true;
9517                      }
9518                      break;
9519                 case "b":
9520                 case "bottom":
9521                 case "down":
9522                      if(h - t > ch){
9523                          var v = Math.min(t + distance, h-ch);
9524                          this.scrollTo("top", v, a);
9525                          scrolled = true;
9526                      }
9527                      break;
9528              }
9529              return scrolled;
9530         },
9531
9532         /**
9533          * Translates the passed page coordinates into left/top css values for this element
9534          * @param {Number/Array} x The page x or an array containing [x, y]
9535          * @param {Number} y The page y
9536          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9537          */
9538         translatePoints : function(x, y){
9539             if(typeof x == 'object' || x instanceof Array){
9540                 y = x[1]; x = x[0];
9541             }
9542             var p = this.getStyle('position');
9543             var o = this.getXY();
9544
9545             var l = parseInt(this.getStyle('left'), 10);
9546             var t = parseInt(this.getStyle('top'), 10);
9547
9548             if(isNaN(l)){
9549                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9550             }
9551             if(isNaN(t)){
9552                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9553             }
9554
9555             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9556         },
9557
9558         /**
9559          * Returns the current scroll position of the element.
9560          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9561          */
9562         getScroll : function(){
9563             var d = this.dom, doc = document;
9564             if(d == doc || d == doc.body){
9565                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9566                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9567                 return {left: l, top: t};
9568             }else{
9569                 return {left: d.scrollLeft, top: d.scrollTop};
9570             }
9571         },
9572
9573         /**
9574          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9575          * are convert to standard 6 digit hex color.
9576          * @param {String} attr The css attribute
9577          * @param {String} defaultValue The default value to use when a valid color isn't found
9578          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9579          * YUI color anims.
9580          */
9581         getColor : function(attr, defaultValue, prefix){
9582             var v = this.getStyle(attr);
9583             if(!v || v == "transparent" || v == "inherit") {
9584                 return defaultValue;
9585             }
9586             var color = typeof prefix == "undefined" ? "#" : prefix;
9587             if(v.substr(0, 4) == "rgb("){
9588                 var rvs = v.slice(4, v.length -1).split(",");
9589                 for(var i = 0; i < 3; i++){
9590                     var h = parseInt(rvs[i]).toString(16);
9591                     if(h < 16){
9592                         h = "0" + h;
9593                     }
9594                     color += h;
9595                 }
9596             } else {
9597                 if(v.substr(0, 1) == "#"){
9598                     if(v.length == 4) {
9599                         for(var i = 1; i < 4; i++){
9600                             var c = v.charAt(i);
9601                             color +=  c + c;
9602                         }
9603                     }else if(v.length == 7){
9604                         color += v.substr(1);
9605                     }
9606                 }
9607             }
9608             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9609         },
9610
9611         /**
9612          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9613          * gradient background, rounded corners and a 4-way shadow.
9614          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9615          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9616          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9617          * @return {Roo.Element} this
9618          */
9619         boxWrap : function(cls){
9620             cls = cls || 'x-box';
9621             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9622             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9623             return el;
9624         },
9625
9626         /**
9627          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9628          * @param {String} namespace The namespace in which to look for the attribute
9629          * @param {String} name The attribute name
9630          * @return {String} The attribute value
9631          */
9632         getAttributeNS : Roo.isIE ? function(ns, name){
9633             var d = this.dom;
9634             var type = typeof d[ns+":"+name];
9635             if(type != 'undefined' && type != 'unknown'){
9636                 return d[ns+":"+name];
9637             }
9638             return d[name];
9639         } : function(ns, name){
9640             var d = this.dom;
9641             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9642         },
9643         
9644         
9645         /**
9646          * Sets or Returns the value the dom attribute value
9647          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9648          * @param {String} value (optional) The value to set the attribute to
9649          * @return {String} The attribute value
9650          */
9651         attr : function(name){
9652             if (arguments.length > 1) {
9653                 this.dom.setAttribute(name, arguments[1]);
9654                 return arguments[1];
9655             }
9656             if (typeof(name) == 'object') {
9657                 for(var i in name) {
9658                     this.attr(i, name[i]);
9659                 }
9660                 return name;
9661             }
9662             
9663             
9664             if (!this.dom.hasAttribute(name)) {
9665                 return undefined;
9666             }
9667             return this.dom.getAttribute(name);
9668         }
9669         
9670         
9671         
9672     };
9673
9674     var ep = El.prototype;
9675
9676     /**
9677      * Appends an event handler (Shorthand for addListener)
9678      * @param {String}   eventName     The type of event to append
9679      * @param {Function} fn        The method the event invokes
9680      * @param {Object} scope       (optional) The scope (this object) of the fn
9681      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9682      * @method
9683      */
9684     ep.on = ep.addListener;
9685         // backwards compat
9686     ep.mon = ep.addListener;
9687
9688     /**
9689      * Removes an event handler from this element (shorthand for removeListener)
9690      * @param {String} eventName the type of event to remove
9691      * @param {Function} fn the method the event invokes
9692      * @return {Roo.Element} this
9693      * @method
9694      */
9695     ep.un = ep.removeListener;
9696
9697     /**
9698      * true to automatically adjust width and height settings for box-model issues (default to true)
9699      */
9700     ep.autoBoxAdjust = true;
9701
9702     // private
9703     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9704
9705     // private
9706     El.addUnits = function(v, defaultUnit){
9707         if(v === "" || v == "auto"){
9708             return v;
9709         }
9710         if(v === undefined){
9711             return '';
9712         }
9713         if(typeof v == "number" || !El.unitPattern.test(v)){
9714             return v + (defaultUnit || 'px');
9715         }
9716         return v;
9717     };
9718
9719     // special markup used throughout Roo when box wrapping elements
9720     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9721     /**
9722      * Visibility mode constant - Use visibility to hide element
9723      * @static
9724      * @type Number
9725      */
9726     El.VISIBILITY = 1;
9727     /**
9728      * Visibility mode constant - Use display to hide element
9729      * @static
9730      * @type Number
9731      */
9732     El.DISPLAY = 2;
9733
9734     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9735     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9736     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9737
9738
9739
9740     /**
9741      * @private
9742      */
9743     El.cache = {};
9744
9745     var docEl;
9746
9747     /**
9748      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9749      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9750      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9751      * @return {Element} The Element object
9752      * @static
9753      */
9754     El.get = function(el){
9755         var ex, elm, id;
9756         if(!el){ return null; }
9757         if(typeof el == "string"){ // element id
9758             if(!(elm = document.getElementById(el))){
9759                 return null;
9760             }
9761             if(ex = El.cache[el]){
9762                 ex.dom = elm;
9763             }else{
9764                 ex = El.cache[el] = new El(elm);
9765             }
9766             return ex;
9767         }else if(el.tagName){ // dom element
9768             if(!(id = el.id)){
9769                 id = Roo.id(el);
9770             }
9771             if(ex = El.cache[id]){
9772                 ex.dom = el;
9773             }else{
9774                 ex = El.cache[id] = new El(el);
9775             }
9776             return ex;
9777         }else if(el instanceof El){
9778             if(el != docEl){
9779                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9780                                                               // catch case where it hasn't been appended
9781                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9782             }
9783             return el;
9784         }else if(el.isComposite){
9785             return el;
9786         }else if(el instanceof Array){
9787             return El.select(el);
9788         }else if(el == document){
9789             // create a bogus element object representing the document object
9790             if(!docEl){
9791                 var f = function(){};
9792                 f.prototype = El.prototype;
9793                 docEl = new f();
9794                 docEl.dom = document;
9795             }
9796             return docEl;
9797         }
9798         return null;
9799     };
9800
9801     // private
9802     El.uncache = function(el){
9803         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9804             if(a[i]){
9805                 delete El.cache[a[i].id || a[i]];
9806             }
9807         }
9808     };
9809
9810     // private
9811     // Garbage collection - uncache elements/purge listeners on orphaned elements
9812     // so we don't hold a reference and cause the browser to retain them
9813     El.garbageCollect = function(){
9814         if(!Roo.enableGarbageCollector){
9815             clearInterval(El.collectorThread);
9816             return;
9817         }
9818         for(var eid in El.cache){
9819             var el = El.cache[eid], d = el.dom;
9820             // -------------------------------------------------------
9821             // Determining what is garbage:
9822             // -------------------------------------------------------
9823             // !d
9824             // dom node is null, definitely garbage
9825             // -------------------------------------------------------
9826             // !d.parentNode
9827             // no parentNode == direct orphan, definitely garbage
9828             // -------------------------------------------------------
9829             // !d.offsetParent && !document.getElementById(eid)
9830             // display none elements have no offsetParent so we will
9831             // also try to look it up by it's id. However, check
9832             // offsetParent first so we don't do unneeded lookups.
9833             // This enables collection of elements that are not orphans
9834             // directly, but somewhere up the line they have an orphan
9835             // parent.
9836             // -------------------------------------------------------
9837             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9838                 delete El.cache[eid];
9839                 if(d && Roo.enableListenerCollection){
9840                     E.purgeElement(d);
9841                 }
9842             }
9843         }
9844     }
9845     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9846
9847
9848     // dom is optional
9849     El.Flyweight = function(dom){
9850         this.dom = dom;
9851     };
9852     El.Flyweight.prototype = El.prototype;
9853
9854     El._flyweights = {};
9855     /**
9856      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9857      * the dom node can be overwritten by other code.
9858      * @param {String/HTMLElement} el The dom node or id
9859      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9860      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9861      * @static
9862      * @return {Element} The shared Element object
9863      */
9864     El.fly = function(el, named){
9865         named = named || '_global';
9866         el = Roo.getDom(el);
9867         if(!el){
9868             return null;
9869         }
9870         if(!El._flyweights[named]){
9871             El._flyweights[named] = new El.Flyweight();
9872         }
9873         El._flyweights[named].dom = el;
9874         return El._flyweights[named];
9875     };
9876
9877     /**
9878      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9879      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9880      * Shorthand of {@link Roo.Element#get}
9881      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9882      * @return {Element} The Element object
9883      * @member Roo
9884      * @method get
9885      */
9886     Roo.get = El.get;
9887     /**
9888      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9889      * the dom node can be overwritten by other code.
9890      * Shorthand of {@link Roo.Element#fly}
9891      * @param {String/HTMLElement} el The dom node or id
9892      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9893      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9894      * @static
9895      * @return {Element} The shared Element object
9896      * @member Roo
9897      * @method fly
9898      */
9899     Roo.fly = El.fly;
9900
9901     // speedy lookup for elements never to box adjust
9902     var noBoxAdjust = Roo.isStrict ? {
9903         select:1
9904     } : {
9905         input:1, select:1, textarea:1
9906     };
9907     if(Roo.isIE || Roo.isGecko){
9908         noBoxAdjust['button'] = 1;
9909     }
9910
9911
9912     Roo.EventManager.on(window, 'unload', function(){
9913         delete El.cache;
9914         delete El._flyweights;
9915     });
9916 })();
9917
9918
9919
9920
9921 if(Roo.DomQuery){
9922     Roo.Element.selectorFunction = Roo.DomQuery.select;
9923 }
9924
9925 Roo.Element.select = function(selector, unique, root){
9926     var els;
9927     if(typeof selector == "string"){
9928         els = Roo.Element.selectorFunction(selector, root);
9929     }else if(selector.length !== undefined){
9930         els = selector;
9931     }else{
9932         throw "Invalid selector";
9933     }
9934     if(unique === true){
9935         return new Roo.CompositeElement(els);
9936     }else{
9937         return new Roo.CompositeElementLite(els);
9938     }
9939 };
9940 /**
9941  * Selects elements based on the passed CSS selector to enable working on them as 1.
9942  * @param {String/Array} selector The CSS selector or an array of elements
9943  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9944  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9945  * @return {CompositeElementLite/CompositeElement}
9946  * @member Roo
9947  * @method select
9948  */
9949 Roo.select = Roo.Element.select;
9950
9951
9952
9953
9954
9955
9956
9957
9958
9959
9960
9961
9962
9963
9964 /*
9965  * Based on:
9966  * Ext JS Library 1.1.1
9967  * Copyright(c) 2006-2007, Ext JS, LLC.
9968  *
9969  * Originally Released Under LGPL - original licence link has changed is not relivant.
9970  *
9971  * Fork - LGPL
9972  * <script type="text/javascript">
9973  */
9974
9975
9976
9977 //Notifies Element that fx methods are available
9978 Roo.enableFx = true;
9979
9980 /**
9981  * @class Roo.Fx
9982  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9983  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9984  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9985  * Element effects to work.</p><br/>
9986  *
9987  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9988  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9989  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9990  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9991  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9992  * expected results and should be done with care.</p><br/>
9993  *
9994  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9995  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9996 <pre>
9997 Value  Description
9998 -----  -----------------------------
9999 tl     The top left corner
10000 t      The center of the top edge
10001 tr     The top right corner
10002 l      The center of the left edge
10003 r      The center of the right edge
10004 bl     The bottom left corner
10005 b      The center of the bottom edge
10006 br     The bottom right corner
10007 </pre>
10008  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10009  * below are common options that can be passed to any Fx method.</b>
10010  * @cfg {Function} callback A function called when the effect is finished
10011  * @cfg {Object} scope The scope of the effect function
10012  * @cfg {String} easing A valid Easing value for the effect
10013  * @cfg {String} afterCls A css class to apply after the effect
10014  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10015  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10016  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10017  * effects that end with the element being visually hidden, ignored otherwise)
10018  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10019  * a function which returns such a specification that will be applied to the Element after the effect finishes
10020  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10021  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
10022  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10023  */
10024 Roo.Fx = {
10025         /**
10026          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10027          * origin for the slide effect.  This function automatically handles wrapping the element with
10028          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10029          * Usage:
10030          *<pre><code>
10031 // default: slide the element in from the top
10032 el.slideIn();
10033
10034 // custom: slide the element in from the right with a 2-second duration
10035 el.slideIn('r', { duration: 2 });
10036
10037 // common config options shown with default values
10038 el.slideIn('t', {
10039     easing: 'easeOut',
10040     duration: .5
10041 });
10042 </code></pre>
10043          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10044          * @param {Object} options (optional) Object literal with any of the Fx config options
10045          * @return {Roo.Element} The Element
10046          */
10047     slideIn : function(anchor, o){
10048         var el = this.getFxEl();
10049         o = o || {};
10050
10051         el.queueFx(o, function(){
10052
10053             anchor = anchor || "t";
10054
10055             // fix display to visibility
10056             this.fixDisplay();
10057
10058             // restore values after effect
10059             var r = this.getFxRestore();
10060             var b = this.getBox();
10061             // fixed size for slide
10062             this.setSize(b);
10063
10064             // wrap if needed
10065             var wrap = this.fxWrap(r.pos, o, "hidden");
10066
10067             var st = this.dom.style;
10068             st.visibility = "visible";
10069             st.position = "absolute";
10070
10071             // clear out temp styles after slide and unwrap
10072             var after = function(){
10073                 el.fxUnwrap(wrap, r.pos, o);
10074                 st.width = r.width;
10075                 st.height = r.height;
10076                 el.afterFx(o);
10077             };
10078             // time to calc the positions
10079             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10080
10081             switch(anchor.toLowerCase()){
10082                 case "t":
10083                     wrap.setSize(b.width, 0);
10084                     st.left = st.bottom = "0";
10085                     a = {height: bh};
10086                 break;
10087                 case "l":
10088                     wrap.setSize(0, b.height);
10089                     st.right = st.top = "0";
10090                     a = {width: bw};
10091                 break;
10092                 case "r":
10093                     wrap.setSize(0, b.height);
10094                     wrap.setX(b.right);
10095                     st.left = st.top = "0";
10096                     a = {width: bw, points: pt};
10097                 break;
10098                 case "b":
10099                     wrap.setSize(b.width, 0);
10100                     wrap.setY(b.bottom);
10101                     st.left = st.top = "0";
10102                     a = {height: bh, points: pt};
10103                 break;
10104                 case "tl":
10105                     wrap.setSize(0, 0);
10106                     st.right = st.bottom = "0";
10107                     a = {width: bw, height: bh};
10108                 break;
10109                 case "bl":
10110                     wrap.setSize(0, 0);
10111                     wrap.setY(b.y+b.height);
10112                     st.right = st.top = "0";
10113                     a = {width: bw, height: bh, points: pt};
10114                 break;
10115                 case "br":
10116                     wrap.setSize(0, 0);
10117                     wrap.setXY([b.right, b.bottom]);
10118                     st.left = st.top = "0";
10119                     a = {width: bw, height: bh, points: pt};
10120                 break;
10121                 case "tr":
10122                     wrap.setSize(0, 0);
10123                     wrap.setX(b.x+b.width);
10124                     st.left = st.bottom = "0";
10125                     a = {width: bw, height: bh, points: pt};
10126                 break;
10127             }
10128             this.dom.style.visibility = "visible";
10129             wrap.show();
10130
10131             arguments.callee.anim = wrap.fxanim(a,
10132                 o,
10133                 'motion',
10134                 .5,
10135                 'easeOut', after);
10136         });
10137         return this;
10138     },
10139     
10140         /**
10141          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10142          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10143          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10144          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10145          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10146          * Usage:
10147          *<pre><code>
10148 // default: slide the element out to the top
10149 el.slideOut();
10150
10151 // custom: slide the element out to the right with a 2-second duration
10152 el.slideOut('r', { duration: 2 });
10153
10154 // common config options shown with default values
10155 el.slideOut('t', {
10156     easing: 'easeOut',
10157     duration: .5,
10158     remove: false,
10159     useDisplay: false
10160 });
10161 </code></pre>
10162          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10163          * @param {Object} options (optional) Object literal with any of the Fx config options
10164          * @return {Roo.Element} The Element
10165          */
10166     slideOut : function(anchor, o){
10167         var el = this.getFxEl();
10168         o = o || {};
10169
10170         el.queueFx(o, function(){
10171
10172             anchor = anchor || "t";
10173
10174             // restore values after effect
10175             var r = this.getFxRestore();
10176             
10177             var b = this.getBox();
10178             // fixed size for slide
10179             this.setSize(b);
10180
10181             // wrap if needed
10182             var wrap = this.fxWrap(r.pos, o, "visible");
10183
10184             var st = this.dom.style;
10185             st.visibility = "visible";
10186             st.position = "absolute";
10187
10188             wrap.setSize(b);
10189
10190             var after = function(){
10191                 if(o.useDisplay){
10192                     el.setDisplayed(false);
10193                 }else{
10194                     el.hide();
10195                 }
10196
10197                 el.fxUnwrap(wrap, r.pos, o);
10198
10199                 st.width = r.width;
10200                 st.height = r.height;
10201
10202                 el.afterFx(o);
10203             };
10204
10205             var a, zero = {to: 0};
10206             switch(anchor.toLowerCase()){
10207                 case "t":
10208                     st.left = st.bottom = "0";
10209                     a = {height: zero};
10210                 break;
10211                 case "l":
10212                     st.right = st.top = "0";
10213                     a = {width: zero};
10214                 break;
10215                 case "r":
10216                     st.left = st.top = "0";
10217                     a = {width: zero, points: {to:[b.right, b.y]}};
10218                 break;
10219                 case "b":
10220                     st.left = st.top = "0";
10221                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10222                 break;
10223                 case "tl":
10224                     st.right = st.bottom = "0";
10225                     a = {width: zero, height: zero};
10226                 break;
10227                 case "bl":
10228                     st.right = st.top = "0";
10229                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10230                 break;
10231                 case "br":
10232                     st.left = st.top = "0";
10233                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10234                 break;
10235                 case "tr":
10236                     st.left = st.bottom = "0";
10237                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10238                 break;
10239             }
10240
10241             arguments.callee.anim = wrap.fxanim(a,
10242                 o,
10243                 'motion',
10244                 .5,
10245                 "easeOut", after);
10246         });
10247         return this;
10248     },
10249
10250         /**
10251          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10252          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10253          * The element must be removed from the DOM using the 'remove' config option if desired.
10254          * Usage:
10255          *<pre><code>
10256 // default
10257 el.puff();
10258
10259 // common config options shown with default values
10260 el.puff({
10261     easing: 'easeOut',
10262     duration: .5,
10263     remove: false,
10264     useDisplay: false
10265 });
10266 </code></pre>
10267          * @param {Object} options (optional) Object literal with any of the Fx config options
10268          * @return {Roo.Element} The Element
10269          */
10270     puff : function(o){
10271         var el = this.getFxEl();
10272         o = o || {};
10273
10274         el.queueFx(o, function(){
10275             this.clearOpacity();
10276             this.show();
10277
10278             // restore values after effect
10279             var r = this.getFxRestore();
10280             var st = this.dom.style;
10281
10282             var after = function(){
10283                 if(o.useDisplay){
10284                     el.setDisplayed(false);
10285                 }else{
10286                     el.hide();
10287                 }
10288
10289                 el.clearOpacity();
10290
10291                 el.setPositioning(r.pos);
10292                 st.width = r.width;
10293                 st.height = r.height;
10294                 st.fontSize = '';
10295                 el.afterFx(o);
10296             };
10297
10298             var width = this.getWidth();
10299             var height = this.getHeight();
10300
10301             arguments.callee.anim = this.fxanim({
10302                     width : {to: this.adjustWidth(width * 2)},
10303                     height : {to: this.adjustHeight(height * 2)},
10304                     points : {by: [-(width * .5), -(height * .5)]},
10305                     opacity : {to: 0},
10306                     fontSize: {to:200, unit: "%"}
10307                 },
10308                 o,
10309                 'motion',
10310                 .5,
10311                 "easeOut", after);
10312         });
10313         return this;
10314     },
10315
10316         /**
10317          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10318          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10319          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10320          * Usage:
10321          *<pre><code>
10322 // default
10323 el.switchOff();
10324
10325 // all config options shown with default values
10326 el.switchOff({
10327     easing: 'easeIn',
10328     duration: .3,
10329     remove: false,
10330     useDisplay: false
10331 });
10332 </code></pre>
10333          * @param {Object} options (optional) Object literal with any of the Fx config options
10334          * @return {Roo.Element} The Element
10335          */
10336     switchOff : function(o){
10337         var el = this.getFxEl();
10338         o = o || {};
10339
10340         el.queueFx(o, function(){
10341             this.clearOpacity();
10342             this.clip();
10343
10344             // restore values after effect
10345             var r = this.getFxRestore();
10346             var st = this.dom.style;
10347
10348             var after = function(){
10349                 if(o.useDisplay){
10350                     el.setDisplayed(false);
10351                 }else{
10352                     el.hide();
10353                 }
10354
10355                 el.clearOpacity();
10356                 el.setPositioning(r.pos);
10357                 st.width = r.width;
10358                 st.height = r.height;
10359
10360                 el.afterFx(o);
10361             };
10362
10363             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10364                 this.clearOpacity();
10365                 (function(){
10366                     this.fxanim({
10367                         height:{to:1},
10368                         points:{by:[0, this.getHeight() * .5]}
10369                     }, o, 'motion', 0.3, 'easeIn', after);
10370                 }).defer(100, this);
10371             });
10372         });
10373         return this;
10374     },
10375
10376     /**
10377      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10378      * changed using the "attr" config option) and then fading back to the original color. If no original
10379      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10380      * Usage:
10381 <pre><code>
10382 // default: highlight background to yellow
10383 el.highlight();
10384
10385 // custom: highlight foreground text to blue for 2 seconds
10386 el.highlight("0000ff", { attr: 'color', duration: 2 });
10387
10388 // common config options shown with default values
10389 el.highlight("ffff9c", {
10390     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10391     endColor: (current color) or "ffffff",
10392     easing: 'easeIn',
10393     duration: 1
10394 });
10395 </code></pre>
10396      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10397      * @param {Object} options (optional) Object literal with any of the Fx config options
10398      * @return {Roo.Element} The Element
10399      */ 
10400     highlight : function(color, o){
10401         var el = this.getFxEl();
10402         o = o || {};
10403
10404         el.queueFx(o, function(){
10405             color = color || "ffff9c";
10406             attr = o.attr || "backgroundColor";
10407
10408             this.clearOpacity();
10409             this.show();
10410
10411             var origColor = this.getColor(attr);
10412             var restoreColor = this.dom.style[attr];
10413             endColor = (o.endColor || origColor) || "ffffff";
10414
10415             var after = function(){
10416                 el.dom.style[attr] = restoreColor;
10417                 el.afterFx(o);
10418             };
10419
10420             var a = {};
10421             a[attr] = {from: color, to: endColor};
10422             arguments.callee.anim = this.fxanim(a,
10423                 o,
10424                 'color',
10425                 1,
10426                 'easeIn', after);
10427         });
10428         return this;
10429     },
10430
10431    /**
10432     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10433     * Usage:
10434 <pre><code>
10435 // default: a single light blue ripple
10436 el.frame();
10437
10438 // custom: 3 red ripples lasting 3 seconds total
10439 el.frame("ff0000", 3, { duration: 3 });
10440
10441 // common config options shown with default values
10442 el.frame("C3DAF9", 1, {
10443     duration: 1 //duration of entire animation (not each individual ripple)
10444     // Note: Easing is not configurable and will be ignored if included
10445 });
10446 </code></pre>
10447     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10448     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10449     * @param {Object} options (optional) Object literal with any of the Fx config options
10450     * @return {Roo.Element} The Element
10451     */
10452     frame : function(color, count, o){
10453         var el = this.getFxEl();
10454         o = o || {};
10455
10456         el.queueFx(o, function(){
10457             color = color || "#C3DAF9";
10458             if(color.length == 6){
10459                 color = "#" + color;
10460             }
10461             count = count || 1;
10462             duration = o.duration || 1;
10463             this.show();
10464
10465             var b = this.getBox();
10466             var animFn = function(){
10467                 var proxy = this.createProxy({
10468
10469                      style:{
10470                         visbility:"hidden",
10471                         position:"absolute",
10472                         "z-index":"35000", // yee haw
10473                         border:"0px solid " + color
10474                      }
10475                   });
10476                 var scale = Roo.isBorderBox ? 2 : 1;
10477                 proxy.animate({
10478                     top:{from:b.y, to:b.y - 20},
10479                     left:{from:b.x, to:b.x - 20},
10480                     borderWidth:{from:0, to:10},
10481                     opacity:{from:1, to:0},
10482                     height:{from:b.height, to:(b.height + (20*scale))},
10483                     width:{from:b.width, to:(b.width + (20*scale))}
10484                 }, duration, function(){
10485                     proxy.remove();
10486                 });
10487                 if(--count > 0){
10488                      animFn.defer((duration/2)*1000, this);
10489                 }else{
10490                     el.afterFx(o);
10491                 }
10492             };
10493             animFn.call(this);
10494         });
10495         return this;
10496     },
10497
10498    /**
10499     * Creates a pause before any subsequent queued effects begin.  If there are
10500     * no effects queued after the pause it will have no effect.
10501     * Usage:
10502 <pre><code>
10503 el.pause(1);
10504 </code></pre>
10505     * @param {Number} seconds The length of time to pause (in seconds)
10506     * @return {Roo.Element} The Element
10507     */
10508     pause : function(seconds){
10509         var el = this.getFxEl();
10510         var o = {};
10511
10512         el.queueFx(o, function(){
10513             setTimeout(function(){
10514                 el.afterFx(o);
10515             }, seconds * 1000);
10516         });
10517         return this;
10518     },
10519
10520    /**
10521     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10522     * using the "endOpacity" config option.
10523     * Usage:
10524 <pre><code>
10525 // default: fade in from opacity 0 to 100%
10526 el.fadeIn();
10527
10528 // custom: fade in from opacity 0 to 75% over 2 seconds
10529 el.fadeIn({ endOpacity: .75, duration: 2});
10530
10531 // common config options shown with default values
10532 el.fadeIn({
10533     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10534     easing: 'easeOut',
10535     duration: .5
10536 });
10537 </code></pre>
10538     * @param {Object} options (optional) Object literal with any of the Fx config options
10539     * @return {Roo.Element} The Element
10540     */
10541     fadeIn : function(o){
10542         var el = this.getFxEl();
10543         o = o || {};
10544         el.queueFx(o, function(){
10545             this.setOpacity(0);
10546             this.fixDisplay();
10547             this.dom.style.visibility = 'visible';
10548             var to = o.endOpacity || 1;
10549             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10550                 o, null, .5, "easeOut", function(){
10551                 if(to == 1){
10552                     this.clearOpacity();
10553                 }
10554                 el.afterFx(o);
10555             });
10556         });
10557         return this;
10558     },
10559
10560    /**
10561     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10562     * using the "endOpacity" config option.
10563     * Usage:
10564 <pre><code>
10565 // default: fade out from the element's current opacity to 0
10566 el.fadeOut();
10567
10568 // custom: fade out from the element's current opacity to 25% over 2 seconds
10569 el.fadeOut({ endOpacity: .25, duration: 2});
10570
10571 // common config options shown with default values
10572 el.fadeOut({
10573     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10574     easing: 'easeOut',
10575     duration: .5
10576     remove: false,
10577     useDisplay: false
10578 });
10579 </code></pre>
10580     * @param {Object} options (optional) Object literal with any of the Fx config options
10581     * @return {Roo.Element} The Element
10582     */
10583     fadeOut : function(o){
10584         var el = this.getFxEl();
10585         o = o || {};
10586         el.queueFx(o, function(){
10587             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10588                 o, null, .5, "easeOut", function(){
10589                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10590                      this.dom.style.display = "none";
10591                 }else{
10592                      this.dom.style.visibility = "hidden";
10593                 }
10594                 this.clearOpacity();
10595                 el.afterFx(o);
10596             });
10597         });
10598         return this;
10599     },
10600
10601    /**
10602     * Animates the transition of an element's dimensions from a starting height/width
10603     * to an ending height/width.
10604     * Usage:
10605 <pre><code>
10606 // change height and width to 100x100 pixels
10607 el.scale(100, 100);
10608
10609 // common config options shown with default values.  The height and width will default to
10610 // the element's existing values if passed as null.
10611 el.scale(
10612     [element's width],
10613     [element's height], {
10614     easing: 'easeOut',
10615     duration: .35
10616 });
10617 </code></pre>
10618     * @param {Number} width  The new width (pass undefined to keep the original width)
10619     * @param {Number} height  The new height (pass undefined to keep the original height)
10620     * @param {Object} options (optional) Object literal with any of the Fx config options
10621     * @return {Roo.Element} The Element
10622     */
10623     scale : function(w, h, o){
10624         this.shift(Roo.apply({}, o, {
10625             width: w,
10626             height: h
10627         }));
10628         return this;
10629     },
10630
10631    /**
10632     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10633     * Any of these properties not specified in the config object will not be changed.  This effect 
10634     * requires that at least one new dimension, position or opacity setting must be passed in on
10635     * the config object in order for the function to have any effect.
10636     * Usage:
10637 <pre><code>
10638 // slide the element horizontally to x position 200 while changing the height and opacity
10639 el.shift({ x: 200, height: 50, opacity: .8 });
10640
10641 // common config options shown with default values.
10642 el.shift({
10643     width: [element's width],
10644     height: [element's height],
10645     x: [element's x position],
10646     y: [element's y position],
10647     opacity: [element's opacity],
10648     easing: 'easeOut',
10649     duration: .35
10650 });
10651 </code></pre>
10652     * @param {Object} options  Object literal with any of the Fx config options
10653     * @return {Roo.Element} The Element
10654     */
10655     shift : function(o){
10656         var el = this.getFxEl();
10657         o = o || {};
10658         el.queueFx(o, function(){
10659             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10660             if(w !== undefined){
10661                 a.width = {to: this.adjustWidth(w)};
10662             }
10663             if(h !== undefined){
10664                 a.height = {to: this.adjustHeight(h)};
10665             }
10666             if(x !== undefined || y !== undefined){
10667                 a.points = {to: [
10668                     x !== undefined ? x : this.getX(),
10669                     y !== undefined ? y : this.getY()
10670                 ]};
10671             }
10672             if(op !== undefined){
10673                 a.opacity = {to: op};
10674             }
10675             if(o.xy !== undefined){
10676                 a.points = {to: o.xy};
10677             }
10678             arguments.callee.anim = this.fxanim(a,
10679                 o, 'motion', .35, "easeOut", function(){
10680                 el.afterFx(o);
10681             });
10682         });
10683         return this;
10684     },
10685
10686         /**
10687          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10688          * ending point of the effect.
10689          * Usage:
10690          *<pre><code>
10691 // default: slide the element downward while fading out
10692 el.ghost();
10693
10694 // custom: slide the element out to the right with a 2-second duration
10695 el.ghost('r', { duration: 2 });
10696
10697 // common config options shown with default values
10698 el.ghost('b', {
10699     easing: 'easeOut',
10700     duration: .5
10701     remove: false,
10702     useDisplay: false
10703 });
10704 </code></pre>
10705          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10706          * @param {Object} options (optional) Object literal with any of the Fx config options
10707          * @return {Roo.Element} The Element
10708          */
10709     ghost : function(anchor, o){
10710         var el = this.getFxEl();
10711         o = o || {};
10712
10713         el.queueFx(o, function(){
10714             anchor = anchor || "b";
10715
10716             // restore values after effect
10717             var r = this.getFxRestore();
10718             var w = this.getWidth(),
10719                 h = this.getHeight();
10720
10721             var st = this.dom.style;
10722
10723             var after = function(){
10724                 if(o.useDisplay){
10725                     el.setDisplayed(false);
10726                 }else{
10727                     el.hide();
10728                 }
10729
10730                 el.clearOpacity();
10731                 el.setPositioning(r.pos);
10732                 st.width = r.width;
10733                 st.height = r.height;
10734
10735                 el.afterFx(o);
10736             };
10737
10738             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10739             switch(anchor.toLowerCase()){
10740                 case "t":
10741                     pt.by = [0, -h];
10742                 break;
10743                 case "l":
10744                     pt.by = [-w, 0];
10745                 break;
10746                 case "r":
10747                     pt.by = [w, 0];
10748                 break;
10749                 case "b":
10750                     pt.by = [0, h];
10751                 break;
10752                 case "tl":
10753                     pt.by = [-w, -h];
10754                 break;
10755                 case "bl":
10756                     pt.by = [-w, h];
10757                 break;
10758                 case "br":
10759                     pt.by = [w, h];
10760                 break;
10761                 case "tr":
10762                     pt.by = [w, -h];
10763                 break;
10764             }
10765
10766             arguments.callee.anim = this.fxanim(a,
10767                 o,
10768                 'motion',
10769                 .5,
10770                 "easeOut", after);
10771         });
10772         return this;
10773     },
10774
10775         /**
10776          * Ensures that all effects queued after syncFx is called on the element are
10777          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10778          * @return {Roo.Element} The Element
10779          */
10780     syncFx : function(){
10781         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10782             block : false,
10783             concurrent : true,
10784             stopFx : false
10785         });
10786         return this;
10787     },
10788
10789         /**
10790          * Ensures that all effects queued after sequenceFx is called on the element are
10791          * run in sequence.  This is the opposite of {@link #syncFx}.
10792          * @return {Roo.Element} The Element
10793          */
10794     sequenceFx : function(){
10795         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10796             block : false,
10797             concurrent : false,
10798             stopFx : false
10799         });
10800         return this;
10801     },
10802
10803         /* @private */
10804     nextFx : function(){
10805         var ef = this.fxQueue[0];
10806         if(ef){
10807             ef.call(this);
10808         }
10809     },
10810
10811         /**
10812          * Returns true if the element has any effects actively running or queued, else returns false.
10813          * @return {Boolean} True if element has active effects, else false
10814          */
10815     hasActiveFx : function(){
10816         return this.fxQueue && this.fxQueue[0];
10817     },
10818
10819         /**
10820          * Stops any running effects and clears the element's internal effects queue if it contains
10821          * any additional effects that haven't started yet.
10822          * @return {Roo.Element} The Element
10823          */
10824     stopFx : function(){
10825         if(this.hasActiveFx()){
10826             var cur = this.fxQueue[0];
10827             if(cur && cur.anim && cur.anim.isAnimated()){
10828                 this.fxQueue = [cur]; // clear out others
10829                 cur.anim.stop(true);
10830             }
10831         }
10832         return this;
10833     },
10834
10835         /* @private */
10836     beforeFx : function(o){
10837         if(this.hasActiveFx() && !o.concurrent){
10838            if(o.stopFx){
10839                this.stopFx();
10840                return true;
10841            }
10842            return false;
10843         }
10844         return true;
10845     },
10846
10847         /**
10848          * Returns true if the element is currently blocking so that no other effect can be queued
10849          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10850          * used to ensure that an effect initiated by a user action runs to completion prior to the
10851          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10852          * @return {Boolean} True if blocking, else false
10853          */
10854     hasFxBlock : function(){
10855         var q = this.fxQueue;
10856         return q && q[0] && q[0].block;
10857     },
10858
10859         /* @private */
10860     queueFx : function(o, fn){
10861         if(!this.fxQueue){
10862             this.fxQueue = [];
10863         }
10864         if(!this.hasFxBlock()){
10865             Roo.applyIf(o, this.fxDefaults);
10866             if(!o.concurrent){
10867                 var run = this.beforeFx(o);
10868                 fn.block = o.block;
10869                 this.fxQueue.push(fn);
10870                 if(run){
10871                     this.nextFx();
10872                 }
10873             }else{
10874                 fn.call(this);
10875             }
10876         }
10877         return this;
10878     },
10879
10880         /* @private */
10881     fxWrap : function(pos, o, vis){
10882         var wrap;
10883         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10884             var wrapXY;
10885             if(o.fixPosition){
10886                 wrapXY = this.getXY();
10887             }
10888             var div = document.createElement("div");
10889             div.style.visibility = vis;
10890             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10891             wrap.setPositioning(pos);
10892             if(wrap.getStyle("position") == "static"){
10893                 wrap.position("relative");
10894             }
10895             this.clearPositioning('auto');
10896             wrap.clip();
10897             wrap.dom.appendChild(this.dom);
10898             if(wrapXY){
10899                 wrap.setXY(wrapXY);
10900             }
10901         }
10902         return wrap;
10903     },
10904
10905         /* @private */
10906     fxUnwrap : function(wrap, pos, o){
10907         this.clearPositioning();
10908         this.setPositioning(pos);
10909         if(!o.wrap){
10910             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10911             wrap.remove();
10912         }
10913     },
10914
10915         /* @private */
10916     getFxRestore : function(){
10917         var st = this.dom.style;
10918         return {pos: this.getPositioning(), width: st.width, height : st.height};
10919     },
10920
10921         /* @private */
10922     afterFx : function(o){
10923         if(o.afterStyle){
10924             this.applyStyles(o.afterStyle);
10925         }
10926         if(o.afterCls){
10927             this.addClass(o.afterCls);
10928         }
10929         if(o.remove === true){
10930             this.remove();
10931         }
10932         Roo.callback(o.callback, o.scope, [this]);
10933         if(!o.concurrent){
10934             this.fxQueue.shift();
10935             this.nextFx();
10936         }
10937     },
10938
10939         /* @private */
10940     getFxEl : function(){ // support for composite element fx
10941         return Roo.get(this.dom);
10942     },
10943
10944         /* @private */
10945     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10946         animType = animType || 'run';
10947         opt = opt || {};
10948         var anim = Roo.lib.Anim[animType](
10949             this.dom, args,
10950             (opt.duration || defaultDur) || .35,
10951             (opt.easing || defaultEase) || 'easeOut',
10952             function(){
10953                 Roo.callback(cb, this);
10954             },
10955             this
10956         );
10957         opt.anim = anim;
10958         return anim;
10959     }
10960 };
10961
10962 // backwords compat
10963 Roo.Fx.resize = Roo.Fx.scale;
10964
10965 //When included, Roo.Fx is automatically applied to Element so that all basic
10966 //effects are available directly via the Element API
10967 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10968  * Based on:
10969  * Ext JS Library 1.1.1
10970  * Copyright(c) 2006-2007, Ext JS, LLC.
10971  *
10972  * Originally Released Under LGPL - original licence link has changed is not relivant.
10973  *
10974  * Fork - LGPL
10975  * <script type="text/javascript">
10976  */
10977
10978
10979 /**
10980  * @class Roo.CompositeElement
10981  * Standard composite class. Creates a Roo.Element for every element in the collection.
10982  * <br><br>
10983  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10984  * actions will be performed on all the elements in this collection.</b>
10985  * <br><br>
10986  * All methods return <i>this</i> and can be chained.
10987  <pre><code>
10988  var els = Roo.select("#some-el div.some-class", true);
10989  // or select directly from an existing element
10990  var el = Roo.get('some-el');
10991  el.select('div.some-class', true);
10992
10993  els.setWidth(100); // all elements become 100 width
10994  els.hide(true); // all elements fade out and hide
10995  // or
10996  els.setWidth(100).hide(true);
10997  </code></pre>
10998  */
10999 Roo.CompositeElement = function(els){
11000     this.elements = [];
11001     this.addElements(els);
11002 };
11003 Roo.CompositeElement.prototype = {
11004     isComposite: true,
11005     addElements : function(els){
11006         if(!els) {
11007             return this;
11008         }
11009         if(typeof els == "string"){
11010             els = Roo.Element.selectorFunction(els);
11011         }
11012         var yels = this.elements;
11013         var index = yels.length-1;
11014         for(var i = 0, len = els.length; i < len; i++) {
11015                 yels[++index] = Roo.get(els[i]);
11016         }
11017         return this;
11018     },
11019
11020     /**
11021     * Clears this composite and adds the elements returned by the passed selector.
11022     * @param {String/Array} els A string CSS selector, an array of elements or an element
11023     * @return {CompositeElement} this
11024     */
11025     fill : function(els){
11026         this.elements = [];
11027         this.add(els);
11028         return this;
11029     },
11030
11031     /**
11032     * Filters this composite to only elements that match the passed selector.
11033     * @param {String} selector A string CSS selector
11034     * @param {Boolean} inverse return inverse filter (not matches)
11035     * @return {CompositeElement} this
11036     */
11037     filter : function(selector, inverse){
11038         var els = [];
11039         inverse = inverse || false;
11040         this.each(function(el){
11041             var match = inverse ? !el.is(selector) : el.is(selector);
11042             if(match){
11043                 els[els.length] = el.dom;
11044             }
11045         });
11046         this.fill(els);
11047         return this;
11048     },
11049
11050     invoke : function(fn, args){
11051         var els = this.elements;
11052         for(var i = 0, len = els.length; i < len; i++) {
11053                 Roo.Element.prototype[fn].apply(els[i], args);
11054         }
11055         return this;
11056     },
11057     /**
11058     * Adds elements to this composite.
11059     * @param {String/Array} els A string CSS selector, an array of elements or an element
11060     * @return {CompositeElement} this
11061     */
11062     add : function(els){
11063         if(typeof els == "string"){
11064             this.addElements(Roo.Element.selectorFunction(els));
11065         }else if(els.length !== undefined){
11066             this.addElements(els);
11067         }else{
11068             this.addElements([els]);
11069         }
11070         return this;
11071     },
11072     /**
11073     * Calls the passed function passing (el, this, index) for each element in this composite.
11074     * @param {Function} fn The function to call
11075     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11076     * @return {CompositeElement} this
11077     */
11078     each : function(fn, scope){
11079         var els = this.elements;
11080         for(var i = 0, len = els.length; i < len; i++){
11081             if(fn.call(scope || els[i], els[i], this, i) === false) {
11082                 break;
11083             }
11084         }
11085         return this;
11086     },
11087
11088     /**
11089      * Returns the Element object at the specified index
11090      * @param {Number} index
11091      * @return {Roo.Element}
11092      */
11093     item : function(index){
11094         return this.elements[index] || null;
11095     },
11096
11097     /**
11098      * Returns the first Element
11099      * @return {Roo.Element}
11100      */
11101     first : function(){
11102         return this.item(0);
11103     },
11104
11105     /**
11106      * Returns the last Element
11107      * @return {Roo.Element}
11108      */
11109     last : function(){
11110         return this.item(this.elements.length-1);
11111     },
11112
11113     /**
11114      * Returns the number of elements in this composite
11115      * @return Number
11116      */
11117     getCount : function(){
11118         return this.elements.length;
11119     },
11120
11121     /**
11122      * Returns true if this composite contains the passed element
11123      * @return Boolean
11124      */
11125     contains : function(el){
11126         return this.indexOf(el) !== -1;
11127     },
11128
11129     /**
11130      * Returns true if this composite contains the passed element
11131      * @return Boolean
11132      */
11133     indexOf : function(el){
11134         return this.elements.indexOf(Roo.get(el));
11135     },
11136
11137
11138     /**
11139     * Removes the specified element(s).
11140     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11141     * or an array of any of those.
11142     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11143     * @return {CompositeElement} this
11144     */
11145     removeElement : function(el, removeDom){
11146         if(el instanceof Array){
11147             for(var i = 0, len = el.length; i < len; i++){
11148                 this.removeElement(el[i]);
11149             }
11150             return this;
11151         }
11152         var index = typeof el == 'number' ? el : this.indexOf(el);
11153         if(index !== -1){
11154             if(removeDom){
11155                 var d = this.elements[index];
11156                 if(d.dom){
11157                     d.remove();
11158                 }else{
11159                     d.parentNode.removeChild(d);
11160                 }
11161             }
11162             this.elements.splice(index, 1);
11163         }
11164         return this;
11165     },
11166
11167     /**
11168     * Replaces the specified element with the passed element.
11169     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11170     * to replace.
11171     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11172     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11173     * @return {CompositeElement} this
11174     */
11175     replaceElement : function(el, replacement, domReplace){
11176         var index = typeof el == 'number' ? el : this.indexOf(el);
11177         if(index !== -1){
11178             if(domReplace){
11179                 this.elements[index].replaceWith(replacement);
11180             }else{
11181                 this.elements.splice(index, 1, Roo.get(replacement))
11182             }
11183         }
11184         return this;
11185     },
11186
11187     /**
11188      * Removes all elements.
11189      */
11190     clear : function(){
11191         this.elements = [];
11192     }
11193 };
11194 (function(){
11195     Roo.CompositeElement.createCall = function(proto, fnName){
11196         if(!proto[fnName]){
11197             proto[fnName] = function(){
11198                 return this.invoke(fnName, arguments);
11199             };
11200         }
11201     };
11202     for(var fnName in Roo.Element.prototype){
11203         if(typeof Roo.Element.prototype[fnName] == "function"){
11204             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11205         }
11206     };
11207 })();
11208 /*
11209  * Based on:
11210  * Ext JS Library 1.1.1
11211  * Copyright(c) 2006-2007, Ext JS, LLC.
11212  *
11213  * Originally Released Under LGPL - original licence link has changed is not relivant.
11214  *
11215  * Fork - LGPL
11216  * <script type="text/javascript">
11217  */
11218
11219 /**
11220  * @class Roo.CompositeElementLite
11221  * @extends Roo.CompositeElement
11222  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11223  <pre><code>
11224  var els = Roo.select("#some-el div.some-class");
11225  // or select directly from an existing element
11226  var el = Roo.get('some-el');
11227  el.select('div.some-class');
11228
11229  els.setWidth(100); // all elements become 100 width
11230  els.hide(true); // all elements fade out and hide
11231  // or
11232  els.setWidth(100).hide(true);
11233  </code></pre><br><br>
11234  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11235  * actions will be performed on all the elements in this collection.</b>
11236  */
11237 Roo.CompositeElementLite = function(els){
11238     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11239     this.el = new Roo.Element.Flyweight();
11240 };
11241 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11242     addElements : function(els){
11243         if(els){
11244             if(els instanceof Array){
11245                 this.elements = this.elements.concat(els);
11246             }else{
11247                 var yels = this.elements;
11248                 var index = yels.length-1;
11249                 for(var i = 0, len = els.length; i < len; i++) {
11250                     yels[++index] = els[i];
11251                 }
11252             }
11253         }
11254         return this;
11255     },
11256     invoke : function(fn, args){
11257         var els = this.elements;
11258         var el = this.el;
11259         for(var i = 0, len = els.length; i < len; i++) {
11260             el.dom = els[i];
11261                 Roo.Element.prototype[fn].apply(el, args);
11262         }
11263         return this;
11264     },
11265     /**
11266      * Returns a flyweight Element of the dom element object at the specified index
11267      * @param {Number} index
11268      * @return {Roo.Element}
11269      */
11270     item : function(index){
11271         if(!this.elements[index]){
11272             return null;
11273         }
11274         this.el.dom = this.elements[index];
11275         return this.el;
11276     },
11277
11278     // fixes scope with flyweight
11279     addListener : function(eventName, handler, scope, opt){
11280         var els = this.elements;
11281         for(var i = 0, len = els.length; i < len; i++) {
11282             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11283         }
11284         return this;
11285     },
11286
11287     /**
11288     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11289     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11290     * a reference to the dom node, use el.dom.</b>
11291     * @param {Function} fn The function to call
11292     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11293     * @return {CompositeElement} this
11294     */
11295     each : function(fn, scope){
11296         var els = this.elements;
11297         var el = this.el;
11298         for(var i = 0, len = els.length; i < len; i++){
11299             el.dom = els[i];
11300                 if(fn.call(scope || el, el, this, i) === false){
11301                 break;
11302             }
11303         }
11304         return this;
11305     },
11306
11307     indexOf : function(el){
11308         return this.elements.indexOf(Roo.getDom(el));
11309     },
11310
11311     replaceElement : function(el, replacement, domReplace){
11312         var index = typeof el == 'number' ? el : this.indexOf(el);
11313         if(index !== -1){
11314             replacement = Roo.getDom(replacement);
11315             if(domReplace){
11316                 var d = this.elements[index];
11317                 d.parentNode.insertBefore(replacement, d);
11318                 d.parentNode.removeChild(d);
11319             }
11320             this.elements.splice(index, 1, replacement);
11321         }
11322         return this;
11323     }
11324 });
11325 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11326
11327 /*
11328  * Based on:
11329  * Ext JS Library 1.1.1
11330  * Copyright(c) 2006-2007, Ext JS, LLC.
11331  *
11332  * Originally Released Under LGPL - original licence link has changed is not relivant.
11333  *
11334  * Fork - LGPL
11335  * <script type="text/javascript">
11336  */
11337
11338  
11339
11340 /**
11341  * @class Roo.data.Connection
11342  * @extends Roo.util.Observable
11343  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11344  * either to a configured URL, or to a URL specified at request time.<br><br>
11345  * <p>
11346  * Requests made by this class are asynchronous, and will return immediately. No data from
11347  * the server will be available to the statement immediately following the {@link #request} call.
11348  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11349  * <p>
11350  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11351  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11352  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11353  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11354  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11355  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11356  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11357  * standard DOM methods.
11358  * @constructor
11359  * @param {Object} config a configuration object.
11360  */
11361 Roo.data.Connection = function(config){
11362     Roo.apply(this, config);
11363     this.addEvents({
11364         /**
11365          * @event beforerequest
11366          * Fires before a network request is made to retrieve a data object.
11367          * @param {Connection} conn This Connection object.
11368          * @param {Object} options The options config object passed to the {@link #request} method.
11369          */
11370         "beforerequest" : true,
11371         /**
11372          * @event requestcomplete
11373          * Fires if the request was successfully completed.
11374          * @param {Connection} conn This Connection object.
11375          * @param {Object} response The XHR object containing the response data.
11376          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11377          * @param {Object} options The options config object passed to the {@link #request} method.
11378          */
11379         "requestcomplete" : true,
11380         /**
11381          * @event requestexception
11382          * Fires if an error HTTP status was returned from the server.
11383          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11384          * @param {Connection} conn This Connection object.
11385          * @param {Object} response The XHR object containing the response data.
11386          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11387          * @param {Object} options The options config object passed to the {@link #request} method.
11388          */
11389         "requestexception" : true
11390     });
11391     Roo.data.Connection.superclass.constructor.call(this);
11392 };
11393
11394 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11395     /**
11396      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11397      */
11398     /**
11399      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11400      * extra parameters to each request made by this object. (defaults to undefined)
11401      */
11402     /**
11403      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11404      *  to each request made by this object. (defaults to undefined)
11405      */
11406     /**
11407      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11408      */
11409     /**
11410      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11411      */
11412     timeout : 30000,
11413     /**
11414      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11415      * @type Boolean
11416      */
11417     autoAbort:false,
11418
11419     /**
11420      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11421      * @type Boolean
11422      */
11423     disableCaching: true,
11424
11425     /**
11426      * Sends an HTTP request to a remote server.
11427      * @param {Object} options An object which may contain the following properties:<ul>
11428      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11429      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11430      * request, a url encoded string or a function to call to get either.</li>
11431      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11432      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11433      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11434      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11435      * <li>options {Object} The parameter to the request call.</li>
11436      * <li>success {Boolean} True if the request succeeded.</li>
11437      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11438      * </ul></li>
11439      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11440      * The callback is passed the following parameters:<ul>
11441      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11442      * <li>options {Object} The parameter to the request call.</li>
11443      * </ul></li>
11444      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11445      * The callback is passed the following parameters:<ul>
11446      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11447      * <li>options {Object} The parameter to the request call.</li>
11448      * </ul></li>
11449      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11450      * for the callback function. Defaults to the browser window.</li>
11451      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11452      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11453      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11454      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11455      * params for the post data. Any params will be appended to the URL.</li>
11456      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11457      * </ul>
11458      * @return {Number} transactionId
11459      */
11460     request : function(o){
11461         if(this.fireEvent("beforerequest", this, o) !== false){
11462             var p = o.params;
11463
11464             if(typeof p == "function"){
11465                 p = p.call(o.scope||window, o);
11466             }
11467             if(typeof p == "object"){
11468                 p = Roo.urlEncode(o.params);
11469             }
11470             if(this.extraParams){
11471                 var extras = Roo.urlEncode(this.extraParams);
11472                 p = p ? (p + '&' + extras) : extras;
11473             }
11474
11475             var url = o.url || this.url;
11476             if(typeof url == 'function'){
11477                 url = url.call(o.scope||window, o);
11478             }
11479
11480             if(o.form){
11481                 var form = Roo.getDom(o.form);
11482                 url = url || form.action;
11483
11484                 var enctype = form.getAttribute("enctype");
11485                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11486                     return this.doFormUpload(o, p, url);
11487                 }
11488                 var f = Roo.lib.Ajax.serializeForm(form);
11489                 p = p ? (p + '&' + f) : f;
11490             }
11491
11492             var hs = o.headers;
11493             if(this.defaultHeaders){
11494                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11495                 if(!o.headers){
11496                     o.headers = hs;
11497                 }
11498             }
11499
11500             var cb = {
11501                 success: this.handleResponse,
11502                 failure: this.handleFailure,
11503                 scope: this,
11504                 argument: {options: o},
11505                 timeout : o.timeout || this.timeout
11506             };
11507
11508             var method = o.method||this.method||(p ? "POST" : "GET");
11509
11510             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11511                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11512             }
11513
11514             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11515                 if(o.autoAbort){
11516                     this.abort();
11517                 }
11518             }else if(this.autoAbort !== false){
11519                 this.abort();
11520             }
11521
11522             if((method == 'GET' && p) || o.xmlData){
11523                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11524                 p = '';
11525             }
11526             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11527             return this.transId;
11528         }else{
11529             Roo.callback(o.callback, o.scope, [o, null, null]);
11530             return null;
11531         }
11532     },
11533
11534     /**
11535      * Determine whether this object has a request outstanding.
11536      * @param {Number} transactionId (Optional) defaults to the last transaction
11537      * @return {Boolean} True if there is an outstanding request.
11538      */
11539     isLoading : function(transId){
11540         if(transId){
11541             return Roo.lib.Ajax.isCallInProgress(transId);
11542         }else{
11543             return this.transId ? true : false;
11544         }
11545     },
11546
11547     /**
11548      * Aborts any outstanding request.
11549      * @param {Number} transactionId (Optional) defaults to the last transaction
11550      */
11551     abort : function(transId){
11552         if(transId || this.isLoading()){
11553             Roo.lib.Ajax.abort(transId || this.transId);
11554         }
11555     },
11556
11557     // private
11558     handleResponse : function(response){
11559         this.transId = false;
11560         var options = response.argument.options;
11561         response.argument = options ? options.argument : null;
11562         this.fireEvent("requestcomplete", this, response, options);
11563         Roo.callback(options.success, options.scope, [response, options]);
11564         Roo.callback(options.callback, options.scope, [options, true, response]);
11565     },
11566
11567     // private
11568     handleFailure : function(response, e){
11569         this.transId = false;
11570         var options = response.argument.options;
11571         response.argument = options ? options.argument : null;
11572         this.fireEvent("requestexception", this, response, options, e);
11573         Roo.callback(options.failure, options.scope, [response, options]);
11574         Roo.callback(options.callback, options.scope, [options, false, response]);
11575     },
11576
11577     // private
11578     doFormUpload : function(o, ps, url){
11579         var id = Roo.id();
11580         var frame = document.createElement('iframe');
11581         frame.id = id;
11582         frame.name = id;
11583         frame.className = 'x-hidden';
11584         if(Roo.isIE){
11585             frame.src = Roo.SSL_SECURE_URL;
11586         }
11587         document.body.appendChild(frame);
11588
11589         if(Roo.isIE){
11590            document.frames[id].name = id;
11591         }
11592
11593         var form = Roo.getDom(o.form);
11594         form.target = id;
11595         form.method = 'POST';
11596         form.enctype = form.encoding = 'multipart/form-data';
11597         if(url){
11598             form.action = url;
11599         }
11600
11601         var hiddens, hd;
11602         if(ps){ // add dynamic params
11603             hiddens = [];
11604             ps = Roo.urlDecode(ps, false);
11605             for(var k in ps){
11606                 if(ps.hasOwnProperty(k)){
11607                     hd = document.createElement('input');
11608                     hd.type = 'hidden';
11609                     hd.name = k;
11610                     hd.value = ps[k];
11611                     form.appendChild(hd);
11612                     hiddens.push(hd);
11613                 }
11614             }
11615         }
11616
11617         function cb(){
11618             var r = {  // bogus response object
11619                 responseText : '',
11620                 responseXML : null
11621             };
11622
11623             r.argument = o ? o.argument : null;
11624
11625             try { //
11626                 var doc;
11627                 if(Roo.isIE){
11628                     doc = frame.contentWindow.document;
11629                 }else {
11630                     doc = (frame.contentDocument || window.frames[id].document);
11631                 }
11632                 if(doc && doc.body){
11633                     r.responseText = doc.body.innerHTML;
11634                 }
11635                 if(doc && doc.XMLDocument){
11636                     r.responseXML = doc.XMLDocument;
11637                 }else {
11638                     r.responseXML = doc;
11639                 }
11640             }
11641             catch(e) {
11642                 // ignore
11643             }
11644
11645             Roo.EventManager.removeListener(frame, 'load', cb, this);
11646
11647             this.fireEvent("requestcomplete", this, r, o);
11648             Roo.callback(o.success, o.scope, [r, o]);
11649             Roo.callback(o.callback, o.scope, [o, true, r]);
11650
11651             setTimeout(function(){document.body.removeChild(frame);}, 100);
11652         }
11653
11654         Roo.EventManager.on(frame, 'load', cb, this);
11655         form.submit();
11656
11657         if(hiddens){ // remove dynamic params
11658             for(var i = 0, len = hiddens.length; i < len; i++){
11659                 form.removeChild(hiddens[i]);
11660             }
11661         }
11662     }
11663 });
11664 /*
11665  * Based on:
11666  * Ext JS Library 1.1.1
11667  * Copyright(c) 2006-2007, Ext JS, LLC.
11668  *
11669  * Originally Released Under LGPL - original licence link has changed is not relivant.
11670  *
11671  * Fork - LGPL
11672  * <script type="text/javascript">
11673  */
11674  
11675 /**
11676  * Global Ajax request class.
11677  * 
11678  * @class Roo.Ajax
11679  * @extends Roo.data.Connection
11680  * @static
11681  * 
11682  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11683  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11684  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11685  * @cfg {String} method (Optional)  The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11686  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11687  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11688  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11689  */
11690 Roo.Ajax = new Roo.data.Connection({
11691     // fix up the docs
11692     /**
11693      * @scope Roo.Ajax
11694      * @type {Boolear} 
11695      */
11696     autoAbort : false,
11697
11698     /**
11699      * Serialize the passed form into a url encoded string
11700      * @scope Roo.Ajax
11701      * @param {String/HTMLElement} form
11702      * @return {String}
11703      */
11704     serializeForm : function(form){
11705         return Roo.lib.Ajax.serializeForm(form);
11706     }
11707 });/*
11708  * Based on:
11709  * Ext JS Library 1.1.1
11710  * Copyright(c) 2006-2007, Ext JS, LLC.
11711  *
11712  * Originally Released Under LGPL - original licence link has changed is not relivant.
11713  *
11714  * Fork - LGPL
11715  * <script type="text/javascript">
11716  */
11717
11718  
11719 /**
11720  * @class Roo.UpdateManager
11721  * @extends Roo.util.Observable
11722  * Provides AJAX-style update for Element object.<br><br>
11723  * Usage:<br>
11724  * <pre><code>
11725  * // Get it from a Roo.Element object
11726  * var el = Roo.get("foo");
11727  * var mgr = el.getUpdateManager();
11728  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11729  * ...
11730  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11731  * <br>
11732  * // or directly (returns the same UpdateManager instance)
11733  * var mgr = new Roo.UpdateManager("myElementId");
11734  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11735  * mgr.on("update", myFcnNeedsToKnow);
11736  * <br>
11737    // short handed call directly from the element object
11738    Roo.get("foo").load({
11739         url: "bar.php",
11740         scripts:true,
11741         params: "for=bar",
11742         text: "Loading Foo..."
11743    });
11744  * </code></pre>
11745  * @constructor
11746  * Create new UpdateManager directly.
11747  * @param {String/HTMLElement/Roo.Element} el The element to update
11748  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11749  */
11750 Roo.UpdateManager = function(el, forceNew){
11751     el = Roo.get(el);
11752     if(!forceNew && el.updateManager){
11753         return el.updateManager;
11754     }
11755     /**
11756      * The Element object
11757      * @type Roo.Element
11758      */
11759     this.el = el;
11760     /**
11761      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11762      * @type String
11763      */
11764     this.defaultUrl = null;
11765
11766     this.addEvents({
11767         /**
11768          * @event beforeupdate
11769          * Fired before an update is made, return false from your handler and the update is cancelled.
11770          * @param {Roo.Element} el
11771          * @param {String/Object/Function} url
11772          * @param {String/Object} params
11773          */
11774         "beforeupdate": true,
11775         /**
11776          * @event update
11777          * Fired after successful update is made.
11778          * @param {Roo.Element} el
11779          * @param {Object} oResponseObject The response Object
11780          */
11781         "update": true,
11782         /**
11783          * @event failure
11784          * Fired on update failure.
11785          * @param {Roo.Element} el
11786          * @param {Object} oResponseObject The response Object
11787          */
11788         "failure": true
11789     });
11790     var d = Roo.UpdateManager.defaults;
11791     /**
11792      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11793      * @type String
11794      */
11795     this.sslBlankUrl = d.sslBlankUrl;
11796     /**
11797      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11798      * @type Boolean
11799      */
11800     this.disableCaching = d.disableCaching;
11801     /**
11802      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11803      * @type String
11804      */
11805     this.indicatorText = d.indicatorText;
11806     /**
11807      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11808      * @type String
11809      */
11810     this.showLoadIndicator = d.showLoadIndicator;
11811     /**
11812      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11813      * @type Number
11814      */
11815     this.timeout = d.timeout;
11816
11817     /**
11818      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11819      * @type Boolean
11820      */
11821     this.loadScripts = d.loadScripts;
11822
11823     /**
11824      * Transaction object of current executing transaction
11825      */
11826     this.transaction = null;
11827
11828     /**
11829      * @private
11830      */
11831     this.autoRefreshProcId = null;
11832     /**
11833      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11834      * @type Function
11835      */
11836     this.refreshDelegate = this.refresh.createDelegate(this);
11837     /**
11838      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11839      * @type Function
11840      */
11841     this.updateDelegate = this.update.createDelegate(this);
11842     /**
11843      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11844      * @type Function
11845      */
11846     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11847     /**
11848      * @private
11849      */
11850     this.successDelegate = this.processSuccess.createDelegate(this);
11851     /**
11852      * @private
11853      */
11854     this.failureDelegate = this.processFailure.createDelegate(this);
11855
11856     if(!this.renderer){
11857      /**
11858       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11859       */
11860     this.renderer = new Roo.UpdateManager.BasicRenderer();
11861     }
11862     
11863     Roo.UpdateManager.superclass.constructor.call(this);
11864 };
11865
11866 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11867     /**
11868      * Get the Element this UpdateManager is bound to
11869      * @return {Roo.Element} The element
11870      */
11871     getEl : function(){
11872         return this.el;
11873     },
11874     /**
11875      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11876      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
11877 <pre><code>
11878 um.update({<br/>
11879     url: "your-url.php",<br/>
11880     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11881     callback: yourFunction,<br/>
11882     scope: yourObject, //(optional scope)  <br/>
11883     discardUrl: false, <br/>
11884     nocache: false,<br/>
11885     text: "Loading...",<br/>
11886     timeout: 30,<br/>
11887     scripts: false<br/>
11888 });
11889 </code></pre>
11890      * The only required property is url. The optional properties nocache, text and scripts
11891      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11892      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
11893      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11894      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
11895      */
11896     update : function(url, params, callback, discardUrl){
11897         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11898             var method = this.method,
11899                 cfg;
11900             if(typeof url == "object"){ // must be config object
11901                 cfg = url;
11902                 url = cfg.url;
11903                 params = params || cfg.params;
11904                 callback = callback || cfg.callback;
11905                 discardUrl = discardUrl || cfg.discardUrl;
11906                 if(callback && cfg.scope){
11907                     callback = callback.createDelegate(cfg.scope);
11908                 }
11909                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11910                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11911                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11912                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11913                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11914             }
11915             this.showLoading();
11916             if(!discardUrl){
11917                 this.defaultUrl = url;
11918             }
11919             if(typeof url == "function"){
11920                 url = url.call(this);
11921             }
11922
11923             method = method || (params ? "POST" : "GET");
11924             if(method == "GET"){
11925                 url = this.prepareUrl(url);
11926             }
11927
11928             var o = Roo.apply(cfg ||{}, {
11929                 url : url,
11930                 params: params,
11931                 success: this.successDelegate,
11932                 failure: this.failureDelegate,
11933                 callback: undefined,
11934                 timeout: (this.timeout*1000),
11935                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11936             });
11937             Roo.log("updated manager called with timeout of " + o.timeout);
11938             this.transaction = Roo.Ajax.request(o);
11939         }
11940     },
11941
11942     /**
11943      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
11944      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11945      * @param {String/HTMLElement} form The form Id or form element
11946      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11947      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11948      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11949      */
11950     formUpdate : function(form, url, reset, callback){
11951         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11952             if(typeof url == "function"){
11953                 url = url.call(this);
11954             }
11955             form = Roo.getDom(form);
11956             this.transaction = Roo.Ajax.request({
11957                 form: form,
11958                 url:url,
11959                 success: this.successDelegate,
11960                 failure: this.failureDelegate,
11961                 timeout: (this.timeout*1000),
11962                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11963             });
11964             this.showLoading.defer(1, this);
11965         }
11966     },
11967
11968     /**
11969      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11970      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11971      */
11972     refresh : function(callback){
11973         if(this.defaultUrl == null){
11974             return;
11975         }
11976         this.update(this.defaultUrl, null, callback, true);
11977     },
11978
11979     /**
11980      * Set this element to auto refresh.
11981      * @param {Number} interval How often to update (in seconds).
11982      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
11983      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
11984      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11985      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11986      */
11987     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11988         if(refreshNow){
11989             this.update(url || this.defaultUrl, params, callback, true);
11990         }
11991         if(this.autoRefreshProcId){
11992             clearInterval(this.autoRefreshProcId);
11993         }
11994         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11995     },
11996
11997     /**
11998      * Stop auto refresh on this element.
11999      */
12000      stopAutoRefresh : function(){
12001         if(this.autoRefreshProcId){
12002             clearInterval(this.autoRefreshProcId);
12003             delete this.autoRefreshProcId;
12004         }
12005     },
12006
12007     isAutoRefreshing : function(){
12008        return this.autoRefreshProcId ? true : false;
12009     },
12010     /**
12011      * Called to update the element to "Loading" state. Override to perform custom action.
12012      */
12013     showLoading : function(){
12014         if(this.showLoadIndicator){
12015             this.el.update(this.indicatorText);
12016         }
12017     },
12018
12019     /**
12020      * Adds unique parameter to query string if disableCaching = true
12021      * @private
12022      */
12023     prepareUrl : function(url){
12024         if(this.disableCaching){
12025             var append = "_dc=" + (new Date().getTime());
12026             if(url.indexOf("?") !== -1){
12027                 url += "&" + append;
12028             }else{
12029                 url += "?" + append;
12030             }
12031         }
12032         return url;
12033     },
12034
12035     /**
12036      * @private
12037      */
12038     processSuccess : function(response){
12039         this.transaction = null;
12040         if(response.argument.form && response.argument.reset){
12041             try{ // put in try/catch since some older FF releases had problems with this
12042                 response.argument.form.reset();
12043             }catch(e){}
12044         }
12045         if(this.loadScripts){
12046             this.renderer.render(this.el, response, this,
12047                 this.updateComplete.createDelegate(this, [response]));
12048         }else{
12049             this.renderer.render(this.el, response, this);
12050             this.updateComplete(response);
12051         }
12052     },
12053
12054     updateComplete : function(response){
12055         this.fireEvent("update", this.el, response);
12056         if(typeof response.argument.callback == "function"){
12057             response.argument.callback(this.el, true, response);
12058         }
12059     },
12060
12061     /**
12062      * @private
12063      */
12064     processFailure : function(response){
12065         this.transaction = null;
12066         this.fireEvent("failure", this.el, response);
12067         if(typeof response.argument.callback == "function"){
12068             response.argument.callback(this.el, false, response);
12069         }
12070     },
12071
12072     /**
12073      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12074      * @param {Object} renderer The object implementing the render() method
12075      */
12076     setRenderer : function(renderer){
12077         this.renderer = renderer;
12078     },
12079
12080     getRenderer : function(){
12081        return this.renderer;
12082     },
12083
12084     /**
12085      * Set the defaultUrl used for updates
12086      * @param {String/Function} defaultUrl The url or a function to call to get the url
12087      */
12088     setDefaultUrl : function(defaultUrl){
12089         this.defaultUrl = defaultUrl;
12090     },
12091
12092     /**
12093      * Aborts the executing transaction
12094      */
12095     abort : function(){
12096         if(this.transaction){
12097             Roo.Ajax.abort(this.transaction);
12098         }
12099     },
12100
12101     /**
12102      * Returns true if an update is in progress
12103      * @return {Boolean}
12104      */
12105     isUpdating : function(){
12106         if(this.transaction){
12107             return Roo.Ajax.isLoading(this.transaction);
12108         }
12109         return false;
12110     }
12111 });
12112
12113 /**
12114  * @class Roo.UpdateManager.defaults
12115  * @static (not really - but it helps the doc tool)
12116  * The defaults collection enables customizing the default properties of UpdateManager
12117  */
12118    Roo.UpdateManager.defaults = {
12119        /**
12120          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12121          * @type Number
12122          */
12123          timeout : 30,
12124
12125          /**
12126          * True to process scripts by default (Defaults to false).
12127          * @type Boolean
12128          */
12129         loadScripts : false,
12130
12131         /**
12132         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12133         * @type String
12134         */
12135         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12136         /**
12137          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12138          * @type Boolean
12139          */
12140         disableCaching : false,
12141         /**
12142          * Whether to show indicatorText when loading (Defaults to true).
12143          * @type Boolean
12144          */
12145         showLoadIndicator : true,
12146         /**
12147          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12148          * @type String
12149          */
12150         indicatorText : '<div class="loading-indicator">Loading...</div>'
12151    };
12152
12153 /**
12154  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12155  *Usage:
12156  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12157  * @param {String/HTMLElement/Roo.Element} el The element to update
12158  * @param {String} url The url
12159  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12160  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12161  * @static
12162  * @deprecated
12163  * @member Roo.UpdateManager
12164  */
12165 Roo.UpdateManager.updateElement = function(el, url, params, options){
12166     var um = Roo.get(el, true).getUpdateManager();
12167     Roo.apply(um, options);
12168     um.update(url, params, options ? options.callback : null);
12169 };
12170 // alias for backwards compat
12171 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12172 /**
12173  * @class Roo.UpdateManager.BasicRenderer
12174  * Default Content renderer. Updates the elements innerHTML with the responseText.
12175  */
12176 Roo.UpdateManager.BasicRenderer = function(){};
12177
12178 Roo.UpdateManager.BasicRenderer.prototype = {
12179     /**
12180      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12181      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12182      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12183      * @param {Roo.Element} el The element being rendered
12184      * @param {Object} response The YUI Connect response object
12185      * @param {UpdateManager} updateManager The calling update manager
12186      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12187      */
12188      render : function(el, response, updateManager, callback){
12189         el.update(response.responseText, updateManager.loadScripts, callback);
12190     }
12191 };
12192 /*
12193  * Based on:
12194  * Roo JS
12195  * (c)) Alan Knowles
12196  * Licence : LGPL
12197  */
12198
12199
12200 /**
12201  * @class Roo.DomTemplate
12202  * @extends Roo.Template
12203  * An effort at a dom based template engine..
12204  *
12205  * Similar to XTemplate, except it uses dom parsing to create the template..
12206  *
12207  * Supported features:
12208  *
12209  *  Tags:
12210
12211 <pre><code>
12212       {a_variable} - output encoded.
12213       {a_variable.format:("Y-m-d")} - call a method on the variable
12214       {a_variable:raw} - unencoded output
12215       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12216       {a_variable:this.method_on_template(...)} - call a method on the template object.
12217  
12218 </code></pre>
12219  *  The tpl tag:
12220 <pre><code>
12221         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12222         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12223         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12224         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12225   
12226 </code></pre>
12227  *      
12228  */
12229 Roo.DomTemplate = function()
12230 {
12231      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12232      if (this.html) {
12233         this.compile();
12234      }
12235 };
12236
12237
12238 Roo.extend(Roo.DomTemplate, Roo.Template, {
12239     /**
12240      * id counter for sub templates.
12241      */
12242     id : 0,
12243     /**
12244      * flag to indicate if dom parser is inside a pre,
12245      * it will strip whitespace if not.
12246      */
12247     inPre : false,
12248     
12249     /**
12250      * The various sub templates
12251      */
12252     tpls : false,
12253     
12254     
12255     
12256     /**
12257      *
12258      * basic tag replacing syntax
12259      * WORD:WORD()
12260      *
12261      * // you can fake an object call by doing this
12262      *  x.t:(test,tesT) 
12263      * 
12264      */
12265     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12266     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12267     
12268     iterChild : function (node, method) {
12269         
12270         var oldPre = this.inPre;
12271         if (node.tagName == 'PRE') {
12272             this.inPre = true;
12273         }
12274         for( var i = 0; i < node.childNodes.length; i++) {
12275             method.call(this, node.childNodes[i]);
12276         }
12277         this.inPre = oldPre;
12278     },
12279     
12280     
12281     
12282     /**
12283      * compile the template
12284      *
12285      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12286      *
12287      */
12288     compile: function()
12289     {
12290         var s = this.html;
12291         
12292         // covert the html into DOM...
12293         var doc = false;
12294         var div =false;
12295         try {
12296             doc = document.implementation.createHTMLDocument("");
12297             doc.documentElement.innerHTML =   this.html  ;
12298             div = doc.documentElement;
12299         } catch (e) {
12300             // old IE... - nasty -- it causes all sorts of issues.. with
12301             // images getting pulled from server..
12302             div = document.createElement('div');
12303             div.innerHTML = this.html;
12304         }
12305         //doc.documentElement.innerHTML = htmlBody
12306          
12307         
12308         
12309         this.tpls = [];
12310         var _t = this;
12311         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12312         
12313         var tpls = this.tpls;
12314         
12315         // create a top level template from the snippet..
12316         
12317         //Roo.log(div.innerHTML);
12318         
12319         var tpl = {
12320             uid : 'master',
12321             id : this.id++,
12322             attr : false,
12323             value : false,
12324             body : div.innerHTML,
12325             
12326             forCall : false,
12327             execCall : false,
12328             dom : div,
12329             isTop : true
12330             
12331         };
12332         tpls.unshift(tpl);
12333         
12334         
12335         // compile them...
12336         this.tpls = [];
12337         Roo.each(tpls, function(tp){
12338             this.compileTpl(tp);
12339             this.tpls[tp.id] = tp;
12340         }, this);
12341         
12342         this.master = tpls[0];
12343         return this;
12344         
12345         
12346     },
12347     
12348     compileNode : function(node, istop) {
12349         // test for
12350         //Roo.log(node);
12351         
12352         
12353         // skip anything not a tag..
12354         if (node.nodeType != 1) {
12355             if (node.nodeType == 3 && !this.inPre) {
12356                 // reduce white space..
12357                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12358                 
12359             }
12360             return;
12361         }
12362         
12363         var tpl = {
12364             uid : false,
12365             id : false,
12366             attr : false,
12367             value : false,
12368             body : '',
12369             
12370             forCall : false,
12371             execCall : false,
12372             dom : false,
12373             isTop : istop
12374             
12375             
12376         };
12377         
12378         
12379         switch(true) {
12380             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12381             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12382             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12383             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12384             // no default..
12385         }
12386         
12387         
12388         if (!tpl.attr) {
12389             // just itterate children..
12390             this.iterChild(node,this.compileNode);
12391             return;
12392         }
12393         tpl.uid = this.id++;
12394         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12395         node.removeAttribute('roo-'+ tpl.attr);
12396         if (tpl.attr != 'name') {
12397             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12398             node.parentNode.replaceChild(placeholder,  node);
12399         } else {
12400             
12401             var placeholder =  document.createElement('span');
12402             placeholder.className = 'roo-tpl-' + tpl.value;
12403             node.parentNode.replaceChild(placeholder,  node);
12404         }
12405         
12406         // parent now sees '{domtplXXXX}
12407         this.iterChild(node,this.compileNode);
12408         
12409         // we should now have node body...
12410         var div = document.createElement('div');
12411         div.appendChild(node);
12412         tpl.dom = node;
12413         // this has the unfortunate side effect of converting tagged attributes
12414         // eg. href="{...}" into %7C...%7D
12415         // this has been fixed by searching for those combo's although it's a bit hacky..
12416         
12417         
12418         tpl.body = div.innerHTML;
12419         
12420         
12421          
12422         tpl.id = tpl.uid;
12423         switch(tpl.attr) {
12424             case 'for' :
12425                 switch (tpl.value) {
12426                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12427                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12428                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12429                 }
12430                 break;
12431             
12432             case 'exec':
12433                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12434                 break;
12435             
12436             case 'if':     
12437                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12438                 break;
12439             
12440             case 'name':
12441                 tpl.id  = tpl.value; // replace non characters???
12442                 break;
12443             
12444         }
12445         
12446         
12447         this.tpls.push(tpl);
12448         
12449         
12450         
12451     },
12452     
12453     
12454     
12455     
12456     /**
12457      * Compile a segment of the template into a 'sub-template'
12458      *
12459      * 
12460      * 
12461      *
12462      */
12463     compileTpl : function(tpl)
12464     {
12465         var fm = Roo.util.Format;
12466         var useF = this.disableFormats !== true;
12467         
12468         var sep = Roo.isGecko ? "+\n" : ",\n";
12469         
12470         var undef = function(str) {
12471             Roo.debug && Roo.log("Property not found :"  + str);
12472             return '';
12473         };
12474           
12475         //Roo.log(tpl.body);
12476         
12477         
12478         
12479         var fn = function(m, lbrace, name, format, args)
12480         {
12481             //Roo.log("ARGS");
12482             //Roo.log(arguments);
12483             args = args ? args.replace(/\\'/g,"'") : args;
12484             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12485             if (typeof(format) == 'undefined') {
12486                 format =  'htmlEncode'; 
12487             }
12488             if (format == 'raw' ) {
12489                 format = false;
12490             }
12491             
12492             if(name.substr(0, 6) == 'domtpl'){
12493                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12494             }
12495             
12496             // build an array of options to determine if value is undefined..
12497             
12498             // basically get 'xxxx.yyyy' then do
12499             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12500             //    (function () { Roo.log("Property not found"); return ''; })() :
12501             //    ......
12502             
12503             var udef_ar = [];
12504             var lookfor = '';
12505             Roo.each(name.split('.'), function(st) {
12506                 lookfor += (lookfor.length ? '.': '') + st;
12507                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12508             });
12509             
12510             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12511             
12512             
12513             if(format && useF){
12514                 
12515                 args = args ? ',' + args : "";
12516                  
12517                 if(format.substr(0, 5) != "this."){
12518                     format = "fm." + format + '(';
12519                 }else{
12520                     format = 'this.call("'+ format.substr(5) + '", ';
12521                     args = ", values";
12522                 }
12523                 
12524                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12525             }
12526              
12527             if (args && args.length) {
12528                 // called with xxyx.yuu:(test,test)
12529                 // change to ()
12530                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12531             }
12532             // raw.. - :raw modifier..
12533             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12534             
12535         };
12536         var body;
12537         // branched to use + in gecko and [].join() in others
12538         if(Roo.isGecko){
12539             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12540                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12541                     "';};};";
12542         }else{
12543             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12544             body.push(tpl.body.replace(/(\r\n|\n)/g,
12545                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12546             body.push("'].join('');};};");
12547             body = body.join('');
12548         }
12549         
12550         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12551        
12552         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12553         eval(body);
12554         
12555         return this;
12556     },
12557      
12558     /**
12559      * same as applyTemplate, except it's done to one of the subTemplates
12560      * when using named templates, you can do:
12561      *
12562      * var str = pl.applySubTemplate('your-name', values);
12563      *
12564      * 
12565      * @param {Number} id of the template
12566      * @param {Object} values to apply to template
12567      * @param {Object} parent (normaly the instance of this object)
12568      */
12569     applySubTemplate : function(id, values, parent)
12570     {
12571         
12572         
12573         var t = this.tpls[id];
12574         
12575         
12576         try { 
12577             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12578                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12579                 return '';
12580             }
12581         } catch(e) {
12582             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12583             Roo.log(values);
12584           
12585             return '';
12586         }
12587         try { 
12588             
12589             if(t.execCall && t.execCall.call(this, values, parent)){
12590                 return '';
12591             }
12592         } catch(e) {
12593             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12594             Roo.log(values);
12595             return '';
12596         }
12597         
12598         try {
12599             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12600             parent = t.target ? values : parent;
12601             if(t.forCall && vs instanceof Array){
12602                 var buf = [];
12603                 for(var i = 0, len = vs.length; i < len; i++){
12604                     try {
12605                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12606                     } catch (e) {
12607                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12608                         Roo.log(e.body);
12609                         //Roo.log(t.compiled);
12610                         Roo.log(vs[i]);
12611                     }   
12612                 }
12613                 return buf.join('');
12614             }
12615         } catch (e) {
12616             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12617             Roo.log(values);
12618             return '';
12619         }
12620         try {
12621             return t.compiled.call(this, vs, parent);
12622         } catch (e) {
12623             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12624             Roo.log(e.body);
12625             //Roo.log(t.compiled);
12626             Roo.log(values);
12627             return '';
12628         }
12629     },
12630
12631    
12632
12633     applyTemplate : function(values){
12634         return this.master.compiled.call(this, values, {});
12635         //var s = this.subs;
12636     },
12637
12638     apply : function(){
12639         return this.applyTemplate.apply(this, arguments);
12640     }
12641
12642  });
12643
12644 Roo.DomTemplate.from = function(el){
12645     el = Roo.getDom(el);
12646     return new Roo.Domtemplate(el.value || el.innerHTML);
12647 };/*
12648  * Based on:
12649  * Ext JS Library 1.1.1
12650  * Copyright(c) 2006-2007, Ext JS, LLC.
12651  *
12652  * Originally Released Under LGPL - original licence link has changed is not relivant.
12653  *
12654  * Fork - LGPL
12655  * <script type="text/javascript">
12656  */
12657
12658 /**
12659  * @class Roo.util.DelayedTask
12660  * Provides a convenient method of performing setTimeout where a new
12661  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12662  * You can use this class to buffer
12663  * the keypress events for a certain number of milliseconds, and perform only if they stop
12664  * for that amount of time.
12665  * @constructor The parameters to this constructor serve as defaults and are not required.
12666  * @param {Function} fn (optional) The default function to timeout
12667  * @param {Object} scope (optional) The default scope of that timeout
12668  * @param {Array} args (optional) The default Array of arguments
12669  */
12670 Roo.util.DelayedTask = function(fn, scope, args){
12671     var id = null, d, t;
12672
12673     var call = function(){
12674         var now = new Date().getTime();
12675         if(now - t >= d){
12676             clearInterval(id);
12677             id = null;
12678             fn.apply(scope, args || []);
12679         }
12680     };
12681     /**
12682      * Cancels any pending timeout and queues a new one
12683      * @param {Number} delay The milliseconds to delay
12684      * @param {Function} newFn (optional) Overrides function passed to constructor
12685      * @param {Object} newScope (optional) Overrides scope passed to constructor
12686      * @param {Array} newArgs (optional) Overrides args passed to constructor
12687      */
12688     this.delay = function(delay, newFn, newScope, newArgs){
12689         if(id && delay != d){
12690             this.cancel();
12691         }
12692         d = delay;
12693         t = new Date().getTime();
12694         fn = newFn || fn;
12695         scope = newScope || scope;
12696         args = newArgs || args;
12697         if(!id){
12698             id = setInterval(call, d);
12699         }
12700     };
12701
12702     /**
12703      * Cancel the last queued timeout
12704      */
12705     this.cancel = function(){
12706         if(id){
12707             clearInterval(id);
12708             id = null;
12709         }
12710     };
12711 };/*
12712  * Based on:
12713  * Ext JS Library 1.1.1
12714  * Copyright(c) 2006-2007, Ext JS, LLC.
12715  *
12716  * Originally Released Under LGPL - original licence link has changed is not relivant.
12717  *
12718  * Fork - LGPL
12719  * <script type="text/javascript">
12720  */
12721  
12722  
12723 Roo.util.TaskRunner = function(interval){
12724     interval = interval || 10;
12725     var tasks = [], removeQueue = [];
12726     var id = 0;
12727     var running = false;
12728
12729     var stopThread = function(){
12730         running = false;
12731         clearInterval(id);
12732         id = 0;
12733     };
12734
12735     var startThread = function(){
12736         if(!running){
12737             running = true;
12738             id = setInterval(runTasks, interval);
12739         }
12740     };
12741
12742     var removeTask = function(task){
12743         removeQueue.push(task);
12744         if(task.onStop){
12745             task.onStop();
12746         }
12747     };
12748
12749     var runTasks = function(){
12750         if(removeQueue.length > 0){
12751             for(var i = 0, len = removeQueue.length; i < len; i++){
12752                 tasks.remove(removeQueue[i]);
12753             }
12754             removeQueue = [];
12755             if(tasks.length < 1){
12756                 stopThread();
12757                 return;
12758             }
12759         }
12760         var now = new Date().getTime();
12761         for(var i = 0, len = tasks.length; i < len; ++i){
12762             var t = tasks[i];
12763             var itime = now - t.taskRunTime;
12764             if(t.interval <= itime){
12765                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12766                 t.taskRunTime = now;
12767                 if(rt === false || t.taskRunCount === t.repeat){
12768                     removeTask(t);
12769                     return;
12770                 }
12771             }
12772             if(t.duration && t.duration <= (now - t.taskStartTime)){
12773                 removeTask(t);
12774             }
12775         }
12776     };
12777
12778     /**
12779      * Queues a new task.
12780      * @param {Object} task
12781      */
12782     this.start = function(task){
12783         tasks.push(task);
12784         task.taskStartTime = new Date().getTime();
12785         task.taskRunTime = 0;
12786         task.taskRunCount = 0;
12787         startThread();
12788         return task;
12789     };
12790
12791     this.stop = function(task){
12792         removeTask(task);
12793         return task;
12794     };
12795
12796     this.stopAll = function(){
12797         stopThread();
12798         for(var i = 0, len = tasks.length; i < len; i++){
12799             if(tasks[i].onStop){
12800                 tasks[i].onStop();
12801             }
12802         }
12803         tasks = [];
12804         removeQueue = [];
12805     };
12806 };
12807
12808 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12809  * Based on:
12810  * Ext JS Library 1.1.1
12811  * Copyright(c) 2006-2007, Ext JS, LLC.
12812  *
12813  * Originally Released Under LGPL - original licence link has changed is not relivant.
12814  *
12815  * Fork - LGPL
12816  * <script type="text/javascript">
12817  */
12818
12819  
12820 /**
12821  * @class Roo.util.MixedCollection
12822  * @extends Roo.util.Observable
12823  * A Collection class that maintains both numeric indexes and keys and exposes events.
12824  * @constructor
12825  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12826  * collection (defaults to false)
12827  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12828  * and return the key value for that item.  This is used when available to look up the key on items that
12829  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12830  * equivalent to providing an implementation for the {@link #getKey} method.
12831  */
12832 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12833     this.items = [];
12834     this.map = {};
12835     this.keys = [];
12836     this.length = 0;
12837     this.addEvents({
12838         /**
12839          * @event clear
12840          * Fires when the collection is cleared.
12841          */
12842         "clear" : true,
12843         /**
12844          * @event add
12845          * Fires when an item is added to the collection.
12846          * @param {Number} index The index at which the item was added.
12847          * @param {Object} o The item added.
12848          * @param {String} key The key associated with the added item.
12849          */
12850         "add" : true,
12851         /**
12852          * @event replace
12853          * Fires when an item is replaced in the collection.
12854          * @param {String} key he key associated with the new added.
12855          * @param {Object} old The item being replaced.
12856          * @param {Object} new The new item.
12857          */
12858         "replace" : true,
12859         /**
12860          * @event remove
12861          * Fires when an item is removed from the collection.
12862          * @param {Object} o The item being removed.
12863          * @param {String} key (optional) The key associated with the removed item.
12864          */
12865         "remove" : true,
12866         "sort" : true
12867     });
12868     this.allowFunctions = allowFunctions === true;
12869     if(keyFn){
12870         this.getKey = keyFn;
12871     }
12872     Roo.util.MixedCollection.superclass.constructor.call(this);
12873 };
12874
12875 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12876     allowFunctions : false,
12877     
12878 /**
12879  * Adds an item to the collection.
12880  * @param {String} key The key to associate with the item
12881  * @param {Object} o The item to add.
12882  * @return {Object} The item added.
12883  */
12884     add : function(key, o){
12885         if(arguments.length == 1){
12886             o = arguments[0];
12887             key = this.getKey(o);
12888         }
12889         if(typeof key == "undefined" || key === null){
12890             this.length++;
12891             this.items.push(o);
12892             this.keys.push(null);
12893         }else{
12894             var old = this.map[key];
12895             if(old){
12896                 return this.replace(key, o);
12897             }
12898             this.length++;
12899             this.items.push(o);
12900             this.map[key] = o;
12901             this.keys.push(key);
12902         }
12903         this.fireEvent("add", this.length-1, o, key);
12904         return o;
12905     },
12906        
12907 /**
12908   * MixedCollection has a generic way to fetch keys if you implement getKey.
12909 <pre><code>
12910 // normal way
12911 var mc = new Roo.util.MixedCollection();
12912 mc.add(someEl.dom.id, someEl);
12913 mc.add(otherEl.dom.id, otherEl);
12914 //and so on
12915
12916 // using getKey
12917 var mc = new Roo.util.MixedCollection();
12918 mc.getKey = function(el){
12919    return el.dom.id;
12920 };
12921 mc.add(someEl);
12922 mc.add(otherEl);
12923
12924 // or via the constructor
12925 var mc = new Roo.util.MixedCollection(false, function(el){
12926    return el.dom.id;
12927 });
12928 mc.add(someEl);
12929 mc.add(otherEl);
12930 </code></pre>
12931  * @param o {Object} The item for which to find the key.
12932  * @return {Object} The key for the passed item.
12933  */
12934     getKey : function(o){
12935          return o.id; 
12936     },
12937    
12938 /**
12939  * Replaces an item in the collection.
12940  * @param {String} key The key associated with the item to replace, or the item to replace.
12941  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12942  * @return {Object}  The new item.
12943  */
12944     replace : function(key, o){
12945         if(arguments.length == 1){
12946             o = arguments[0];
12947             key = this.getKey(o);
12948         }
12949         var old = this.item(key);
12950         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12951              return this.add(key, o);
12952         }
12953         var index = this.indexOfKey(key);
12954         this.items[index] = o;
12955         this.map[key] = o;
12956         this.fireEvent("replace", key, old, o);
12957         return o;
12958     },
12959    
12960 /**
12961  * Adds all elements of an Array or an Object to the collection.
12962  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12963  * an Array of values, each of which are added to the collection.
12964  */
12965     addAll : function(objs){
12966         if(arguments.length > 1 || objs instanceof Array){
12967             var args = arguments.length > 1 ? arguments : objs;
12968             for(var i = 0, len = args.length; i < len; i++){
12969                 this.add(args[i]);
12970             }
12971         }else{
12972             for(var key in objs){
12973                 if(this.allowFunctions || typeof objs[key] != "function"){
12974                     this.add(key, objs[key]);
12975                 }
12976             }
12977         }
12978     },
12979    
12980 /**
12981  * Executes the specified function once for every item in the collection, passing each
12982  * item as the first and only parameter. returning false from the function will stop the iteration.
12983  * @param {Function} fn The function to execute for each item.
12984  * @param {Object} scope (optional) The scope in which to execute the function.
12985  */
12986     each : function(fn, scope){
12987         var items = [].concat(this.items); // each safe for removal
12988         for(var i = 0, len = items.length; i < len; i++){
12989             if(fn.call(scope || items[i], items[i], i, len) === false){
12990                 break;
12991             }
12992         }
12993     },
12994    
12995 /**
12996  * Executes the specified function once for every key in the collection, passing each
12997  * key, and its associated item as the first two parameters.
12998  * @param {Function} fn The function to execute for each item.
12999  * @param {Object} scope (optional) The scope in which to execute the function.
13000  */
13001     eachKey : function(fn, scope){
13002         for(var i = 0, len = this.keys.length; i < len; i++){
13003             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13004         }
13005     },
13006    
13007 /**
13008  * Returns the first item in the collection which elicits a true return value from the
13009  * passed selection function.
13010  * @param {Function} fn The selection function to execute for each item.
13011  * @param {Object} scope (optional) The scope in which to execute the function.
13012  * @return {Object} The first item in the collection which returned true from the selection function.
13013  */
13014     find : function(fn, scope){
13015         for(var i = 0, len = this.items.length; i < len; i++){
13016             if(fn.call(scope || window, this.items[i], this.keys[i])){
13017                 return this.items[i];
13018             }
13019         }
13020         return null;
13021     },
13022    
13023 /**
13024  * Inserts an item at the specified index in the collection.
13025  * @param {Number} index The index to insert the item at.
13026  * @param {String} key The key to associate with the new item, or the item itself.
13027  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13028  * @return {Object} The item inserted.
13029  */
13030     insert : function(index, key, o){
13031         if(arguments.length == 2){
13032             o = arguments[1];
13033             key = this.getKey(o);
13034         }
13035         if(index >= this.length){
13036             return this.add(key, o);
13037         }
13038         this.length++;
13039         this.items.splice(index, 0, o);
13040         if(typeof key != "undefined" && key != null){
13041             this.map[key] = o;
13042         }
13043         this.keys.splice(index, 0, key);
13044         this.fireEvent("add", index, o, key);
13045         return o;
13046     },
13047    
13048 /**
13049  * Removed an item from the collection.
13050  * @param {Object} o The item to remove.
13051  * @return {Object} The item removed.
13052  */
13053     remove : function(o){
13054         return this.removeAt(this.indexOf(o));
13055     },
13056    
13057 /**
13058  * Remove an item from a specified index in the collection.
13059  * @param {Number} index The index within the collection of the item to remove.
13060  */
13061     removeAt : function(index){
13062         if(index < this.length && index >= 0){
13063             this.length--;
13064             var o = this.items[index];
13065             this.items.splice(index, 1);
13066             var key = this.keys[index];
13067             if(typeof key != "undefined"){
13068                 delete this.map[key];
13069             }
13070             this.keys.splice(index, 1);
13071             this.fireEvent("remove", o, key);
13072         }
13073     },
13074    
13075 /**
13076  * Removed an item associated with the passed key fom the collection.
13077  * @param {String} key The key of the item to remove.
13078  */
13079     removeKey : function(key){
13080         return this.removeAt(this.indexOfKey(key));
13081     },
13082    
13083 /**
13084  * Returns the number of items in the collection.
13085  * @return {Number} the number of items in the collection.
13086  */
13087     getCount : function(){
13088         return this.length; 
13089     },
13090    
13091 /**
13092  * Returns index within the collection of the passed Object.
13093  * @param {Object} o The item to find the index of.
13094  * @return {Number} index of the item.
13095  */
13096     indexOf : function(o){
13097         if(!this.items.indexOf){
13098             for(var i = 0, len = this.items.length; i < len; i++){
13099                 if(this.items[i] == o) {
13100                     return i;
13101                 }
13102             }
13103             return -1;
13104         }else{
13105             return this.items.indexOf(o);
13106         }
13107     },
13108    
13109 /**
13110  * Returns index within the collection of the passed key.
13111  * @param {String} key The key to find the index of.
13112  * @return {Number} index of the key.
13113  */
13114     indexOfKey : function(key){
13115         if(!this.keys.indexOf){
13116             for(var i = 0, len = this.keys.length; i < len; i++){
13117                 if(this.keys[i] == key) {
13118                     return i;
13119                 }
13120             }
13121             return -1;
13122         }else{
13123             return this.keys.indexOf(key);
13124         }
13125     },
13126    
13127 /**
13128  * Returns the item associated with the passed key OR index. Key has priority over index.
13129  * @param {String/Number} key The key or index of the item.
13130  * @return {Object} The item associated with the passed key.
13131  */
13132     item : function(key){
13133         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13134         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13135     },
13136     
13137 /**
13138  * Returns the item at the specified index.
13139  * @param {Number} index The index of the item.
13140  * @return {Object}
13141  */
13142     itemAt : function(index){
13143         return this.items[index];
13144     },
13145     
13146 /**
13147  * Returns the item associated with the passed key.
13148  * @param {String/Number} key The key of the item.
13149  * @return {Object} The item associated with the passed key.
13150  */
13151     key : function(key){
13152         return this.map[key];
13153     },
13154    
13155 /**
13156  * Returns true if the collection contains the passed Object as an item.
13157  * @param {Object} o  The Object to look for in the collection.
13158  * @return {Boolean} True if the collection contains the Object as an item.
13159  */
13160     contains : function(o){
13161         return this.indexOf(o) != -1;
13162     },
13163    
13164 /**
13165  * Returns true if the collection contains the passed Object as a key.
13166  * @param {String} key The key to look for in the collection.
13167  * @return {Boolean} True if the collection contains the Object as a key.
13168  */
13169     containsKey : function(key){
13170         return typeof this.map[key] != "undefined";
13171     },
13172    
13173 /**
13174  * Removes all items from the collection.
13175  */
13176     clear : function(){
13177         this.length = 0;
13178         this.items = [];
13179         this.keys = [];
13180         this.map = {};
13181         this.fireEvent("clear");
13182     },
13183    
13184 /**
13185  * Returns the first item in the collection.
13186  * @return {Object} the first item in the collection..
13187  */
13188     first : function(){
13189         return this.items[0]; 
13190     },
13191    
13192 /**
13193  * Returns the last item in the collection.
13194  * @return {Object} the last item in the collection..
13195  */
13196     last : function(){
13197         return this.items[this.length-1];   
13198     },
13199     
13200     _sort : function(property, dir, fn){
13201         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13202         fn = fn || function(a, b){
13203             return a-b;
13204         };
13205         var c = [], k = this.keys, items = this.items;
13206         for(var i = 0, len = items.length; i < len; i++){
13207             c[c.length] = {key: k[i], value: items[i], index: i};
13208         }
13209         c.sort(function(a, b){
13210             var v = fn(a[property], b[property]) * dsc;
13211             if(v == 0){
13212                 v = (a.index < b.index ? -1 : 1);
13213             }
13214             return v;
13215         });
13216         for(var i = 0, len = c.length; i < len; i++){
13217             items[i] = c[i].value;
13218             k[i] = c[i].key;
13219         }
13220         this.fireEvent("sort", this);
13221     },
13222     
13223     /**
13224      * Sorts this collection with the passed comparison function
13225      * @param {String} direction (optional) "ASC" or "DESC"
13226      * @param {Function} fn (optional) comparison function
13227      */
13228     sort : function(dir, fn){
13229         this._sort("value", dir, fn);
13230     },
13231     
13232     /**
13233      * Sorts this collection by keys
13234      * @param {String} direction (optional) "ASC" or "DESC"
13235      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13236      */
13237     keySort : function(dir, fn){
13238         this._sort("key", dir, fn || function(a, b){
13239             return String(a).toUpperCase()-String(b).toUpperCase();
13240         });
13241     },
13242     
13243     /**
13244      * Returns a range of items in this collection
13245      * @param {Number} startIndex (optional) defaults to 0
13246      * @param {Number} endIndex (optional) default to the last item
13247      * @return {Array} An array of items
13248      */
13249     getRange : function(start, end){
13250         var items = this.items;
13251         if(items.length < 1){
13252             return [];
13253         }
13254         start = start || 0;
13255         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13256         var r = [];
13257         if(start <= end){
13258             for(var i = start; i <= end; i++) {
13259                     r[r.length] = items[i];
13260             }
13261         }else{
13262             for(var i = start; i >= end; i--) {
13263                     r[r.length] = items[i];
13264             }
13265         }
13266         return r;
13267     },
13268         
13269     /**
13270      * Filter the <i>objects</i> in this collection by a specific property. 
13271      * Returns a new collection that has been filtered.
13272      * @param {String} property A property on your objects
13273      * @param {String/RegExp} value Either string that the property values 
13274      * should start with or a RegExp to test against the property
13275      * @return {MixedCollection} The new filtered collection
13276      */
13277     filter : function(property, value){
13278         if(!value.exec){ // not a regex
13279             value = String(value);
13280             if(value.length == 0){
13281                 return this.clone();
13282             }
13283             value = new RegExp("^" + Roo.escapeRe(value), "i");
13284         }
13285         return this.filterBy(function(o){
13286             return o && value.test(o[property]);
13287         });
13288         },
13289     
13290     /**
13291      * Filter by a function. * Returns a new collection that has been filtered.
13292      * The passed function will be called with each 
13293      * object in the collection. If the function returns true, the value is included 
13294      * otherwise it is filtered.
13295      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13296      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13297      * @return {MixedCollection} The new filtered collection
13298      */
13299     filterBy : function(fn, scope){
13300         var r = new Roo.util.MixedCollection();
13301         r.getKey = this.getKey;
13302         var k = this.keys, it = this.items;
13303         for(var i = 0, len = it.length; i < len; i++){
13304             if(fn.call(scope||this, it[i], k[i])){
13305                                 r.add(k[i], it[i]);
13306                         }
13307         }
13308         return r;
13309     },
13310     
13311     /**
13312      * Creates a duplicate of this collection
13313      * @return {MixedCollection}
13314      */
13315     clone : function(){
13316         var r = new Roo.util.MixedCollection();
13317         var k = this.keys, it = this.items;
13318         for(var i = 0, len = it.length; i < len; i++){
13319             r.add(k[i], it[i]);
13320         }
13321         r.getKey = this.getKey;
13322         return r;
13323     }
13324 });
13325 /**
13326  * Returns the item associated with the passed key or index.
13327  * @method
13328  * @param {String/Number} key The key or index of the item.
13329  * @return {Object} The item associated with the passed key.
13330  */
13331 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13332  * Based on:
13333  * Ext JS Library 1.1.1
13334  * Copyright(c) 2006-2007, Ext JS, LLC.
13335  *
13336  * Originally Released Under LGPL - original licence link has changed is not relivant.
13337  *
13338  * Fork - LGPL
13339  * <script type="text/javascript">
13340  */
13341 /**
13342  * @class Roo.util.JSON
13343  * Modified version of Douglas Crockford"s json.js that doesn"t
13344  * mess with the Object prototype 
13345  * http://www.json.org/js.html
13346  * @singleton
13347  */
13348 Roo.util.JSON = new (function(){
13349     var useHasOwn = {}.hasOwnProperty ? true : false;
13350     
13351     // crashes Safari in some instances
13352     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13353     
13354     var pad = function(n) {
13355         return n < 10 ? "0" + n : n;
13356     };
13357     
13358     var m = {
13359         "\b": '\\b',
13360         "\t": '\\t',
13361         "\n": '\\n',
13362         "\f": '\\f',
13363         "\r": '\\r',
13364         '"' : '\\"',
13365         "\\": '\\\\'
13366     };
13367
13368     var encodeString = function(s){
13369         if (/["\\\x00-\x1f]/.test(s)) {
13370             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13371                 var c = m[b];
13372                 if(c){
13373                     return c;
13374                 }
13375                 c = b.charCodeAt();
13376                 return "\\u00" +
13377                     Math.floor(c / 16).toString(16) +
13378                     (c % 16).toString(16);
13379             }) + '"';
13380         }
13381         return '"' + s + '"';
13382     };
13383     
13384     var encodeArray = function(o){
13385         var a = ["["], b, i, l = o.length, v;
13386             for (i = 0; i < l; i += 1) {
13387                 v = o[i];
13388                 switch (typeof v) {
13389                     case "undefined":
13390                     case "function":
13391                     case "unknown":
13392                         break;
13393                     default:
13394                         if (b) {
13395                             a.push(',');
13396                         }
13397                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13398                         b = true;
13399                 }
13400             }
13401             a.push("]");
13402             return a.join("");
13403     };
13404     
13405     var encodeDate = function(o){
13406         return '"' + o.getFullYear() + "-" +
13407                 pad(o.getMonth() + 1) + "-" +
13408                 pad(o.getDate()) + "T" +
13409                 pad(o.getHours()) + ":" +
13410                 pad(o.getMinutes()) + ":" +
13411                 pad(o.getSeconds()) + '"';
13412     };
13413     
13414     /**
13415      * Encodes an Object, Array or other value
13416      * @param {Mixed} o The variable to encode
13417      * @return {String} The JSON string
13418      */
13419     this.encode = function(o)
13420     {
13421         // should this be extended to fully wrap stringify..
13422         
13423         if(typeof o == "undefined" || o === null){
13424             return "null";
13425         }else if(o instanceof Array){
13426             return encodeArray(o);
13427         }else if(o instanceof Date){
13428             return encodeDate(o);
13429         }else if(typeof o == "string"){
13430             return encodeString(o);
13431         }else if(typeof o == "number"){
13432             return isFinite(o) ? String(o) : "null";
13433         }else if(typeof o == "boolean"){
13434             return String(o);
13435         }else {
13436             var a = ["{"], b, i, v;
13437             for (i in o) {
13438                 if(!useHasOwn || o.hasOwnProperty(i)) {
13439                     v = o[i];
13440                     switch (typeof v) {
13441                     case "undefined":
13442                     case "function":
13443                     case "unknown":
13444                         break;
13445                     default:
13446                         if(b){
13447                             a.push(',');
13448                         }
13449                         a.push(this.encode(i), ":",
13450                                 v === null ? "null" : this.encode(v));
13451                         b = true;
13452                     }
13453                 }
13454             }
13455             a.push("}");
13456             return a.join("");
13457         }
13458     };
13459     
13460     /**
13461      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13462      * @param {String} json The JSON string
13463      * @return {Object} The resulting object
13464      */
13465     this.decode = function(json){
13466         
13467         return  /** eval:var:json */ eval("(" + json + ')');
13468     };
13469 })();
13470 /** 
13471  * Shorthand for {@link Roo.util.JSON#encode}
13472  * @member Roo encode 
13473  * @method */
13474 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13475 /** 
13476  * Shorthand for {@link Roo.util.JSON#decode}
13477  * @member Roo decode 
13478  * @method */
13479 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13480 /*
13481  * Based on:
13482  * Ext JS Library 1.1.1
13483  * Copyright(c) 2006-2007, Ext JS, LLC.
13484  *
13485  * Originally Released Under LGPL - original licence link has changed is not relivant.
13486  *
13487  * Fork - LGPL
13488  * <script type="text/javascript">
13489  */
13490  
13491 /**
13492  * @class Roo.util.Format
13493  * Reusable data formatting functions
13494  * @singleton
13495  */
13496 Roo.util.Format = function(){
13497     var trimRe = /^\s+|\s+$/g;
13498     return {
13499         /**
13500          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13501          * @param {String} value The string to truncate
13502          * @param {Number} length The maximum length to allow before truncating
13503          * @return {String} The converted text
13504          */
13505         ellipsis : function(value, len){
13506             if(value && value.length > len){
13507                 return value.substr(0, len-3)+"...";
13508             }
13509             return value;
13510         },
13511
13512         /**
13513          * Checks a reference and converts it to empty string if it is undefined
13514          * @param {Mixed} value Reference to check
13515          * @return {Mixed} Empty string if converted, otherwise the original value
13516          */
13517         undef : function(value){
13518             return typeof value != "undefined" ? value : "";
13519         },
13520
13521         /**
13522          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13523          * @param {String} value The string to encode
13524          * @return {String} The encoded text
13525          */
13526         htmlEncode : function(value){
13527             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13528         },
13529
13530         /**
13531          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13532          * @param {String} value The string to decode
13533          * @return {String} The decoded text
13534          */
13535         htmlDecode : function(value){
13536             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13537         },
13538
13539         /**
13540          * Trims any whitespace from either side of a string
13541          * @param {String} value The text to trim
13542          * @return {String} The trimmed text
13543          */
13544         trim : function(value){
13545             return String(value).replace(trimRe, "");
13546         },
13547
13548         /**
13549          * Returns a substring from within an original string
13550          * @param {String} value The original text
13551          * @param {Number} start The start index of the substring
13552          * @param {Number} length The length of the substring
13553          * @return {String} The substring
13554          */
13555         substr : function(value, start, length){
13556             return String(value).substr(start, length);
13557         },
13558
13559         /**
13560          * Converts a string to all lower case letters
13561          * @param {String} value The text to convert
13562          * @return {String} The converted text
13563          */
13564         lowercase : function(value){
13565             return String(value).toLowerCase();
13566         },
13567
13568         /**
13569          * Converts a string to all upper case letters
13570          * @param {String} value The text to convert
13571          * @return {String} The converted text
13572          */
13573         uppercase : function(value){
13574             return String(value).toUpperCase();
13575         },
13576
13577         /**
13578          * Converts the first character only of a string to upper case
13579          * @param {String} value The text to convert
13580          * @return {String} The converted text
13581          */
13582         capitalize : function(value){
13583             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13584         },
13585
13586         // private
13587         call : function(value, fn){
13588             if(arguments.length > 2){
13589                 var args = Array.prototype.slice.call(arguments, 2);
13590                 args.unshift(value);
13591                  
13592                 return /** eval:var:value */  eval(fn).apply(window, args);
13593             }else{
13594                 /** eval:var:value */
13595                 return /** eval:var:value */ eval(fn).call(window, value);
13596             }
13597         },
13598
13599        
13600         /**
13601          * safer version of Math.toFixed..??/
13602          * @param {Number/String} value The numeric value to format
13603          * @param {Number/String} value Decimal places 
13604          * @return {String} The formatted currency string
13605          */
13606         toFixed : function(v, n)
13607         {
13608             // why not use to fixed - precision is buggered???
13609             if (!n) {
13610                 return Math.round(v-0);
13611             }
13612             var fact = Math.pow(10,n+1);
13613             v = (Math.round((v-0)*fact))/fact;
13614             var z = (''+fact).substring(2);
13615             if (v == Math.floor(v)) {
13616                 return Math.floor(v) + '.' + z;
13617             }
13618             
13619             // now just padd decimals..
13620             var ps = String(v).split('.');
13621             var fd = (ps[1] + z);
13622             var r = fd.substring(0,n); 
13623             var rm = fd.substring(n); 
13624             if (rm < 5) {
13625                 return ps[0] + '.' + r;
13626             }
13627             r*=1; // turn it into a number;
13628             r++;
13629             if (String(r).length != n) {
13630                 ps[0]*=1;
13631                 ps[0]++;
13632                 r = String(r).substring(1); // chop the end off.
13633             }
13634             
13635             return ps[0] + '.' + r;
13636              
13637         },
13638         
13639         /**
13640          * Format a number as US currency
13641          * @param {Number/String} value The numeric value to format
13642          * @return {String} The formatted currency string
13643          */
13644         usMoney : function(v){
13645             return '$' + Roo.util.Format.number(v);
13646         },
13647         
13648         /**
13649          * Format a number
13650          * eventually this should probably emulate php's number_format
13651          * @param {Number/String} value The numeric value to format
13652          * @param {Number} decimals number of decimal places
13653          * @return {String} The formatted currency string
13654          */
13655         number : function(v,decimals)
13656         {
13657             // multiply and round.
13658             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13659             var mul = Math.pow(10, decimals);
13660             var zero = String(mul).substring(1);
13661             v = (Math.round((v-0)*mul))/mul;
13662             
13663             // if it's '0' number.. then
13664             
13665             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13666             v = String(v);
13667             var ps = v.split('.');
13668             var whole = ps[0];
13669             
13670             
13671             var r = /(\d+)(\d{3})/;
13672             // add comma's
13673             while (r.test(whole)) {
13674                 whole = whole.replace(r, '$1' + ',' + '$2');
13675             }
13676             
13677             
13678             var sub = ps[1] ?
13679                     // has decimals..
13680                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13681                     // does not have decimals
13682                     (decimals ? ('.' + zero) : '');
13683             
13684             
13685             return whole + sub ;
13686         },
13687         
13688         /**
13689          * Parse a value into a formatted date using the specified format pattern.
13690          * @param {Mixed} value The value to format
13691          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13692          * @return {String} The formatted date string
13693          */
13694         date : function(v, format){
13695             if(!v){
13696                 return "";
13697             }
13698             if(!(v instanceof Date)){
13699                 v = new Date(Date.parse(v));
13700             }
13701             return v.dateFormat(format || Roo.util.Format.defaults.date);
13702         },
13703
13704         /**
13705          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13706          * @param {String} format Any valid date format string
13707          * @return {Function} The date formatting function
13708          */
13709         dateRenderer : function(format){
13710             return function(v){
13711                 return Roo.util.Format.date(v, format);  
13712             };
13713         },
13714
13715         // private
13716         stripTagsRE : /<\/?[^>]+>/gi,
13717         
13718         /**
13719          * Strips all HTML tags
13720          * @param {Mixed} value The text from which to strip tags
13721          * @return {String} The stripped text
13722          */
13723         stripTags : function(v){
13724             return !v ? v : String(v).replace(this.stripTagsRE, "");
13725         }
13726     };
13727 }();
13728 Roo.util.Format.defaults = {
13729     date : 'd/M/Y'
13730 };/*
13731  * Based on:
13732  * Ext JS Library 1.1.1
13733  * Copyright(c) 2006-2007, Ext JS, LLC.
13734  *
13735  * Originally Released Under LGPL - original licence link has changed is not relivant.
13736  *
13737  * Fork - LGPL
13738  * <script type="text/javascript">
13739  */
13740
13741
13742  
13743
13744 /**
13745  * @class Roo.MasterTemplate
13746  * @extends Roo.Template
13747  * Provides a template that can have child templates. The syntax is:
13748 <pre><code>
13749 var t = new Roo.MasterTemplate(
13750         '&lt;select name="{name}"&gt;',
13751                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13752         '&lt;/select&gt;'
13753 );
13754 t.add('options', {value: 'foo', text: 'bar'});
13755 // or you can add multiple child elements in one shot
13756 t.addAll('options', [
13757     {value: 'foo', text: 'bar'},
13758     {value: 'foo2', text: 'bar2'},
13759     {value: 'foo3', text: 'bar3'}
13760 ]);
13761 // then append, applying the master template values
13762 t.append('my-form', {name: 'my-select'});
13763 </code></pre>
13764 * A name attribute for the child template is not required if you have only one child
13765 * template or you want to refer to them by index.
13766  */
13767 Roo.MasterTemplate = function(){
13768     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13769     this.originalHtml = this.html;
13770     var st = {};
13771     var m, re = this.subTemplateRe;
13772     re.lastIndex = 0;
13773     var subIndex = 0;
13774     while(m = re.exec(this.html)){
13775         var name = m[1], content = m[2];
13776         st[subIndex] = {
13777             name: name,
13778             index: subIndex,
13779             buffer: [],
13780             tpl : new Roo.Template(content)
13781         };
13782         if(name){
13783             st[name] = st[subIndex];
13784         }
13785         st[subIndex].tpl.compile();
13786         st[subIndex].tpl.call = this.call.createDelegate(this);
13787         subIndex++;
13788     }
13789     this.subCount = subIndex;
13790     this.subs = st;
13791 };
13792 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13793     /**
13794     * The regular expression used to match sub templates
13795     * @type RegExp
13796     * @property
13797     */
13798     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13799
13800     /**
13801      * Applies the passed values to a child template.
13802      * @param {String/Number} name (optional) The name or index of the child template
13803      * @param {Array/Object} values The values to be applied to the template
13804      * @return {MasterTemplate} this
13805      */
13806      add : function(name, values){
13807         if(arguments.length == 1){
13808             values = arguments[0];
13809             name = 0;
13810         }
13811         var s = this.subs[name];
13812         s.buffer[s.buffer.length] = s.tpl.apply(values);
13813         return this;
13814     },
13815
13816     /**
13817      * Applies all the passed values to a child template.
13818      * @param {String/Number} name (optional) The name or index of the child template
13819      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13820      * @param {Boolean} reset (optional) True to reset the template first
13821      * @return {MasterTemplate} this
13822      */
13823     fill : function(name, values, reset){
13824         var a = arguments;
13825         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13826             values = a[0];
13827             name = 0;
13828             reset = a[1];
13829         }
13830         if(reset){
13831             this.reset();
13832         }
13833         for(var i = 0, len = values.length; i < len; i++){
13834             this.add(name, values[i]);
13835         }
13836         return this;
13837     },
13838
13839     /**
13840      * Resets the template for reuse
13841      * @return {MasterTemplate} this
13842      */
13843      reset : function(){
13844         var s = this.subs;
13845         for(var i = 0; i < this.subCount; i++){
13846             s[i].buffer = [];
13847         }
13848         return this;
13849     },
13850
13851     applyTemplate : function(values){
13852         var s = this.subs;
13853         var replaceIndex = -1;
13854         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13855             return s[++replaceIndex].buffer.join("");
13856         });
13857         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13858     },
13859
13860     apply : function(){
13861         return this.applyTemplate.apply(this, arguments);
13862     },
13863
13864     compile : function(){return this;}
13865 });
13866
13867 /**
13868  * Alias for fill().
13869  * @method
13870  */
13871 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13872  /**
13873  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13874  * var tpl = Roo.MasterTemplate.from('element-id');
13875  * @param {String/HTMLElement} el
13876  * @param {Object} config
13877  * @static
13878  */
13879 Roo.MasterTemplate.from = function(el, config){
13880     el = Roo.getDom(el);
13881     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13882 };/*
13883  * Based on:
13884  * Ext JS Library 1.1.1
13885  * Copyright(c) 2006-2007, Ext JS, LLC.
13886  *
13887  * Originally Released Under LGPL - original licence link has changed is not relivant.
13888  *
13889  * Fork - LGPL
13890  * <script type="text/javascript">
13891  */
13892
13893  
13894 /**
13895  * @class Roo.util.CSS
13896  * Utility class for manipulating CSS rules
13897  * @singleton
13898  */
13899 Roo.util.CSS = function(){
13900         var rules = null;
13901         var doc = document;
13902
13903     var camelRe = /(-[a-z])/gi;
13904     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13905
13906    return {
13907    /**
13908     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13909     * tag and appended to the HEAD of the document.
13910     * @param {String|Object} cssText The text containing the css rules
13911     * @param {String} id An id to add to the stylesheet for later removal
13912     * @return {StyleSheet}
13913     */
13914     createStyleSheet : function(cssText, id){
13915         var ss;
13916         var head = doc.getElementsByTagName("head")[0];
13917         var nrules = doc.createElement("style");
13918         nrules.setAttribute("type", "text/css");
13919         if(id){
13920             nrules.setAttribute("id", id);
13921         }
13922         if (typeof(cssText) != 'string') {
13923             // support object maps..
13924             // not sure if this a good idea.. 
13925             // perhaps it should be merged with the general css handling
13926             // and handle js style props.
13927             var cssTextNew = [];
13928             for(var n in cssText) {
13929                 var citems = [];
13930                 for(var k in cssText[n]) {
13931                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13932                 }
13933                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13934                 
13935             }
13936             cssText = cssTextNew.join("\n");
13937             
13938         }
13939        
13940        
13941        if(Roo.isIE){
13942            head.appendChild(nrules);
13943            ss = nrules.styleSheet;
13944            ss.cssText = cssText;
13945        }else{
13946            try{
13947                 nrules.appendChild(doc.createTextNode(cssText));
13948            }catch(e){
13949                nrules.cssText = cssText; 
13950            }
13951            head.appendChild(nrules);
13952            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13953        }
13954        this.cacheStyleSheet(ss);
13955        return ss;
13956    },
13957
13958    /**
13959     * Removes a style or link tag by id
13960     * @param {String} id The id of the tag
13961     */
13962    removeStyleSheet : function(id){
13963        var existing = doc.getElementById(id);
13964        if(existing){
13965            existing.parentNode.removeChild(existing);
13966        }
13967    },
13968
13969    /**
13970     * Dynamically swaps an existing stylesheet reference for a new one
13971     * @param {String} id The id of an existing link tag to remove
13972     * @param {String} url The href of the new stylesheet to include
13973     */
13974    swapStyleSheet : function(id, url){
13975        this.removeStyleSheet(id);
13976        var ss = doc.createElement("link");
13977        ss.setAttribute("rel", "stylesheet");
13978        ss.setAttribute("type", "text/css");
13979        ss.setAttribute("id", id);
13980        ss.setAttribute("href", url);
13981        doc.getElementsByTagName("head")[0].appendChild(ss);
13982    },
13983    
13984    /**
13985     * Refresh the rule cache if you have dynamically added stylesheets
13986     * @return {Object} An object (hash) of rules indexed by selector
13987     */
13988    refreshCache : function(){
13989        return this.getRules(true);
13990    },
13991
13992    // private
13993    cacheStyleSheet : function(stylesheet){
13994        if(!rules){
13995            rules = {};
13996        }
13997        try{// try catch for cross domain access issue
13998            var ssRules = stylesheet.cssRules || stylesheet.rules;
13999            for(var j = ssRules.length-1; j >= 0; --j){
14000                rules[ssRules[j].selectorText] = ssRules[j];
14001            }
14002        }catch(e){}
14003    },
14004    
14005    /**
14006     * Gets all css rules for the document
14007     * @param {Boolean} refreshCache true to refresh the internal cache
14008     * @return {Object} An object (hash) of rules indexed by selector
14009     */
14010    getRules : function(refreshCache){
14011                 if(rules == null || refreshCache){
14012                         rules = {};
14013                         var ds = doc.styleSheets;
14014                         for(var i =0, len = ds.length; i < len; i++){
14015                             try{
14016                         this.cacheStyleSheet(ds[i]);
14017                     }catch(e){} 
14018                 }
14019                 }
14020                 return rules;
14021         },
14022         
14023         /**
14024     * Gets an an individual CSS rule by selector(s)
14025     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14026     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14027     * @return {CSSRule} The CSS rule or null if one is not found
14028     */
14029    getRule : function(selector, refreshCache){
14030                 var rs = this.getRules(refreshCache);
14031                 if(!(selector instanceof Array)){
14032                     return rs[selector];
14033                 }
14034                 for(var i = 0; i < selector.length; i++){
14035                         if(rs[selector[i]]){
14036                                 return rs[selector[i]];
14037                         }
14038                 }
14039                 return null;
14040         },
14041         
14042         
14043         /**
14044     * Updates a rule property
14045     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14046     * @param {String} property The css property
14047     * @param {String} value The new value for the property
14048     * @return {Boolean} true If a rule was found and updated
14049     */
14050    updateRule : function(selector, property, value){
14051                 if(!(selector instanceof Array)){
14052                         var rule = this.getRule(selector);
14053                         if(rule){
14054                                 rule.style[property.replace(camelRe, camelFn)] = value;
14055                                 return true;
14056                         }
14057                 }else{
14058                         for(var i = 0; i < selector.length; i++){
14059                                 if(this.updateRule(selector[i], property, value)){
14060                                         return true;
14061                                 }
14062                         }
14063                 }
14064                 return false;
14065         }
14066    };   
14067 }();/*
14068  * Based on:
14069  * Ext JS Library 1.1.1
14070  * Copyright(c) 2006-2007, Ext JS, LLC.
14071  *
14072  * Originally Released Under LGPL - original licence link has changed is not relivant.
14073  *
14074  * Fork - LGPL
14075  * <script type="text/javascript">
14076  */
14077
14078  
14079
14080 /**
14081  * @class Roo.util.ClickRepeater
14082  * @extends Roo.util.Observable
14083  * 
14084  * A wrapper class which can be applied to any element. Fires a "click" event while the
14085  * mouse is pressed. The interval between firings may be specified in the config but
14086  * defaults to 10 milliseconds.
14087  * 
14088  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14089  * 
14090  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14091  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14092  * Similar to an autorepeat key delay.
14093  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14094  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14095  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14096  *           "interval" and "delay" are ignored. "immediate" is honored.
14097  * @cfg {Boolean} preventDefault True to prevent the default click event
14098  * @cfg {Boolean} stopDefault True to stop the default click event
14099  * 
14100  * @history
14101  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14102  *     2007-02-02 jvs Renamed to ClickRepeater
14103  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14104  *
14105  *  @constructor
14106  * @param {String/HTMLElement/Element} el The element to listen on
14107  * @param {Object} config
14108  **/
14109 Roo.util.ClickRepeater = function(el, config)
14110 {
14111     this.el = Roo.get(el);
14112     this.el.unselectable();
14113
14114     Roo.apply(this, config);
14115
14116     this.addEvents({
14117     /**
14118      * @event mousedown
14119      * Fires when the mouse button is depressed.
14120      * @param {Roo.util.ClickRepeater} this
14121      */
14122         "mousedown" : true,
14123     /**
14124      * @event click
14125      * Fires on a specified interval during the time the element is pressed.
14126      * @param {Roo.util.ClickRepeater} this
14127      */
14128         "click" : true,
14129     /**
14130      * @event mouseup
14131      * Fires when the mouse key is released.
14132      * @param {Roo.util.ClickRepeater} this
14133      */
14134         "mouseup" : true
14135     });
14136
14137     this.el.on("mousedown", this.handleMouseDown, this);
14138     if(this.preventDefault || this.stopDefault){
14139         this.el.on("click", function(e){
14140             if(this.preventDefault){
14141                 e.preventDefault();
14142             }
14143             if(this.stopDefault){
14144                 e.stopEvent();
14145             }
14146         }, this);
14147     }
14148
14149     // allow inline handler
14150     if(this.handler){
14151         this.on("click", this.handler,  this.scope || this);
14152     }
14153
14154     Roo.util.ClickRepeater.superclass.constructor.call(this);
14155 };
14156
14157 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14158     interval : 20,
14159     delay: 250,
14160     preventDefault : true,
14161     stopDefault : false,
14162     timer : 0,
14163
14164     // private
14165     handleMouseDown : function(){
14166         clearTimeout(this.timer);
14167         this.el.blur();
14168         if(this.pressClass){
14169             this.el.addClass(this.pressClass);
14170         }
14171         this.mousedownTime = new Date();
14172
14173         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14174         this.el.on("mouseout", this.handleMouseOut, this);
14175
14176         this.fireEvent("mousedown", this);
14177         this.fireEvent("click", this);
14178         
14179         this.timer = this.click.defer(this.delay || this.interval, this);
14180     },
14181
14182     // private
14183     click : function(){
14184         this.fireEvent("click", this);
14185         this.timer = this.click.defer(this.getInterval(), this);
14186     },
14187
14188     // private
14189     getInterval: function(){
14190         if(!this.accelerate){
14191             return this.interval;
14192         }
14193         var pressTime = this.mousedownTime.getElapsed();
14194         if(pressTime < 500){
14195             return 400;
14196         }else if(pressTime < 1700){
14197             return 320;
14198         }else if(pressTime < 2600){
14199             return 250;
14200         }else if(pressTime < 3500){
14201             return 180;
14202         }else if(pressTime < 4400){
14203             return 140;
14204         }else if(pressTime < 5300){
14205             return 80;
14206         }else if(pressTime < 6200){
14207             return 50;
14208         }else{
14209             return 10;
14210         }
14211     },
14212
14213     // private
14214     handleMouseOut : function(){
14215         clearTimeout(this.timer);
14216         if(this.pressClass){
14217             this.el.removeClass(this.pressClass);
14218         }
14219         this.el.on("mouseover", this.handleMouseReturn, this);
14220     },
14221
14222     // private
14223     handleMouseReturn : function(){
14224         this.el.un("mouseover", this.handleMouseReturn);
14225         if(this.pressClass){
14226             this.el.addClass(this.pressClass);
14227         }
14228         this.click();
14229     },
14230
14231     // private
14232     handleMouseUp : function(){
14233         clearTimeout(this.timer);
14234         this.el.un("mouseover", this.handleMouseReturn);
14235         this.el.un("mouseout", this.handleMouseOut);
14236         Roo.get(document).un("mouseup", this.handleMouseUp);
14237         this.el.removeClass(this.pressClass);
14238         this.fireEvent("mouseup", this);
14239     }
14240 });/*
14241  * Based on:
14242  * Ext JS Library 1.1.1
14243  * Copyright(c) 2006-2007, Ext JS, LLC.
14244  *
14245  * Originally Released Under LGPL - original licence link has changed is not relivant.
14246  *
14247  * Fork - LGPL
14248  * <script type="text/javascript">
14249  */
14250
14251  
14252 /**
14253  * @class Roo.KeyNav
14254  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14255  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14256  * way to implement custom navigation schemes for any UI component.</p>
14257  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14258  * pageUp, pageDown, del, home, end.  Usage:</p>
14259  <pre><code>
14260 var nav = new Roo.KeyNav("my-element", {
14261     "left" : function(e){
14262         this.moveLeft(e.ctrlKey);
14263     },
14264     "right" : function(e){
14265         this.moveRight(e.ctrlKey);
14266     },
14267     "enter" : function(e){
14268         this.save();
14269     },
14270     scope : this
14271 });
14272 </code></pre>
14273  * @constructor
14274  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14275  * @param {Object} config The config
14276  */
14277 Roo.KeyNav = function(el, config){
14278     this.el = Roo.get(el);
14279     Roo.apply(this, config);
14280     if(!this.disabled){
14281         this.disabled = true;
14282         this.enable();
14283     }
14284 };
14285
14286 Roo.KeyNav.prototype = {
14287     /**
14288      * @cfg {Boolean} disabled
14289      * True to disable this KeyNav instance (defaults to false)
14290      */
14291     disabled : false,
14292     /**
14293      * @cfg {String} defaultEventAction
14294      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14295      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14296      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14297      */
14298     defaultEventAction: "stopEvent",
14299     /**
14300      * @cfg {Boolean} forceKeyDown
14301      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14302      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14303      * handle keydown instead of keypress.
14304      */
14305     forceKeyDown : false,
14306
14307     // private
14308     prepareEvent : function(e){
14309         var k = e.getKey();
14310         var h = this.keyToHandler[k];
14311         //if(h && this[h]){
14312         //    e.stopPropagation();
14313         //}
14314         if(Roo.isSafari && h && k >= 37 && k <= 40){
14315             e.stopEvent();
14316         }
14317     },
14318
14319     // private
14320     relay : function(e){
14321         var k = e.getKey();
14322         var h = this.keyToHandler[k];
14323         if(h && this[h]){
14324             if(this.doRelay(e, this[h], h) !== true){
14325                 e[this.defaultEventAction]();
14326             }
14327         }
14328     },
14329
14330     // private
14331     doRelay : function(e, h, hname){
14332         return h.call(this.scope || this, e);
14333     },
14334
14335     // possible handlers
14336     enter : false,
14337     left : false,
14338     right : false,
14339     up : false,
14340     down : false,
14341     tab : false,
14342     esc : false,
14343     pageUp : false,
14344     pageDown : false,
14345     del : false,
14346     home : false,
14347     end : false,
14348
14349     // quick lookup hash
14350     keyToHandler : {
14351         37 : "left",
14352         39 : "right",
14353         38 : "up",
14354         40 : "down",
14355         33 : "pageUp",
14356         34 : "pageDown",
14357         46 : "del",
14358         36 : "home",
14359         35 : "end",
14360         13 : "enter",
14361         27 : "esc",
14362         9  : "tab"
14363     },
14364
14365         /**
14366          * Enable this KeyNav
14367          */
14368         enable: function(){
14369                 if(this.disabled){
14370             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14371             // the EventObject will normalize Safari automatically
14372             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14373                 this.el.on("keydown", this.relay,  this);
14374             }else{
14375                 this.el.on("keydown", this.prepareEvent,  this);
14376                 this.el.on("keypress", this.relay,  this);
14377             }
14378                     this.disabled = false;
14379                 }
14380         },
14381
14382         /**
14383          * Disable this KeyNav
14384          */
14385         disable: function(){
14386                 if(!this.disabled){
14387                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14388                 this.el.un("keydown", this.relay);
14389             }else{
14390                 this.el.un("keydown", this.prepareEvent);
14391                 this.el.un("keypress", this.relay);
14392             }
14393                     this.disabled = true;
14394                 }
14395         }
14396 };/*
14397  * Based on:
14398  * Ext JS Library 1.1.1
14399  * Copyright(c) 2006-2007, Ext JS, LLC.
14400  *
14401  * Originally Released Under LGPL - original licence link has changed is not relivant.
14402  *
14403  * Fork - LGPL
14404  * <script type="text/javascript">
14405  */
14406
14407  
14408 /**
14409  * @class Roo.KeyMap
14410  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14411  * The constructor accepts the same config object as defined by {@link #addBinding}.
14412  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14413  * combination it will call the function with this signature (if the match is a multi-key
14414  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14415  * A KeyMap can also handle a string representation of keys.<br />
14416  * Usage:
14417  <pre><code>
14418 // map one key by key code
14419 var map = new Roo.KeyMap("my-element", {
14420     key: 13, // or Roo.EventObject.ENTER
14421     fn: myHandler,
14422     scope: myObject
14423 });
14424
14425 // map multiple keys to one action by string
14426 var map = new Roo.KeyMap("my-element", {
14427     key: "a\r\n\t",
14428     fn: myHandler,
14429     scope: myObject
14430 });
14431
14432 // map multiple keys to multiple actions by strings and array of codes
14433 var map = new Roo.KeyMap("my-element", [
14434     {
14435         key: [10,13],
14436         fn: function(){ alert("Return was pressed"); }
14437     }, {
14438         key: "abc",
14439         fn: function(){ alert('a, b or c was pressed'); }
14440     }, {
14441         key: "\t",
14442         ctrl:true,
14443         shift:true,
14444         fn: function(){ alert('Control + shift + tab was pressed.'); }
14445     }
14446 ]);
14447 </code></pre>
14448  * <b>Note: A KeyMap starts enabled</b>
14449  * @constructor
14450  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14451  * @param {Object} config The config (see {@link #addBinding})
14452  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14453  */
14454 Roo.KeyMap = function(el, config, eventName){
14455     this.el  = Roo.get(el);
14456     this.eventName = eventName || "keydown";
14457     this.bindings = [];
14458     if(config){
14459         this.addBinding(config);
14460     }
14461     this.enable();
14462 };
14463
14464 Roo.KeyMap.prototype = {
14465     /**
14466      * True to stop the event from bubbling and prevent the default browser action if the
14467      * key was handled by the KeyMap (defaults to false)
14468      * @type Boolean
14469      */
14470     stopEvent : false,
14471
14472     /**
14473      * Add a new binding to this KeyMap. The following config object properties are supported:
14474      * <pre>
14475 Property    Type             Description
14476 ----------  ---------------  ----------------------------------------------------------------------
14477 key         String/Array     A single keycode or an array of keycodes to handle
14478 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14479 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14480 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14481 fn          Function         The function to call when KeyMap finds the expected key combination
14482 scope       Object           The scope of the callback function
14483 </pre>
14484      *
14485      * Usage:
14486      * <pre><code>
14487 // Create a KeyMap
14488 var map = new Roo.KeyMap(document, {
14489     key: Roo.EventObject.ENTER,
14490     fn: handleKey,
14491     scope: this
14492 });
14493
14494 //Add a new binding to the existing KeyMap later
14495 map.addBinding({
14496     key: 'abc',
14497     shift: true,
14498     fn: handleKey,
14499     scope: this
14500 });
14501 </code></pre>
14502      * @param {Object/Array} config A single KeyMap config or an array of configs
14503      */
14504         addBinding : function(config){
14505         if(config instanceof Array){
14506             for(var i = 0, len = config.length; i < len; i++){
14507                 this.addBinding(config[i]);
14508             }
14509             return;
14510         }
14511         var keyCode = config.key,
14512             shift = config.shift, 
14513             ctrl = config.ctrl, 
14514             alt = config.alt,
14515             fn = config.fn,
14516             scope = config.scope;
14517         if(typeof keyCode == "string"){
14518             var ks = [];
14519             var keyString = keyCode.toUpperCase();
14520             for(var j = 0, len = keyString.length; j < len; j++){
14521                 ks.push(keyString.charCodeAt(j));
14522             }
14523             keyCode = ks;
14524         }
14525         var keyArray = keyCode instanceof Array;
14526         var handler = function(e){
14527             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14528                 var k = e.getKey();
14529                 if(keyArray){
14530                     for(var i = 0, len = keyCode.length; i < len; i++){
14531                         if(keyCode[i] == k){
14532                           if(this.stopEvent){
14533                               e.stopEvent();
14534                           }
14535                           fn.call(scope || window, k, e);
14536                           return;
14537                         }
14538                     }
14539                 }else{
14540                     if(k == keyCode){
14541                         if(this.stopEvent){
14542                            e.stopEvent();
14543                         }
14544                         fn.call(scope || window, k, e);
14545                     }
14546                 }
14547             }
14548         };
14549         this.bindings.push(handler);  
14550         },
14551
14552     /**
14553      * Shorthand for adding a single key listener
14554      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14555      * following options:
14556      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14557      * @param {Function} fn The function to call
14558      * @param {Object} scope (optional) The scope of the function
14559      */
14560     on : function(key, fn, scope){
14561         var keyCode, shift, ctrl, alt;
14562         if(typeof key == "object" && !(key instanceof Array)){
14563             keyCode = key.key;
14564             shift = key.shift;
14565             ctrl = key.ctrl;
14566             alt = key.alt;
14567         }else{
14568             keyCode = key;
14569         }
14570         this.addBinding({
14571             key: keyCode,
14572             shift: shift,
14573             ctrl: ctrl,
14574             alt: alt,
14575             fn: fn,
14576             scope: scope
14577         })
14578     },
14579
14580     // private
14581     handleKeyDown : function(e){
14582             if(this.enabled){ //just in case
14583             var b = this.bindings;
14584             for(var i = 0, len = b.length; i < len; i++){
14585                 b[i].call(this, e);
14586             }
14587             }
14588         },
14589         
14590         /**
14591          * Returns true if this KeyMap is enabled
14592          * @return {Boolean} 
14593          */
14594         isEnabled : function(){
14595             return this.enabled;  
14596         },
14597         
14598         /**
14599          * Enables this KeyMap
14600          */
14601         enable: function(){
14602                 if(!this.enabled){
14603                     this.el.on(this.eventName, this.handleKeyDown, this);
14604                     this.enabled = true;
14605                 }
14606         },
14607
14608         /**
14609          * Disable this KeyMap
14610          */
14611         disable: function(){
14612                 if(this.enabled){
14613                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14614                     this.enabled = false;
14615                 }
14616         }
14617 };/*
14618  * Based on:
14619  * Ext JS Library 1.1.1
14620  * Copyright(c) 2006-2007, Ext JS, LLC.
14621  *
14622  * Originally Released Under LGPL - original licence link has changed is not relivant.
14623  *
14624  * Fork - LGPL
14625  * <script type="text/javascript">
14626  */
14627
14628  
14629 /**
14630  * @class Roo.util.TextMetrics
14631  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14632  * wide, in pixels, a given block of text will be.
14633  * @singleton
14634  */
14635 Roo.util.TextMetrics = function(){
14636     var shared;
14637     return {
14638         /**
14639          * Measures the size of the specified text
14640          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14641          * that can affect the size of the rendered text
14642          * @param {String} text The text to measure
14643          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14644          * in order to accurately measure the text height
14645          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14646          */
14647         measure : function(el, text, fixedWidth){
14648             if(!shared){
14649                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14650             }
14651             shared.bind(el);
14652             shared.setFixedWidth(fixedWidth || 'auto');
14653             return shared.getSize(text);
14654         },
14655
14656         /**
14657          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14658          * the overhead of multiple calls to initialize the style properties on each measurement.
14659          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14660          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14661          * in order to accurately measure the text height
14662          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14663          */
14664         createInstance : function(el, fixedWidth){
14665             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14666         }
14667     };
14668 }();
14669
14670  
14671
14672 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14673     var ml = new Roo.Element(document.createElement('div'));
14674     document.body.appendChild(ml.dom);
14675     ml.position('absolute');
14676     ml.setLeftTop(-1000, -1000);
14677     ml.hide();
14678
14679     if(fixedWidth){
14680         ml.setWidth(fixedWidth);
14681     }
14682      
14683     var instance = {
14684         /**
14685          * Returns the size of the specified text based on the internal element's style and width properties
14686          * @memberOf Roo.util.TextMetrics.Instance#
14687          * @param {String} text The text to measure
14688          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14689          */
14690         getSize : function(text){
14691             ml.update(text);
14692             var s = ml.getSize();
14693             ml.update('');
14694             return s;
14695         },
14696
14697         /**
14698          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14699          * that can affect the size of the rendered text
14700          * @memberOf Roo.util.TextMetrics.Instance#
14701          * @param {String/HTMLElement} el The element, dom node or id
14702          */
14703         bind : function(el){
14704             ml.setStyle(
14705                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14706             );
14707         },
14708
14709         /**
14710          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14711          * to set a fixed width in order to accurately measure the text height.
14712          * @memberOf Roo.util.TextMetrics.Instance#
14713          * @param {Number} width The width to set on the element
14714          */
14715         setFixedWidth : function(width){
14716             ml.setWidth(width);
14717         },
14718
14719         /**
14720          * Returns the measured width of the specified text
14721          * @memberOf Roo.util.TextMetrics.Instance#
14722          * @param {String} text The text to measure
14723          * @return {Number} width The width in pixels
14724          */
14725         getWidth : function(text){
14726             ml.dom.style.width = 'auto';
14727             return this.getSize(text).width;
14728         },
14729
14730         /**
14731          * Returns the measured height of the specified text.  For multiline text, be sure to call
14732          * {@link #setFixedWidth} if necessary.
14733          * @memberOf Roo.util.TextMetrics.Instance#
14734          * @param {String} text The text to measure
14735          * @return {Number} height The height in pixels
14736          */
14737         getHeight : function(text){
14738             return this.getSize(text).height;
14739         }
14740     };
14741
14742     instance.bind(bindTo);
14743
14744     return instance;
14745 };
14746
14747 // backwards compat
14748 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14749  * Based on:
14750  * Ext JS Library 1.1.1
14751  * Copyright(c) 2006-2007, Ext JS, LLC.
14752  *
14753  * Originally Released Under LGPL - original licence link has changed is not relivant.
14754  *
14755  * Fork - LGPL
14756  * <script type="text/javascript">
14757  */
14758
14759 /**
14760  * @class Roo.state.Provider
14761  * Abstract base class for state provider implementations. This class provides methods
14762  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14763  * Provider interface.
14764  */
14765 Roo.state.Provider = function(){
14766     /**
14767      * @event statechange
14768      * Fires when a state change occurs.
14769      * @param {Provider} this This state provider
14770      * @param {String} key The state key which was changed
14771      * @param {String} value The encoded value for the state
14772      */
14773     this.addEvents({
14774         "statechange": true
14775     });
14776     this.state = {};
14777     Roo.state.Provider.superclass.constructor.call(this);
14778 };
14779 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14780     /**
14781      * Returns the current value for a key
14782      * @param {String} name The key name
14783      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14784      * @return {Mixed} The state data
14785      */
14786     get : function(name, defaultValue){
14787         return typeof this.state[name] == "undefined" ?
14788             defaultValue : this.state[name];
14789     },
14790     
14791     /**
14792      * Clears a value from the state
14793      * @param {String} name The key name
14794      */
14795     clear : function(name){
14796         delete this.state[name];
14797         this.fireEvent("statechange", this, name, null);
14798     },
14799     
14800     /**
14801      * Sets the value for a key
14802      * @param {String} name The key name
14803      * @param {Mixed} value The value to set
14804      */
14805     set : function(name, value){
14806         this.state[name] = value;
14807         this.fireEvent("statechange", this, name, value);
14808     },
14809     
14810     /**
14811      * Decodes a string previously encoded with {@link #encodeValue}.
14812      * @param {String} value The value to decode
14813      * @return {Mixed} The decoded value
14814      */
14815     decodeValue : function(cookie){
14816         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14817         var matches = re.exec(unescape(cookie));
14818         if(!matches || !matches[1]) {
14819             return; // non state cookie
14820         }
14821         var type = matches[1];
14822         var v = matches[2];
14823         switch(type){
14824             case "n":
14825                 return parseFloat(v);
14826             case "d":
14827                 return new Date(Date.parse(v));
14828             case "b":
14829                 return (v == "1");
14830             case "a":
14831                 var all = [];
14832                 var values = v.split("^");
14833                 for(var i = 0, len = values.length; i < len; i++){
14834                     all.push(this.decodeValue(values[i]));
14835                 }
14836                 return all;
14837            case "o":
14838                 var all = {};
14839                 var values = v.split("^");
14840                 for(var i = 0, len = values.length; i < len; i++){
14841                     var kv = values[i].split("=");
14842                     all[kv[0]] = this.decodeValue(kv[1]);
14843                 }
14844                 return all;
14845            default:
14846                 return v;
14847         }
14848     },
14849     
14850     /**
14851      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14852      * @param {Mixed} value The value to encode
14853      * @return {String} The encoded value
14854      */
14855     encodeValue : function(v){
14856         var enc;
14857         if(typeof v == "number"){
14858             enc = "n:" + v;
14859         }else if(typeof v == "boolean"){
14860             enc = "b:" + (v ? "1" : "0");
14861         }else if(v instanceof Date){
14862             enc = "d:" + v.toGMTString();
14863         }else if(v instanceof Array){
14864             var flat = "";
14865             for(var i = 0, len = v.length; i < len; i++){
14866                 flat += this.encodeValue(v[i]);
14867                 if(i != len-1) {
14868                     flat += "^";
14869                 }
14870             }
14871             enc = "a:" + flat;
14872         }else if(typeof v == "object"){
14873             var flat = "";
14874             for(var key in v){
14875                 if(typeof v[key] != "function"){
14876                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14877                 }
14878             }
14879             enc = "o:" + flat.substring(0, flat.length-1);
14880         }else{
14881             enc = "s:" + v;
14882         }
14883         return escape(enc);        
14884     }
14885 });
14886
14887 /*
14888  * Based on:
14889  * Ext JS Library 1.1.1
14890  * Copyright(c) 2006-2007, Ext JS, LLC.
14891  *
14892  * Originally Released Under LGPL - original licence link has changed is not relivant.
14893  *
14894  * Fork - LGPL
14895  * <script type="text/javascript">
14896  */
14897 /**
14898  * @class Roo.state.Manager
14899  * This is the global state manager. By default all components that are "state aware" check this class
14900  * for state information if you don't pass them a custom state provider. In order for this class
14901  * to be useful, it must be initialized with a provider when your application initializes.
14902  <pre><code>
14903 // in your initialization function
14904 init : function(){
14905    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14906    ...
14907    // supposed you have a {@link Roo.BorderLayout}
14908    var layout = new Roo.BorderLayout(...);
14909    layout.restoreState();
14910    // or a {Roo.BasicDialog}
14911    var dialog = new Roo.BasicDialog(...);
14912    dialog.restoreState();
14913  </code></pre>
14914  * @singleton
14915  */
14916 Roo.state.Manager = function(){
14917     var provider = new Roo.state.Provider();
14918     
14919     return {
14920         /**
14921          * Configures the default state provider for your application
14922          * @param {Provider} stateProvider The state provider to set
14923          */
14924         setProvider : function(stateProvider){
14925             provider = stateProvider;
14926         },
14927         
14928         /**
14929          * Returns the current value for a key
14930          * @param {String} name The key name
14931          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14932          * @return {Mixed} The state data
14933          */
14934         get : function(key, defaultValue){
14935             return provider.get(key, defaultValue);
14936         },
14937         
14938         /**
14939          * Sets the value for a key
14940          * @param {String} name The key name
14941          * @param {Mixed} value The state data
14942          */
14943          set : function(key, value){
14944             provider.set(key, value);
14945         },
14946         
14947         /**
14948          * Clears a value from the state
14949          * @param {String} name The key name
14950          */
14951         clear : function(key){
14952             provider.clear(key);
14953         },
14954         
14955         /**
14956          * Gets the currently configured state provider
14957          * @return {Provider} The state provider
14958          */
14959         getProvider : function(){
14960             return provider;
14961         }
14962     };
14963 }();
14964 /*
14965  * Based on:
14966  * Ext JS Library 1.1.1
14967  * Copyright(c) 2006-2007, Ext JS, LLC.
14968  *
14969  * Originally Released Under LGPL - original licence link has changed is not relivant.
14970  *
14971  * Fork - LGPL
14972  * <script type="text/javascript">
14973  */
14974 /**
14975  * @class Roo.state.CookieProvider
14976  * @extends Roo.state.Provider
14977  * The default Provider implementation which saves state via cookies.
14978  * <br />Usage:
14979  <pre><code>
14980    var cp = new Roo.state.CookieProvider({
14981        path: "/cgi-bin/",
14982        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14983        domain: "roojs.com"
14984    })
14985    Roo.state.Manager.setProvider(cp);
14986  </code></pre>
14987  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14988  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14989  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14990  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14991  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14992  * domain the page is running on including the 'www' like 'www.roojs.com')
14993  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14994  * @constructor
14995  * Create a new CookieProvider
14996  * @param {Object} config The configuration object
14997  */
14998 Roo.state.CookieProvider = function(config){
14999     Roo.state.CookieProvider.superclass.constructor.call(this);
15000     this.path = "/";
15001     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15002     this.domain = null;
15003     this.secure = false;
15004     Roo.apply(this, config);
15005     this.state = this.readCookies();
15006 };
15007
15008 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15009     // private
15010     set : function(name, value){
15011         if(typeof value == "undefined" || value === null){
15012             this.clear(name);
15013             return;
15014         }
15015         this.setCookie(name, value);
15016         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15017     },
15018
15019     // private
15020     clear : function(name){
15021         this.clearCookie(name);
15022         Roo.state.CookieProvider.superclass.clear.call(this, name);
15023     },
15024
15025     // private
15026     readCookies : function(){
15027         var cookies = {};
15028         var c = document.cookie + ";";
15029         var re = /\s?(.*?)=(.*?);/g;
15030         var matches;
15031         while((matches = re.exec(c)) != null){
15032             var name = matches[1];
15033             var value = matches[2];
15034             if(name && name.substring(0,3) == "ys-"){
15035                 cookies[name.substr(3)] = this.decodeValue(value);
15036             }
15037         }
15038         return cookies;
15039     },
15040
15041     // private
15042     setCookie : function(name, value){
15043         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15044            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15045            ((this.path == null) ? "" : ("; path=" + this.path)) +
15046            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15047            ((this.secure == true) ? "; secure" : "");
15048     },
15049
15050     // private
15051     clearCookie : function(name){
15052         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15053            ((this.path == null) ? "" : ("; path=" + this.path)) +
15054            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15055            ((this.secure == true) ? "; secure" : "");
15056     }
15057 });/*
15058  * Based on:
15059  * Ext JS Library 1.1.1
15060  * Copyright(c) 2006-2007, Ext JS, LLC.
15061  *
15062  * Originally Released Under LGPL - original licence link has changed is not relivant.
15063  *
15064  * Fork - LGPL
15065  * <script type="text/javascript">
15066  */
15067  
15068
15069 /**
15070  * @class Roo.ComponentMgr
15071  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15072  * @singleton
15073  */
15074 Roo.ComponentMgr = function(){
15075     var all = new Roo.util.MixedCollection();
15076
15077     return {
15078         /**
15079          * Registers a component.
15080          * @param {Roo.Component} c The component
15081          */
15082         register : function(c){
15083             all.add(c);
15084         },
15085
15086         /**
15087          * Unregisters a component.
15088          * @param {Roo.Component} c The component
15089          */
15090         unregister : function(c){
15091             all.remove(c);
15092         },
15093
15094         /**
15095          * Returns a component by id
15096          * @param {String} id The component id
15097          */
15098         get : function(id){
15099             return all.get(id);
15100         },
15101
15102         /**
15103          * Registers a function that will be called when a specified component is added to ComponentMgr
15104          * @param {String} id The component id
15105          * @param {Funtction} fn The callback function
15106          * @param {Object} scope The scope of the callback
15107          */
15108         onAvailable : function(id, fn, scope){
15109             all.on("add", function(index, o){
15110                 if(o.id == id){
15111                     fn.call(scope || o, o);
15112                     all.un("add", fn, scope);
15113                 }
15114             });
15115         }
15116     };
15117 }();/*
15118  * Based on:
15119  * Ext JS Library 1.1.1
15120  * Copyright(c) 2006-2007, Ext JS, LLC.
15121  *
15122  * Originally Released Under LGPL - original licence link has changed is not relivant.
15123  *
15124  * Fork - LGPL
15125  * <script type="text/javascript">
15126  */
15127  
15128 /**
15129  * @class Roo.Component
15130  * @extends Roo.util.Observable
15131  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15132  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15133  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15134  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15135  * All visual components (widgets) that require rendering into a layout should subclass Component.
15136  * @constructor
15137  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15138  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
15139  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15140  */
15141 Roo.Component = function(config){
15142     config = config || {};
15143     if(config.tagName || config.dom || typeof config == "string"){ // element object
15144         config = {el: config, id: config.id || config};
15145     }
15146     this.initialConfig = config;
15147
15148     Roo.apply(this, config);
15149     this.addEvents({
15150         /**
15151          * @event disable
15152          * Fires after the component is disabled.
15153              * @param {Roo.Component} this
15154              */
15155         disable : true,
15156         /**
15157          * @event enable
15158          * Fires after the component is enabled.
15159              * @param {Roo.Component} this
15160              */
15161         enable : true,
15162         /**
15163          * @event beforeshow
15164          * Fires before the component is shown.  Return false to stop the show.
15165              * @param {Roo.Component} this
15166              */
15167         beforeshow : true,
15168         /**
15169          * @event show
15170          * Fires after the component is shown.
15171              * @param {Roo.Component} this
15172              */
15173         show : true,
15174         /**
15175          * @event beforehide
15176          * Fires before the component is hidden. Return false to stop the hide.
15177              * @param {Roo.Component} this
15178              */
15179         beforehide : true,
15180         /**
15181          * @event hide
15182          * Fires after the component is hidden.
15183              * @param {Roo.Component} this
15184              */
15185         hide : true,
15186         /**
15187          * @event beforerender
15188          * Fires before the component is rendered. Return false to stop the render.
15189              * @param {Roo.Component} this
15190              */
15191         beforerender : true,
15192         /**
15193          * @event render
15194          * Fires after the component is rendered.
15195              * @param {Roo.Component} this
15196              */
15197         render : true,
15198         /**
15199          * @event beforedestroy
15200          * Fires before the component is destroyed. Return false to stop the destroy.
15201              * @param {Roo.Component} this
15202              */
15203         beforedestroy : true,
15204         /**
15205          * @event destroy
15206          * Fires after the component is destroyed.
15207              * @param {Roo.Component} this
15208              */
15209         destroy : true
15210     });
15211     if(!this.id){
15212         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15213     }
15214     Roo.ComponentMgr.register(this);
15215     Roo.Component.superclass.constructor.call(this);
15216     this.initComponent();
15217     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15218         this.render(this.renderTo);
15219         delete this.renderTo;
15220     }
15221 };
15222
15223 /** @private */
15224 Roo.Component.AUTO_ID = 1000;
15225
15226 Roo.extend(Roo.Component, Roo.util.Observable, {
15227     /**
15228      * @scope Roo.Component.prototype
15229      * @type {Boolean}
15230      * true if this component is hidden. Read-only.
15231      */
15232     hidden : false,
15233     /**
15234      * @type {Boolean}
15235      * true if this component is disabled. Read-only.
15236      */
15237     disabled : false,
15238     /**
15239      * @type {Boolean}
15240      * true if this component has been rendered. Read-only.
15241      */
15242     rendered : false,
15243     
15244     /** @cfg {String} disableClass
15245      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15246      */
15247     disabledClass : "x-item-disabled",
15248         /** @cfg {Boolean} allowDomMove
15249          * Whether the component can move the Dom node when rendering (defaults to true).
15250          */
15251     allowDomMove : true,
15252     /** @cfg {String} hideMode (display|visibility)
15253      * How this component should hidden. Supported values are
15254      * "visibility" (css visibility), "offsets" (negative offset position) and
15255      * "display" (css display) - defaults to "display".
15256      */
15257     hideMode: 'display',
15258
15259     /** @private */
15260     ctype : "Roo.Component",
15261
15262     /**
15263      * @cfg {String} actionMode 
15264      * which property holds the element that used for  hide() / show() / disable() / enable()
15265      * default is 'el' 
15266      */
15267     actionMode : "el",
15268
15269     /** @private */
15270     getActionEl : function(){
15271         return this[this.actionMode];
15272     },
15273
15274     initComponent : Roo.emptyFn,
15275     /**
15276      * If this is a lazy rendering component, render it to its container element.
15277      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
15278      */
15279     render : function(container, position){
15280         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15281             if(!container && this.el){
15282                 this.el = Roo.get(this.el);
15283                 container = this.el.dom.parentNode;
15284                 this.allowDomMove = false;
15285             }
15286             this.container = Roo.get(container);
15287             this.rendered = true;
15288             if(position !== undefined){
15289                 if(typeof position == 'number'){
15290                     position = this.container.dom.childNodes[position];
15291                 }else{
15292                     position = Roo.getDom(position);
15293                 }
15294             }
15295             this.onRender(this.container, position || null);
15296             if(this.cls){
15297                 this.el.addClass(this.cls);
15298                 delete this.cls;
15299             }
15300             if(this.style){
15301                 this.el.applyStyles(this.style);
15302                 delete this.style;
15303             }
15304             this.fireEvent("render", this);
15305             this.afterRender(this.container);
15306             if(this.hidden){
15307                 this.hide();
15308             }
15309             if(this.disabled){
15310                 this.disable();
15311             }
15312         }
15313         return this;
15314     },
15315
15316     /** @private */
15317     // default function is not really useful
15318     onRender : function(ct, position){
15319         if(this.el){
15320             this.el = Roo.get(this.el);
15321             if(this.allowDomMove !== false){
15322                 ct.dom.insertBefore(this.el.dom, position);
15323             }
15324         }
15325     },
15326
15327     /** @private */
15328     getAutoCreate : function(){
15329         var cfg = typeof this.autoCreate == "object" ?
15330                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15331         if(this.id && !cfg.id){
15332             cfg.id = this.id;
15333         }
15334         return cfg;
15335     },
15336
15337     /** @private */
15338     afterRender : Roo.emptyFn,
15339
15340     /**
15341      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15342      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15343      */
15344     destroy : function(){
15345         if(this.fireEvent("beforedestroy", this) !== false){
15346             this.purgeListeners();
15347             this.beforeDestroy();
15348             if(this.rendered){
15349                 this.el.removeAllListeners();
15350                 this.el.remove();
15351                 if(this.actionMode == "container"){
15352                     this.container.remove();
15353                 }
15354             }
15355             this.onDestroy();
15356             Roo.ComponentMgr.unregister(this);
15357             this.fireEvent("destroy", this);
15358         }
15359     },
15360
15361         /** @private */
15362     beforeDestroy : function(){
15363
15364     },
15365
15366         /** @private */
15367         onDestroy : function(){
15368
15369     },
15370
15371     /**
15372      * Returns the underlying {@link Roo.Element}.
15373      * @return {Roo.Element} The element
15374      */
15375     getEl : function(){
15376         return this.el;
15377     },
15378
15379     /**
15380      * Returns the id of this component.
15381      * @return {String}
15382      */
15383     getId : function(){
15384         return this.id;
15385     },
15386
15387     /**
15388      * Try to focus this component.
15389      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15390      * @return {Roo.Component} this
15391      */
15392     focus : function(selectText){
15393         if(this.rendered){
15394             this.el.focus();
15395             if(selectText === true){
15396                 this.el.dom.select();
15397             }
15398         }
15399         return this;
15400     },
15401
15402     /** @private */
15403     blur : function(){
15404         if(this.rendered){
15405             this.el.blur();
15406         }
15407         return this;
15408     },
15409
15410     /**
15411      * Disable this component.
15412      * @return {Roo.Component} this
15413      */
15414     disable : function(){
15415         if(this.rendered){
15416             this.onDisable();
15417         }
15418         this.disabled = true;
15419         this.fireEvent("disable", this);
15420         return this;
15421     },
15422
15423         // private
15424     onDisable : function(){
15425         this.getActionEl().addClass(this.disabledClass);
15426         this.el.dom.disabled = true;
15427     },
15428
15429     /**
15430      * Enable this component.
15431      * @return {Roo.Component} this
15432      */
15433     enable : function(){
15434         if(this.rendered){
15435             this.onEnable();
15436         }
15437         this.disabled = false;
15438         this.fireEvent("enable", this);
15439         return this;
15440     },
15441
15442         // private
15443     onEnable : function(){
15444         this.getActionEl().removeClass(this.disabledClass);
15445         this.el.dom.disabled = false;
15446     },
15447
15448     /**
15449      * Convenience function for setting disabled/enabled by boolean.
15450      * @param {Boolean} disabled
15451      */
15452     setDisabled : function(disabled){
15453         this[disabled ? "disable" : "enable"]();
15454     },
15455
15456     /**
15457      * Show this component.
15458      * @return {Roo.Component} this
15459      */
15460     show: function(){
15461         if(this.fireEvent("beforeshow", this) !== false){
15462             this.hidden = false;
15463             if(this.rendered){
15464                 this.onShow();
15465             }
15466             this.fireEvent("show", this);
15467         }
15468         return this;
15469     },
15470
15471     // private
15472     onShow : function(){
15473         var ae = this.getActionEl();
15474         if(this.hideMode == 'visibility'){
15475             ae.dom.style.visibility = "visible";
15476         }else if(this.hideMode == 'offsets'){
15477             ae.removeClass('x-hidden');
15478         }else{
15479             ae.dom.style.display = "";
15480         }
15481     },
15482
15483     /**
15484      * Hide this component.
15485      * @return {Roo.Component} this
15486      */
15487     hide: function(){
15488         if(this.fireEvent("beforehide", this) !== false){
15489             this.hidden = true;
15490             if(this.rendered){
15491                 this.onHide();
15492             }
15493             this.fireEvent("hide", this);
15494         }
15495         return this;
15496     },
15497
15498     // private
15499     onHide : function(){
15500         var ae = this.getActionEl();
15501         if(this.hideMode == 'visibility'){
15502             ae.dom.style.visibility = "hidden";
15503         }else if(this.hideMode == 'offsets'){
15504             ae.addClass('x-hidden');
15505         }else{
15506             ae.dom.style.display = "none";
15507         }
15508     },
15509
15510     /**
15511      * Convenience function to hide or show this component by boolean.
15512      * @param {Boolean} visible True to show, false to hide
15513      * @return {Roo.Component} this
15514      */
15515     setVisible: function(visible){
15516         if(visible) {
15517             this.show();
15518         }else{
15519             this.hide();
15520         }
15521         return this;
15522     },
15523
15524     /**
15525      * Returns true if this component is visible.
15526      */
15527     isVisible : function(){
15528         return this.getActionEl().isVisible();
15529     },
15530
15531     cloneConfig : function(overrides){
15532         overrides = overrides || {};
15533         var id = overrides.id || Roo.id();
15534         var cfg = Roo.applyIf(overrides, this.initialConfig);
15535         cfg.id = id; // prevent dup id
15536         return new this.constructor(cfg);
15537     }
15538 });/*
15539  * Based on:
15540  * Ext JS Library 1.1.1
15541  * Copyright(c) 2006-2007, Ext JS, LLC.
15542  *
15543  * Originally Released Under LGPL - original licence link has changed is not relivant.
15544  *
15545  * Fork - LGPL
15546  * <script type="text/javascript">
15547  */
15548
15549 /**
15550  * @class Roo.BoxComponent
15551  * @extends Roo.Component
15552  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15553  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15554  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15555  * layout containers.
15556  * @constructor
15557  * @param {Roo.Element/String/Object} config The configuration options.
15558  */
15559 Roo.BoxComponent = function(config){
15560     Roo.Component.call(this, config);
15561     this.addEvents({
15562         /**
15563          * @event resize
15564          * Fires after the component is resized.
15565              * @param {Roo.Component} this
15566              * @param {Number} adjWidth The box-adjusted width that was set
15567              * @param {Number} adjHeight The box-adjusted height that was set
15568              * @param {Number} rawWidth The width that was originally specified
15569              * @param {Number} rawHeight The height that was originally specified
15570              */
15571         resize : true,
15572         /**
15573          * @event move
15574          * Fires after the component is moved.
15575              * @param {Roo.Component} this
15576              * @param {Number} x The new x position
15577              * @param {Number} y The new y position
15578              */
15579         move : true
15580     });
15581 };
15582
15583 Roo.extend(Roo.BoxComponent, Roo.Component, {
15584     // private, set in afterRender to signify that the component has been rendered
15585     boxReady : false,
15586     // private, used to defer height settings to subclasses
15587     deferHeight: false,
15588     /** @cfg {Number} width
15589      * width (optional) size of component
15590      */
15591      /** @cfg {Number} height
15592      * height (optional) size of component
15593      */
15594      
15595     /**
15596      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15597      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15598      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15599      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15600      * @return {Roo.BoxComponent} this
15601      */
15602     setSize : function(w, h){
15603         // support for standard size objects
15604         if(typeof w == 'object'){
15605             h = w.height;
15606             w = w.width;
15607         }
15608         // not rendered
15609         if(!this.boxReady){
15610             this.width = w;
15611             this.height = h;
15612             return this;
15613         }
15614
15615         // prevent recalcs when not needed
15616         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15617             return this;
15618         }
15619         this.lastSize = {width: w, height: h};
15620
15621         var adj = this.adjustSize(w, h);
15622         var aw = adj.width, ah = adj.height;
15623         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15624             var rz = this.getResizeEl();
15625             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15626                 rz.setSize(aw, ah);
15627             }else if(!this.deferHeight && ah !== undefined){
15628                 rz.setHeight(ah);
15629             }else if(aw !== undefined){
15630                 rz.setWidth(aw);
15631             }
15632             this.onResize(aw, ah, w, h);
15633             this.fireEvent('resize', this, aw, ah, w, h);
15634         }
15635         return this;
15636     },
15637
15638     /**
15639      * Gets the current size of the component's underlying element.
15640      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15641      */
15642     getSize : function(){
15643         return this.el.getSize();
15644     },
15645
15646     /**
15647      * Gets the current XY position of the component's underlying element.
15648      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15649      * @return {Array} The XY position of the element (e.g., [100, 200])
15650      */
15651     getPosition : function(local){
15652         if(local === true){
15653             return [this.el.getLeft(true), this.el.getTop(true)];
15654         }
15655         return this.xy || this.el.getXY();
15656     },
15657
15658     /**
15659      * Gets the current box measurements of the component's underlying element.
15660      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15661      * @returns {Object} box An object in the format {x, y, width, height}
15662      */
15663     getBox : function(local){
15664         var s = this.el.getSize();
15665         if(local){
15666             s.x = this.el.getLeft(true);
15667             s.y = this.el.getTop(true);
15668         }else{
15669             var xy = this.xy || this.el.getXY();
15670             s.x = xy[0];
15671             s.y = xy[1];
15672         }
15673         return s;
15674     },
15675
15676     /**
15677      * Sets the current box measurements of the component's underlying element.
15678      * @param {Object} box An object in the format {x, y, width, height}
15679      * @returns {Roo.BoxComponent} this
15680      */
15681     updateBox : function(box){
15682         this.setSize(box.width, box.height);
15683         this.setPagePosition(box.x, box.y);
15684         return this;
15685     },
15686
15687     // protected
15688     getResizeEl : function(){
15689         return this.resizeEl || this.el;
15690     },
15691
15692     // protected
15693     getPositionEl : function(){
15694         return this.positionEl || this.el;
15695     },
15696
15697     /**
15698      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15699      * This method fires the move event.
15700      * @param {Number} left The new left
15701      * @param {Number} top The new top
15702      * @returns {Roo.BoxComponent} this
15703      */
15704     setPosition : function(x, y){
15705         this.x = x;
15706         this.y = y;
15707         if(!this.boxReady){
15708             return this;
15709         }
15710         var adj = this.adjustPosition(x, y);
15711         var ax = adj.x, ay = adj.y;
15712
15713         var el = this.getPositionEl();
15714         if(ax !== undefined || ay !== undefined){
15715             if(ax !== undefined && ay !== undefined){
15716                 el.setLeftTop(ax, ay);
15717             }else if(ax !== undefined){
15718                 el.setLeft(ax);
15719             }else if(ay !== undefined){
15720                 el.setTop(ay);
15721             }
15722             this.onPosition(ax, ay);
15723             this.fireEvent('move', this, ax, ay);
15724         }
15725         return this;
15726     },
15727
15728     /**
15729      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15730      * This method fires the move event.
15731      * @param {Number} x The new x position
15732      * @param {Number} y The new y position
15733      * @returns {Roo.BoxComponent} this
15734      */
15735     setPagePosition : function(x, y){
15736         this.pageX = x;
15737         this.pageY = y;
15738         if(!this.boxReady){
15739             return;
15740         }
15741         if(x === undefined || y === undefined){ // cannot translate undefined points
15742             return;
15743         }
15744         var p = this.el.translatePoints(x, y);
15745         this.setPosition(p.left, p.top);
15746         return this;
15747     },
15748
15749     // private
15750     onRender : function(ct, position){
15751         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15752         if(this.resizeEl){
15753             this.resizeEl = Roo.get(this.resizeEl);
15754         }
15755         if(this.positionEl){
15756             this.positionEl = Roo.get(this.positionEl);
15757         }
15758     },
15759
15760     // private
15761     afterRender : function(){
15762         Roo.BoxComponent.superclass.afterRender.call(this);
15763         this.boxReady = true;
15764         this.setSize(this.width, this.height);
15765         if(this.x || this.y){
15766             this.setPosition(this.x, this.y);
15767         }
15768         if(this.pageX || this.pageY){
15769             this.setPagePosition(this.pageX, this.pageY);
15770         }
15771     },
15772
15773     /**
15774      * Force the component's size to recalculate based on the underlying element's current height and width.
15775      * @returns {Roo.BoxComponent} this
15776      */
15777     syncSize : function(){
15778         delete this.lastSize;
15779         this.setSize(this.el.getWidth(), this.el.getHeight());
15780         return this;
15781     },
15782
15783     /**
15784      * Called after the component is resized, this method is empty by default but can be implemented by any
15785      * subclass that needs to perform custom logic after a resize occurs.
15786      * @param {Number} adjWidth The box-adjusted width that was set
15787      * @param {Number} adjHeight The box-adjusted height that was set
15788      * @param {Number} rawWidth The width that was originally specified
15789      * @param {Number} rawHeight The height that was originally specified
15790      */
15791     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15792
15793     },
15794
15795     /**
15796      * Called after the component is moved, this method is empty by default but can be implemented by any
15797      * subclass that needs to perform custom logic after a move occurs.
15798      * @param {Number} x The new x position
15799      * @param {Number} y The new y position
15800      */
15801     onPosition : function(x, y){
15802
15803     },
15804
15805     // private
15806     adjustSize : function(w, h){
15807         if(this.autoWidth){
15808             w = 'auto';
15809         }
15810         if(this.autoHeight){
15811             h = 'auto';
15812         }
15813         return {width : w, height: h};
15814     },
15815
15816     // private
15817     adjustPosition : function(x, y){
15818         return {x : x, y: y};
15819     }
15820 });/*
15821  * Original code for Roojs - LGPL
15822  * <script type="text/javascript">
15823  */
15824  
15825 /**
15826  * @class Roo.XComponent
15827  * A delayed Element creator...
15828  * Or a way to group chunks of interface together.
15829  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15830  *  used in conjunction with XComponent.build() it will create an instance of each element,
15831  *  then call addxtype() to build the User interface.
15832  * 
15833  * Mypart.xyx = new Roo.XComponent({
15834
15835     parent : 'Mypart.xyz', // empty == document.element.!!
15836     order : '001',
15837     name : 'xxxx'
15838     region : 'xxxx'
15839     disabled : function() {} 
15840      
15841     tree : function() { // return an tree of xtype declared components
15842         var MODULE = this;
15843         return 
15844         {
15845             xtype : 'NestedLayoutPanel',
15846             // technicall
15847         }
15848      ]
15849  *})
15850  *
15851  *
15852  * It can be used to build a big heiracy, with parent etc.
15853  * or you can just use this to render a single compoent to a dom element
15854  * MYPART.render(Roo.Element | String(id) | dom_element )
15855  *
15856  *
15857  * Usage patterns.
15858  *
15859  * Classic Roo
15860  *
15861  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15862  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15863  *
15864  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15865  *
15866  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15867  * - if mulitple topModules exist, the last one is defined as the top module.
15868  *
15869  * Embeded Roo
15870  * 
15871  * When the top level or multiple modules are to embedded into a existing HTML page,
15872  * the parent element can container '#id' of the element where the module will be drawn.
15873  *
15874  * Bootstrap Roo
15875  *
15876  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15877  * it relies more on a include mechanism, where sub modules are included into an outer page.
15878  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15879  * 
15880  * Bootstrap Roo Included elements
15881  *
15882  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15883  * hence confusing the component builder as it thinks there are multiple top level elements. 
15884  *
15885  * 
15886  * 
15887  * @extends Roo.util.Observable
15888  * @constructor
15889  * @param cfg {Object} configuration of component
15890  * 
15891  */
15892 Roo.XComponent = function(cfg) {
15893     Roo.apply(this, cfg);
15894     this.addEvents({ 
15895         /**
15896              * @event built
15897              * Fires when this the componnt is built
15898              * @param {Roo.XComponent} c the component
15899              */
15900         'built' : true
15901         
15902     });
15903     this.region = this.region || 'center'; // default..
15904     Roo.XComponent.register(this);
15905     this.modules = false;
15906     this.el = false; // where the layout goes..
15907     
15908     
15909 }
15910 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15911     /**
15912      * @property el
15913      * The created element (with Roo.factory())
15914      * @type {Roo.Layout}
15915      */
15916     el  : false,
15917     
15918     /**
15919      * @property el
15920      * for BC  - use el in new code
15921      * @type {Roo.Layout}
15922      */
15923     panel : false,
15924     
15925     /**
15926      * @property layout
15927      * for BC  - use el in new code
15928      * @type {Roo.Layout}
15929      */
15930     layout : false,
15931     
15932      /**
15933      * @cfg {Function|boolean} disabled
15934      * If this module is disabled by some rule, return true from the funtion
15935      */
15936     disabled : false,
15937     
15938     /**
15939      * @cfg {String} parent 
15940      * Name of parent element which it get xtype added to..
15941      */
15942     parent: false,
15943     
15944     /**
15945      * @cfg {String} order
15946      * Used to set the order in which elements are created (usefull for multiple tabs)
15947      */
15948     
15949     order : false,
15950     /**
15951      * @cfg {String} name
15952      * String to display while loading.
15953      */
15954     name : false,
15955     /**
15956      * @cfg {String} region
15957      * Region to render component to (defaults to center)
15958      */
15959     region : 'center',
15960     
15961     /**
15962      * @cfg {Array} items
15963      * A single item array - the first element is the root of the tree..
15964      * It's done this way to stay compatible with the Xtype system...
15965      */
15966     items : false,
15967     
15968     /**
15969      * @property _tree
15970      * The method that retuns the tree of parts that make up this compoennt 
15971      * @type {function}
15972      */
15973     _tree  : false,
15974     
15975      /**
15976      * render
15977      * render element to dom or tree
15978      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15979      */
15980     
15981     render : function(el)
15982     {
15983         
15984         el = el || false;
15985         var hp = this.parent ? 1 : 0;
15986         Roo.debug &&  Roo.log(this);
15987         
15988         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15989             // if parent is a '#.....' string, then let's use that..
15990             var ename = this.parent.substr(1);
15991             this.parent = false;
15992             Roo.debug && Roo.log(ename);
15993             switch (ename) {
15994                 case 'bootstrap-body' :
15995                     if (typeof(Roo.bootstrap.Body) != 'undefined') {
15996                         this.parent = { el :  new  Roo.bootstrap.Body() };
15997                         Roo.debug && Roo.log("setting el to doc body");
15998                          
15999                     } else {
16000                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16001                     }
16002                     break;
16003                 case 'bootstrap':
16004                     this.parent = { el : true};
16005                     // fall through
16006                 default:
16007                     el = Roo.get(ename);
16008                     break;
16009             }
16010                 
16011             
16012             if (!el && !this.parent) {
16013                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16014                 return;
16015             }
16016         }
16017         Roo.debug && Roo.log("EL:");
16018         Roo.debug && Roo.log(el);
16019         Roo.debug && Roo.log("this.parent.el:");
16020         Roo.debug && Roo.log(this.parent.el);
16021         
16022         var tree = this._tree ? this._tree() : this.tree();
16023
16024         // altertive root elements ??? - we need a better way to indicate these.
16025         var is_alt = Roo.XComponent.is_alt || (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16026                         (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16027         
16028         if (!this.parent && is_alt) {
16029             //el = Roo.get(document.body);
16030             this.parent = { el : true };
16031         }
16032             
16033             
16034         
16035         if (!this.parent) {
16036             
16037             Roo.debug && Roo.log("no parent - creating one");
16038             
16039             el = el ? Roo.get(el) : false;      
16040             
16041             // it's a top level one..
16042             this.parent =  {
16043                 el : new Roo.BorderLayout(el || document.body, {
16044                 
16045                      center: {
16046                          titlebar: false,
16047                          autoScroll:false,
16048                          closeOnTab: true,
16049                          tabPosition: 'top',
16050                           //resizeTabs: true,
16051                          alwaysShowTabs: el && hp? false :  true,
16052                          hideTabs: el || !hp ? true :  false,
16053                          minTabWidth: 140
16054                      }
16055                  })
16056             };
16057         }
16058         
16059         if (!this.parent.el) {
16060                 // probably an old style ctor, which has been disabled.
16061                 return;
16062
16063         }
16064                 // The 'tree' method is  '_tree now' 
16065             
16066         tree.region = tree.region || this.region;
16067         var is_body = false;
16068         if (this.parent.el === true) {
16069             // bootstrap... - body..
16070             this.parent.el = Roo.factory(tree);
16071             is_body = true;
16072         }
16073         
16074         this.el = this.parent.el.addxtype(tree, undefined, is_body);
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  is_alt
16124      * Is an alternative Root - normally used by bootstrap or other systems,
16125      *    where the top element in the tree can wrap 'body' 
16126      * @type {boolean} true  (default false)
16127      */
16128      
16129     is_alt : false,
16130     /**
16131      * @property  build_from_html
16132      * Build elements from html - used by bootstrap HTML stuff 
16133      *    - this is cleared after build is completed
16134      * @type {boolean} true  (default false)
16135      */
16136      
16137     build_from_html : false,
16138     /**
16139      * Register components to be built later.
16140      *
16141      * This solves the following issues
16142      * - Building is not done on page load, but after an authentication process has occured.
16143      * - Interface elements are registered on page load
16144      * - Parent Interface elements may not be loaded before child, so this handles that..
16145      * 
16146      *
16147      * example:
16148      * 
16149      * MyApp.register({
16150           order : '000001',
16151           module : 'Pman.Tab.projectMgr',
16152           region : 'center',
16153           parent : 'Pman.layout',
16154           disabled : false,  // or use a function..
16155         })
16156      
16157      * * @param {Object} details about module
16158      */
16159     register : function(obj) {
16160                 
16161         Roo.XComponent.event.fireEvent('register', obj);
16162         switch(typeof(obj.disabled) ) {
16163                 
16164             case 'undefined':
16165                 break;
16166             
16167             case 'function':
16168                 if ( obj.disabled() ) {
16169                         return;
16170                 }
16171                 break;
16172             
16173             default:
16174                 if (obj.disabled) {
16175                         return;
16176                 }
16177                 break;
16178         }
16179                 
16180         this.modules.push(obj);
16181          
16182     },
16183     /**
16184      * convert a string to an object..
16185      * eg. 'AAA.BBB' -> finds AAA.BBB
16186
16187      */
16188     
16189     toObject : function(str)
16190     {
16191         if (!str || typeof(str) == 'object') {
16192             return str;
16193         }
16194         if (str.substring(0,1) == '#') {
16195             return str;
16196         }
16197
16198         var ar = str.split('.');
16199         var rt, o;
16200         rt = ar.shift();
16201             /** eval:var:o */
16202         try {
16203             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16204         } catch (e) {
16205             throw "Module not found : " + str;
16206         }
16207         
16208         if (o === false) {
16209             throw "Module not found : " + str;
16210         }
16211         Roo.each(ar, function(e) {
16212             if (typeof(o[e]) == 'undefined') {
16213                 throw "Module not found : " + str;
16214             }
16215             o = o[e];
16216         });
16217         
16218         return o;
16219         
16220     },
16221     
16222     
16223     /**
16224      * move modules into their correct place in the tree..
16225      * 
16226      */
16227     preBuild : function ()
16228     {
16229         var _t = this;
16230         Roo.each(this.modules , function (obj)
16231         {
16232             Roo.XComponent.event.fireEvent('beforebuild', obj);
16233             
16234             var opar = obj.parent;
16235             try { 
16236                 obj.parent = this.toObject(opar);
16237             } catch(e) {
16238                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16239                 return;
16240             }
16241             
16242             if (!obj.parent) {
16243                 Roo.debug && Roo.log("GOT top level module");
16244                 Roo.debug && Roo.log(obj);
16245                 obj.modules = new Roo.util.MixedCollection(false, 
16246                     function(o) { return o.order + '' }
16247                 );
16248                 this.topModule = obj;
16249                 return;
16250             }
16251                         // parent is a string (usually a dom element name..)
16252             if (typeof(obj.parent) == 'string') {
16253                 this.elmodules.push(obj);
16254                 return;
16255             }
16256             if (obj.parent.constructor != Roo.XComponent) {
16257                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16258             }
16259             if (!obj.parent.modules) {
16260                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16261                     function(o) { return o.order + '' }
16262                 );
16263             }
16264             if (obj.parent.disabled) {
16265                 obj.disabled = true;
16266             }
16267             obj.parent.modules.add(obj);
16268         }, this);
16269     },
16270     
16271      /**
16272      * make a list of modules to build.
16273      * @return {Array} list of modules. 
16274      */ 
16275     
16276     buildOrder : function()
16277     {
16278         var _this = this;
16279         var cmp = function(a,b) {   
16280             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16281         };
16282         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16283             throw "No top level modules to build";
16284         }
16285         
16286         // make a flat list in order of modules to build.
16287         var mods = this.topModule ? [ this.topModule ] : [];
16288                 
16289         
16290         // elmodules (is a list of DOM based modules )
16291         Roo.each(this.elmodules, function(e) {
16292             mods.push(e);
16293             if (!this.topModule &&
16294                 typeof(e.parent) == 'string' &&
16295                 e.parent.substring(0,1) == '#' &&
16296                 Roo.get(e.parent.substr(1))
16297                ) {
16298                 
16299                 _this.topModule = e;
16300             }
16301             
16302         });
16303
16304         
16305         // add modules to their parents..
16306         var addMod = function(m) {
16307             Roo.debug && Roo.log("build Order: add: " + m.name);
16308                 
16309             mods.push(m);
16310             if (m.modules && !m.disabled) {
16311                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16312                 m.modules.keySort('ASC',  cmp );
16313                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16314     
16315                 m.modules.each(addMod);
16316             } else {
16317                 Roo.debug && Roo.log("build Order: no child modules");
16318             }
16319             // not sure if this is used any more..
16320             if (m.finalize) {
16321                 m.finalize.name = m.name + " (clean up) ";
16322                 mods.push(m.finalize);
16323             }
16324             
16325         }
16326         if (this.topModule && this.topModule.modules) { 
16327             this.topModule.modules.keySort('ASC',  cmp );
16328             this.topModule.modules.each(addMod);
16329         } 
16330         return mods;
16331     },
16332     
16333      /**
16334      * Build the registered modules.
16335      * @param {Object} parent element.
16336      * @param {Function} optional method to call after module has been added.
16337      * 
16338      */ 
16339    
16340     build : function(opts) 
16341     {
16342         
16343         if (typeof(opts) != 'undefined') {
16344             Roo.apply(this,opts);
16345         }
16346         
16347         this.preBuild();
16348         var mods = this.buildOrder();
16349       
16350         //this.allmods = mods;
16351         //Roo.debug && Roo.log(mods);
16352         //return;
16353         if (!mods.length) { // should not happen
16354             throw "NO modules!!!";
16355         }
16356         
16357         
16358         var msg = "Building Interface...";
16359         // flash it up as modal - so we store the mask!?
16360         if (!this.hideProgress && Roo.MessageBox) {
16361             Roo.MessageBox.show({ title: 'loading' });
16362             Roo.MessageBox.show({
16363                title: "Please wait...",
16364                msg: msg,
16365                width:450,
16366                progress:true,
16367                closable:false,
16368                modal: false
16369               
16370             });
16371         }
16372         var total = mods.length;
16373         
16374         var _this = this;
16375         var progressRun = function() {
16376             if (!mods.length) {
16377                 Roo.debug && Roo.log('hide?');
16378                 if (!this.hideProgress && Roo.MessageBox) {
16379                     Roo.MessageBox.hide();
16380                 }
16381                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16382                 
16383                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16384                 
16385                 // THE END...
16386                 return false;   
16387             }
16388             
16389             var m = mods.shift();
16390             
16391             
16392             Roo.debug && Roo.log(m);
16393             // not sure if this is supported any more.. - modules that are are just function
16394             if (typeof(m) == 'function') { 
16395                 m.call(this);
16396                 return progressRun.defer(10, _this);
16397             } 
16398             
16399             
16400             msg = "Building Interface " + (total  - mods.length) + 
16401                     " of " + total + 
16402                     (m.name ? (' - ' + m.name) : '');
16403                         Roo.debug && Roo.log(msg);
16404             if (!this.hideProgress &&  Roo.MessageBox) { 
16405                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16406             }
16407             
16408          
16409             // is the module disabled?
16410             var disabled = (typeof(m.disabled) == 'function') ?
16411                 m.disabled.call(m.module.disabled) : m.disabled;    
16412             
16413             
16414             if (disabled) {
16415                 return progressRun(); // we do not update the display!
16416             }
16417             
16418             // now build 
16419             
16420                         
16421                         
16422             m.render();
16423             // it's 10 on top level, and 1 on others??? why...
16424             return progressRun.defer(10, _this);
16425              
16426         }
16427         progressRun.defer(1, _this);
16428      
16429         
16430         
16431     },
16432         
16433         
16434         /**
16435          * Event Object.
16436          *
16437          *
16438          */
16439         event: false, 
16440     /**
16441          * wrapper for event.on - aliased later..  
16442          * Typically use to register a event handler for register:
16443          *
16444          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16445          *
16446          */
16447     on : false
16448    
16449     
16450     
16451 });
16452
16453 Roo.XComponent.event = new Roo.util.Observable({
16454                 events : { 
16455                         /**
16456                          * @event register
16457                          * Fires when an Component is registered,
16458                          * set the disable property on the Component to stop registration.
16459                          * @param {Roo.XComponent} c the component being registerd.
16460                          * 
16461                          */
16462                         'register' : true,
16463             /**
16464                          * @event beforebuild
16465                          * Fires before each Component is built
16466                          * can be used to apply permissions.
16467                          * @param {Roo.XComponent} c the component being registerd.
16468                          * 
16469                          */
16470                         'beforebuild' : true,
16471                         /**
16472                          * @event buildcomplete
16473                          * Fires on the top level element when all elements have been built
16474                          * @param {Roo.XComponent} the top level component.
16475                          */
16476                         'buildcomplete' : true
16477                         
16478                 }
16479 });
16480
16481 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16482  /*
16483  * Based on:
16484  * Ext JS Library 1.1.1
16485  * Copyright(c) 2006-2007, Ext JS, LLC.
16486  *
16487  * Originally Released Under LGPL - original licence link has changed is not relivant.
16488  *
16489  * Fork - LGPL
16490  * <script type="text/javascript">
16491  */
16492
16493
16494
16495 /*
16496  * These classes are derivatives of the similarly named classes in the YUI Library.
16497  * The original license:
16498  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16499  * Code licensed under the BSD License:
16500  * http://developer.yahoo.net/yui/license.txt
16501  */
16502
16503 (function() {
16504
16505 var Event=Roo.EventManager;
16506 var Dom=Roo.lib.Dom;
16507
16508 /**
16509  * @class Roo.dd.DragDrop
16510  * @extends Roo.util.Observable
16511  * Defines the interface and base operation of items that that can be
16512  * dragged or can be drop targets.  It was designed to be extended, overriding
16513  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16514  * Up to three html elements can be associated with a DragDrop instance:
16515  * <ul>
16516  * <li>linked element: the element that is passed into the constructor.
16517  * This is the element which defines the boundaries for interaction with
16518  * other DragDrop objects.</li>
16519  * <li>handle element(s): The drag operation only occurs if the element that
16520  * was clicked matches a handle element.  By default this is the linked
16521  * element, but there are times that you will want only a portion of the
16522  * linked element to initiate the drag operation, and the setHandleElId()
16523  * method provides a way to define this.</li>
16524  * <li>drag element: this represents the element that would be moved along
16525  * with the cursor during a drag operation.  By default, this is the linked
16526  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16527  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16528  * </li>
16529  * </ul>
16530  * This class should not be instantiated until the onload event to ensure that
16531  * the associated elements are available.
16532  * The following would define a DragDrop obj that would interact with any
16533  * other DragDrop obj in the "group1" group:
16534  * <pre>
16535  *  dd = new Roo.dd.DragDrop("div1", "group1");
16536  * </pre>
16537  * Since none of the event handlers have been implemented, nothing would
16538  * actually happen if you were to run the code above.  Normally you would
16539  * override this class or one of the default implementations, but you can
16540  * also override the methods you want on an instance of the class...
16541  * <pre>
16542  *  dd.onDragDrop = function(e, id) {
16543  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16544  *  }
16545  * </pre>
16546  * @constructor
16547  * @param {String} id of the element that is linked to this instance
16548  * @param {String} sGroup the group of related DragDrop objects
16549  * @param {object} config an object containing configurable attributes
16550  *                Valid properties for DragDrop:
16551  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16552  */
16553 Roo.dd.DragDrop = function(id, sGroup, config) {
16554     if (id) {
16555         this.init(id, sGroup, config);
16556     }
16557     
16558 };
16559
16560 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16561
16562     /**
16563      * The id of the element associated with this object.  This is what we
16564      * refer to as the "linked element" because the size and position of
16565      * this element is used to determine when the drag and drop objects have
16566      * interacted.
16567      * @property id
16568      * @type String
16569      */
16570     id: null,
16571
16572     /**
16573      * Configuration attributes passed into the constructor
16574      * @property config
16575      * @type object
16576      */
16577     config: null,
16578
16579     /**
16580      * The id of the element that will be dragged.  By default this is same
16581      * as the linked element , but could be changed to another element. Ex:
16582      * Roo.dd.DDProxy
16583      * @property dragElId
16584      * @type String
16585      * @private
16586      */
16587     dragElId: null,
16588
16589     /**
16590      * the id of the element that initiates the drag operation.  By default
16591      * this is the linked element, but could be changed to be a child of this
16592      * element.  This lets us do things like only starting the drag when the
16593      * header element within the linked html element is clicked.
16594      * @property handleElId
16595      * @type String
16596      * @private
16597      */
16598     handleElId: null,
16599
16600     /**
16601      * An associative array of HTML tags that will be ignored if clicked.
16602      * @property invalidHandleTypes
16603      * @type {string: string}
16604      */
16605     invalidHandleTypes: null,
16606
16607     /**
16608      * An associative array of ids for elements that will be ignored if clicked
16609      * @property invalidHandleIds
16610      * @type {string: string}
16611      */
16612     invalidHandleIds: null,
16613
16614     /**
16615      * An indexted array of css class names for elements that will be ignored
16616      * if clicked.
16617      * @property invalidHandleClasses
16618      * @type string[]
16619      */
16620     invalidHandleClasses: null,
16621
16622     /**
16623      * The linked element's absolute X position at the time the drag was
16624      * started
16625      * @property startPageX
16626      * @type int
16627      * @private
16628      */
16629     startPageX: 0,
16630
16631     /**
16632      * The linked element's absolute X position at the time the drag was
16633      * started
16634      * @property startPageY
16635      * @type int
16636      * @private
16637      */
16638     startPageY: 0,
16639
16640     /**
16641      * The group defines a logical collection of DragDrop objects that are
16642      * related.  Instances only get events when interacting with other
16643      * DragDrop object in the same group.  This lets us define multiple
16644      * groups using a single DragDrop subclass if we want.
16645      * @property groups
16646      * @type {string: string}
16647      */
16648     groups: null,
16649
16650     /**
16651      * Individual drag/drop instances can be locked.  This will prevent
16652      * onmousedown start drag.
16653      * @property locked
16654      * @type boolean
16655      * @private
16656      */
16657     locked: false,
16658
16659     /**
16660      * Lock this instance
16661      * @method lock
16662      */
16663     lock: function() { this.locked = true; },
16664
16665     /**
16666      * Unlock this instace
16667      * @method unlock
16668      */
16669     unlock: function() { this.locked = false; },
16670
16671     /**
16672      * By default, all insances can be a drop target.  This can be disabled by
16673      * setting isTarget to false.
16674      * @method isTarget
16675      * @type boolean
16676      */
16677     isTarget: true,
16678
16679     /**
16680      * The padding configured for this drag and drop object for calculating
16681      * the drop zone intersection with this object.
16682      * @method padding
16683      * @type int[]
16684      */
16685     padding: null,
16686
16687     /**
16688      * Cached reference to the linked element
16689      * @property _domRef
16690      * @private
16691      */
16692     _domRef: null,
16693
16694     /**
16695      * Internal typeof flag
16696      * @property __ygDragDrop
16697      * @private
16698      */
16699     __ygDragDrop: true,
16700
16701     /**
16702      * Set to true when horizontal contraints are applied
16703      * @property constrainX
16704      * @type boolean
16705      * @private
16706      */
16707     constrainX: false,
16708
16709     /**
16710      * Set to true when vertical contraints are applied
16711      * @property constrainY
16712      * @type boolean
16713      * @private
16714      */
16715     constrainY: false,
16716
16717     /**
16718      * The left constraint
16719      * @property minX
16720      * @type int
16721      * @private
16722      */
16723     minX: 0,
16724
16725     /**
16726      * The right constraint
16727      * @property maxX
16728      * @type int
16729      * @private
16730      */
16731     maxX: 0,
16732
16733     /**
16734      * The up constraint
16735      * @property minY
16736      * @type int
16737      * @type int
16738      * @private
16739      */
16740     minY: 0,
16741
16742     /**
16743      * The down constraint
16744      * @property maxY
16745      * @type int
16746      * @private
16747      */
16748     maxY: 0,
16749
16750     /**
16751      * Maintain offsets when we resetconstraints.  Set to true when you want
16752      * the position of the element relative to its parent to stay the same
16753      * when the page changes
16754      *
16755      * @property maintainOffset
16756      * @type boolean
16757      */
16758     maintainOffset: false,
16759
16760     /**
16761      * Array of pixel locations the element will snap to if we specified a
16762      * horizontal graduation/interval.  This array is generated automatically
16763      * when you define a tick interval.
16764      * @property xTicks
16765      * @type int[]
16766      */
16767     xTicks: null,
16768
16769     /**
16770      * Array of pixel locations the element will snap to if we specified a
16771      * vertical graduation/interval.  This array is generated automatically
16772      * when you define a tick interval.
16773      * @property yTicks
16774      * @type int[]
16775      */
16776     yTicks: null,
16777
16778     /**
16779      * By default the drag and drop instance will only respond to the primary
16780      * button click (left button for a right-handed mouse).  Set to true to
16781      * allow drag and drop to start with any mouse click that is propogated
16782      * by the browser
16783      * @property primaryButtonOnly
16784      * @type boolean
16785      */
16786     primaryButtonOnly: true,
16787
16788     /**
16789      * The availabe property is false until the linked dom element is accessible.
16790      * @property available
16791      * @type boolean
16792      */
16793     available: false,
16794
16795     /**
16796      * By default, drags can only be initiated if the mousedown occurs in the
16797      * region the linked element is.  This is done in part to work around a
16798      * bug in some browsers that mis-report the mousedown if the previous
16799      * mouseup happened outside of the window.  This property is set to true
16800      * if outer handles are defined.
16801      *
16802      * @property hasOuterHandles
16803      * @type boolean
16804      * @default false
16805      */
16806     hasOuterHandles: false,
16807
16808     /**
16809      * Code that executes immediately before the startDrag event
16810      * @method b4StartDrag
16811      * @private
16812      */
16813     b4StartDrag: function(x, y) { },
16814
16815     /**
16816      * Abstract method called after a drag/drop object is clicked
16817      * and the drag or mousedown time thresholds have beeen met.
16818      * @method startDrag
16819      * @param {int} X click location
16820      * @param {int} Y click location
16821      */
16822     startDrag: function(x, y) { /* override this */ },
16823
16824     /**
16825      * Code that executes immediately before the onDrag event
16826      * @method b4Drag
16827      * @private
16828      */
16829     b4Drag: function(e) { },
16830
16831     /**
16832      * Abstract method called during the onMouseMove event while dragging an
16833      * object.
16834      * @method onDrag
16835      * @param {Event} e the mousemove event
16836      */
16837     onDrag: function(e) { /* override this */ },
16838
16839     /**
16840      * Abstract method called when this element fist begins hovering over
16841      * another DragDrop obj
16842      * @method onDragEnter
16843      * @param {Event} e the mousemove event
16844      * @param {String|DragDrop[]} id In POINT mode, the element
16845      * id this is hovering over.  In INTERSECT mode, an array of one or more
16846      * dragdrop items being hovered over.
16847      */
16848     onDragEnter: function(e, id) { /* override this */ },
16849
16850     /**
16851      * Code that executes immediately before the onDragOver event
16852      * @method b4DragOver
16853      * @private
16854      */
16855     b4DragOver: function(e) { },
16856
16857     /**
16858      * Abstract method called when this element is hovering over another
16859      * DragDrop obj
16860      * @method onDragOver
16861      * @param {Event} e the mousemove event
16862      * @param {String|DragDrop[]} id In POINT mode, the element
16863      * id this is hovering over.  In INTERSECT mode, an array of dd items
16864      * being hovered over.
16865      */
16866     onDragOver: function(e, id) { /* override this */ },
16867
16868     /**
16869      * Code that executes immediately before the onDragOut event
16870      * @method b4DragOut
16871      * @private
16872      */
16873     b4DragOut: function(e) { },
16874
16875     /**
16876      * Abstract method called when we are no longer hovering over an element
16877      * @method onDragOut
16878      * @param {Event} e the mousemove event
16879      * @param {String|DragDrop[]} id In POINT mode, the element
16880      * id this was hovering over.  In INTERSECT mode, an array of dd items
16881      * that the mouse is no longer over.
16882      */
16883     onDragOut: function(e, id) { /* override this */ },
16884
16885     /**
16886      * Code that executes immediately before the onDragDrop event
16887      * @method b4DragDrop
16888      * @private
16889      */
16890     b4DragDrop: function(e) { },
16891
16892     /**
16893      * Abstract method called when this item is dropped on another DragDrop
16894      * obj
16895      * @method onDragDrop
16896      * @param {Event} e the mouseup event
16897      * @param {String|DragDrop[]} id In POINT mode, the element
16898      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16899      * was dropped on.
16900      */
16901     onDragDrop: function(e, id) { /* override this */ },
16902
16903     /**
16904      * Abstract method called when this item is dropped on an area with no
16905      * drop target
16906      * @method onInvalidDrop
16907      * @param {Event} e the mouseup event
16908      */
16909     onInvalidDrop: function(e) { /* override this */ },
16910
16911     /**
16912      * Code that executes immediately before the endDrag event
16913      * @method b4EndDrag
16914      * @private
16915      */
16916     b4EndDrag: function(e) { },
16917
16918     /**
16919      * Fired when we are done dragging the object
16920      * @method endDrag
16921      * @param {Event} e the mouseup event
16922      */
16923     endDrag: function(e) { /* override this */ },
16924
16925     /**
16926      * Code executed immediately before the onMouseDown event
16927      * @method b4MouseDown
16928      * @param {Event} e the mousedown event
16929      * @private
16930      */
16931     b4MouseDown: function(e) {  },
16932
16933     /**
16934      * Event handler that fires when a drag/drop obj gets a mousedown
16935      * @method onMouseDown
16936      * @param {Event} e the mousedown event
16937      */
16938     onMouseDown: function(e) { /* override this */ },
16939
16940     /**
16941      * Event handler that fires when a drag/drop obj gets a mouseup
16942      * @method onMouseUp
16943      * @param {Event} e the mouseup event
16944      */
16945     onMouseUp: function(e) { /* override this */ },
16946
16947     /**
16948      * Override the onAvailable method to do what is needed after the initial
16949      * position was determined.
16950      * @method onAvailable
16951      */
16952     onAvailable: function () {
16953     },
16954
16955     /*
16956      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16957      * @type Object
16958      */
16959     defaultPadding : {left:0, right:0, top:0, bottom:0},
16960
16961     /*
16962      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16963  *
16964  * Usage:
16965  <pre><code>
16966  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16967                 { dragElId: "existingProxyDiv" });
16968  dd.startDrag = function(){
16969      this.constrainTo("parent-id");
16970  };
16971  </code></pre>
16972  * Or you can initalize it using the {@link Roo.Element} object:
16973  <pre><code>
16974  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16975      startDrag : function(){
16976          this.constrainTo("parent-id");
16977      }
16978  });
16979  </code></pre>
16980      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16981      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16982      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16983      * an object containing the sides to pad. For example: {right:10, bottom:10}
16984      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16985      */
16986     constrainTo : function(constrainTo, pad, inContent){
16987         if(typeof pad == "number"){
16988             pad = {left: pad, right:pad, top:pad, bottom:pad};
16989         }
16990         pad = pad || this.defaultPadding;
16991         var b = Roo.get(this.getEl()).getBox();
16992         var ce = Roo.get(constrainTo);
16993         var s = ce.getScroll();
16994         var c, cd = ce.dom;
16995         if(cd == document.body){
16996             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16997         }else{
16998             xy = ce.getXY();
16999             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
17000         }
17001
17002
17003         var topSpace = b.y - c.y;
17004         var leftSpace = b.x - c.x;
17005
17006         this.resetConstraints();
17007         this.setXConstraint(leftSpace - (pad.left||0), // left
17008                 c.width - leftSpace - b.width - (pad.right||0) //right
17009         );
17010         this.setYConstraint(topSpace - (pad.top||0), //top
17011                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
17012         );
17013     },
17014
17015     /**
17016      * Returns a reference to the linked element
17017      * @method getEl
17018      * @return {HTMLElement} the html element
17019      */
17020     getEl: function() {
17021         if (!this._domRef) {
17022             this._domRef = Roo.getDom(this.id);
17023         }
17024
17025         return this._domRef;
17026     },
17027
17028     /**
17029      * Returns a reference to the actual element to drag.  By default this is
17030      * the same as the html element, but it can be assigned to another
17031      * element. An example of this can be found in Roo.dd.DDProxy
17032      * @method getDragEl
17033      * @return {HTMLElement} the html element
17034      */
17035     getDragEl: function() {
17036         return Roo.getDom(this.dragElId);
17037     },
17038
17039     /**
17040      * Sets up the DragDrop object.  Must be called in the constructor of any
17041      * Roo.dd.DragDrop subclass
17042      * @method init
17043      * @param id the id of the linked element
17044      * @param {String} sGroup the group of related items
17045      * @param {object} config configuration attributes
17046      */
17047     init: function(id, sGroup, config) {
17048         this.initTarget(id, sGroup, config);
17049         if (!Roo.isTouch) {
17050             Event.on(this.id, "mousedown", this.handleMouseDown, this);
17051         }
17052         Event.on(this.id, "touchstart", this.handleMouseDown, this);
17053         // Event.on(this.id, "selectstart", Event.preventDefault);
17054     },
17055
17056     /**
17057      * Initializes Targeting functionality only... the object does not
17058      * get a mousedown handler.
17059      * @method initTarget
17060      * @param id the id of the linked element
17061      * @param {String} sGroup the group of related items
17062      * @param {object} config configuration attributes
17063      */
17064     initTarget: function(id, sGroup, config) {
17065
17066         // configuration attributes
17067         this.config = config || {};
17068
17069         // create a local reference to the drag and drop manager
17070         this.DDM = Roo.dd.DDM;
17071         // initialize the groups array
17072         this.groups = {};
17073
17074         // assume that we have an element reference instead of an id if the
17075         // parameter is not a string
17076         if (typeof id !== "string") {
17077             id = Roo.id(id);
17078         }
17079
17080         // set the id
17081         this.id = id;
17082
17083         // add to an interaction group
17084         this.addToGroup((sGroup) ? sGroup : "default");
17085
17086         // We don't want to register this as the handle with the manager
17087         // so we just set the id rather than calling the setter.
17088         this.handleElId = id;
17089
17090         // the linked element is the element that gets dragged by default
17091         this.setDragElId(id);
17092
17093         // by default, clicked anchors will not start drag operations.
17094         this.invalidHandleTypes = { A: "A" };
17095         this.invalidHandleIds = {};
17096         this.invalidHandleClasses = [];
17097
17098         this.applyConfig();
17099
17100         this.handleOnAvailable();
17101     },
17102
17103     /**
17104      * Applies the configuration parameters that were passed into the constructor.
17105      * This is supposed to happen at each level through the inheritance chain.  So
17106      * a DDProxy implentation will execute apply config on DDProxy, DD, and
17107      * DragDrop in order to get all of the parameters that are available in
17108      * each object.
17109      * @method applyConfig
17110      */
17111     applyConfig: function() {
17112
17113         // configurable properties:
17114         //    padding, isTarget, maintainOffset, primaryButtonOnly
17115         this.padding           = this.config.padding || [0, 0, 0, 0];
17116         this.isTarget          = (this.config.isTarget !== false);
17117         this.maintainOffset    = (this.config.maintainOffset);
17118         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
17119
17120     },
17121
17122     /**
17123      * Executed when the linked element is available
17124      * @method handleOnAvailable
17125      * @private
17126      */
17127     handleOnAvailable: function() {
17128         this.available = true;
17129         this.resetConstraints();
17130         this.onAvailable();
17131     },
17132
17133      /**
17134      * Configures the padding for the target zone in px.  Effectively expands
17135      * (or reduces) the virtual object size for targeting calculations.
17136      * Supports css-style shorthand; if only one parameter is passed, all sides
17137      * will have that padding, and if only two are passed, the top and bottom
17138      * will have the first param, the left and right the second.
17139      * @method setPadding
17140      * @param {int} iTop    Top pad
17141      * @param {int} iRight  Right pad
17142      * @param {int} iBot    Bot pad
17143      * @param {int} iLeft   Left pad
17144      */
17145     setPadding: function(iTop, iRight, iBot, iLeft) {
17146         // this.padding = [iLeft, iRight, iTop, iBot];
17147         if (!iRight && 0 !== iRight) {
17148             this.padding = [iTop, iTop, iTop, iTop];
17149         } else if (!iBot && 0 !== iBot) {
17150             this.padding = [iTop, iRight, iTop, iRight];
17151         } else {
17152             this.padding = [iTop, iRight, iBot, iLeft];
17153         }
17154     },
17155
17156     /**
17157      * Stores the initial placement of the linked element.
17158      * @method setInitialPosition
17159      * @param {int} diffX   the X offset, default 0
17160      * @param {int} diffY   the Y offset, default 0
17161      */
17162     setInitPosition: function(diffX, diffY) {
17163         var el = this.getEl();
17164
17165         if (!this.DDM.verifyEl(el)) {
17166             return;
17167         }
17168
17169         var dx = diffX || 0;
17170         var dy = diffY || 0;
17171
17172         var p = Dom.getXY( el );
17173
17174         this.initPageX = p[0] - dx;
17175         this.initPageY = p[1] - dy;
17176
17177         this.lastPageX = p[0];
17178         this.lastPageY = p[1];
17179
17180
17181         this.setStartPosition(p);
17182     },
17183
17184     /**
17185      * Sets the start position of the element.  This is set when the obj
17186      * is initialized, the reset when a drag is started.
17187      * @method setStartPosition
17188      * @param pos current position (from previous lookup)
17189      * @private
17190      */
17191     setStartPosition: function(pos) {
17192         var p = pos || Dom.getXY( this.getEl() );
17193         this.deltaSetXY = null;
17194
17195         this.startPageX = p[0];
17196         this.startPageY = p[1];
17197     },
17198
17199     /**
17200      * Add this instance to a group of related drag/drop objects.  All
17201      * instances belong to at least one group, and can belong to as many
17202      * groups as needed.
17203      * @method addToGroup
17204      * @param sGroup {string} the name of the group
17205      */
17206     addToGroup: function(sGroup) {
17207         this.groups[sGroup] = true;
17208         this.DDM.regDragDrop(this, sGroup);
17209     },
17210
17211     /**
17212      * Remove's this instance from the supplied interaction group
17213      * @method removeFromGroup
17214      * @param {string}  sGroup  The group to drop
17215      */
17216     removeFromGroup: function(sGroup) {
17217         if (this.groups[sGroup]) {
17218             delete this.groups[sGroup];
17219         }
17220
17221         this.DDM.removeDDFromGroup(this, sGroup);
17222     },
17223
17224     /**
17225      * Allows you to specify that an element other than the linked element
17226      * will be moved with the cursor during a drag
17227      * @method setDragElId
17228      * @param id {string} the id of the element that will be used to initiate the drag
17229      */
17230     setDragElId: function(id) {
17231         this.dragElId = id;
17232     },
17233
17234     /**
17235      * Allows you to specify a child of the linked element that should be
17236      * used to initiate the drag operation.  An example of this would be if
17237      * you have a content div with text and links.  Clicking anywhere in the
17238      * content area would normally start the drag operation.  Use this method
17239      * to specify that an element inside of the content div is the element
17240      * that starts the drag operation.
17241      * @method setHandleElId
17242      * @param id {string} the id of the element that will be used to
17243      * initiate the drag.
17244      */
17245     setHandleElId: function(id) {
17246         if (typeof id !== "string") {
17247             id = Roo.id(id);
17248         }
17249         this.handleElId = id;
17250         this.DDM.regHandle(this.id, id);
17251     },
17252
17253     /**
17254      * Allows you to set an element outside of the linked element as a drag
17255      * handle
17256      * @method setOuterHandleElId
17257      * @param id the id of the element that will be used to initiate the drag
17258      */
17259     setOuterHandleElId: function(id) {
17260         if (typeof id !== "string") {
17261             id = Roo.id(id);
17262         }
17263         Event.on(id, "mousedown",
17264                 this.handleMouseDown, this);
17265         this.setHandleElId(id);
17266
17267         this.hasOuterHandles = true;
17268     },
17269
17270     /**
17271      * Remove all drag and drop hooks for this element
17272      * @method unreg
17273      */
17274     unreg: function() {
17275         Event.un(this.id, "mousedown",
17276                 this.handleMouseDown);
17277         Event.un(this.id, "touchstart",
17278                 this.handleMouseDown);
17279         this._domRef = null;
17280         this.DDM._remove(this);
17281     },
17282
17283     destroy : function(){
17284         this.unreg();
17285     },
17286
17287     /**
17288      * Returns true if this instance is locked, or the drag drop mgr is locked
17289      * (meaning that all drag/drop is disabled on the page.)
17290      * @method isLocked
17291      * @return {boolean} true if this obj or all drag/drop is locked, else
17292      * false
17293      */
17294     isLocked: function() {
17295         return (this.DDM.isLocked() || this.locked);
17296     },
17297
17298     /**
17299      * Fired when this object is clicked
17300      * @method handleMouseDown
17301      * @param {Event} e
17302      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17303      * @private
17304      */
17305     handleMouseDown: function(e, oDD){
17306      
17307         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17308             //Roo.log('not touch/ button !=0');
17309             return;
17310         }
17311         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17312             return; // double touch..
17313         }
17314         
17315
17316         if (this.isLocked()) {
17317             //Roo.log('locked');
17318             return;
17319         }
17320
17321         this.DDM.refreshCache(this.groups);
17322 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17323         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17324         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17325             //Roo.log('no outer handes or not over target');
17326                 // do nothing.
17327         } else {
17328 //            Roo.log('check validator');
17329             if (this.clickValidator(e)) {
17330 //                Roo.log('validate success');
17331                 // set the initial element position
17332                 this.setStartPosition();
17333
17334
17335                 this.b4MouseDown(e);
17336                 this.onMouseDown(e);
17337
17338                 this.DDM.handleMouseDown(e, this);
17339
17340                 this.DDM.stopEvent(e);
17341             } else {
17342
17343
17344             }
17345         }
17346     },
17347
17348     clickValidator: function(e) {
17349         var target = e.getTarget();
17350         return ( this.isValidHandleChild(target) &&
17351                     (this.id == this.handleElId ||
17352                         this.DDM.handleWasClicked(target, this.id)) );
17353     },
17354
17355     /**
17356      * Allows you to specify a tag name that should not start a drag operation
17357      * when clicked.  This is designed to facilitate embedding links within a
17358      * drag handle that do something other than start the drag.
17359      * @method addInvalidHandleType
17360      * @param {string} tagName the type of element to exclude
17361      */
17362     addInvalidHandleType: function(tagName) {
17363         var type = tagName.toUpperCase();
17364         this.invalidHandleTypes[type] = type;
17365     },
17366
17367     /**
17368      * Lets you to specify an element id for a child of a drag handle
17369      * that should not initiate a drag
17370      * @method addInvalidHandleId
17371      * @param {string} id the element id of the element you wish to ignore
17372      */
17373     addInvalidHandleId: function(id) {
17374         if (typeof id !== "string") {
17375             id = Roo.id(id);
17376         }
17377         this.invalidHandleIds[id] = id;
17378     },
17379
17380     /**
17381      * Lets you specify a css class of elements that will not initiate a drag
17382      * @method addInvalidHandleClass
17383      * @param {string} cssClass the class of the elements you wish to ignore
17384      */
17385     addInvalidHandleClass: function(cssClass) {
17386         this.invalidHandleClasses.push(cssClass);
17387     },
17388
17389     /**
17390      * Unsets an excluded tag name set by addInvalidHandleType
17391      * @method removeInvalidHandleType
17392      * @param {string} tagName the type of element to unexclude
17393      */
17394     removeInvalidHandleType: function(tagName) {
17395         var type = tagName.toUpperCase();
17396         // this.invalidHandleTypes[type] = null;
17397         delete this.invalidHandleTypes[type];
17398     },
17399
17400     /**
17401      * Unsets an invalid handle id
17402      * @method removeInvalidHandleId
17403      * @param {string} id the id of the element to re-enable
17404      */
17405     removeInvalidHandleId: function(id) {
17406         if (typeof id !== "string") {
17407             id = Roo.id(id);
17408         }
17409         delete this.invalidHandleIds[id];
17410     },
17411
17412     /**
17413      * Unsets an invalid css class
17414      * @method removeInvalidHandleClass
17415      * @param {string} cssClass the class of the element(s) you wish to
17416      * re-enable
17417      */
17418     removeInvalidHandleClass: function(cssClass) {
17419         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17420             if (this.invalidHandleClasses[i] == cssClass) {
17421                 delete this.invalidHandleClasses[i];
17422             }
17423         }
17424     },
17425
17426     /**
17427      * Checks the tag exclusion list to see if this click should be ignored
17428      * @method isValidHandleChild
17429      * @param {HTMLElement} node the HTMLElement to evaluate
17430      * @return {boolean} true if this is a valid tag type, false if not
17431      */
17432     isValidHandleChild: function(node) {
17433
17434         var valid = true;
17435         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17436         var nodeName;
17437         try {
17438             nodeName = node.nodeName.toUpperCase();
17439         } catch(e) {
17440             nodeName = node.nodeName;
17441         }
17442         valid = valid && !this.invalidHandleTypes[nodeName];
17443         valid = valid && !this.invalidHandleIds[node.id];
17444
17445         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17446             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17447         }
17448
17449
17450         return valid;
17451
17452     },
17453
17454     /**
17455      * Create the array of horizontal tick marks if an interval was specified
17456      * in setXConstraint().
17457      * @method setXTicks
17458      * @private
17459      */
17460     setXTicks: function(iStartX, iTickSize) {
17461         this.xTicks = [];
17462         this.xTickSize = iTickSize;
17463
17464         var tickMap = {};
17465
17466         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17467             if (!tickMap[i]) {
17468                 this.xTicks[this.xTicks.length] = i;
17469                 tickMap[i] = true;
17470             }
17471         }
17472
17473         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17474             if (!tickMap[i]) {
17475                 this.xTicks[this.xTicks.length] = i;
17476                 tickMap[i] = true;
17477             }
17478         }
17479
17480         this.xTicks.sort(this.DDM.numericSort) ;
17481     },
17482
17483     /**
17484      * Create the array of vertical tick marks if an interval was specified in
17485      * setYConstraint().
17486      * @method setYTicks
17487      * @private
17488      */
17489     setYTicks: function(iStartY, iTickSize) {
17490         this.yTicks = [];
17491         this.yTickSize = iTickSize;
17492
17493         var tickMap = {};
17494
17495         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17496             if (!tickMap[i]) {
17497                 this.yTicks[this.yTicks.length] = i;
17498                 tickMap[i] = true;
17499             }
17500         }
17501
17502         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17503             if (!tickMap[i]) {
17504                 this.yTicks[this.yTicks.length] = i;
17505                 tickMap[i] = true;
17506             }
17507         }
17508
17509         this.yTicks.sort(this.DDM.numericSort) ;
17510     },
17511
17512     /**
17513      * By default, the element can be dragged any place on the screen.  Use
17514      * this method to limit the horizontal travel of the element.  Pass in
17515      * 0,0 for the parameters if you want to lock the drag to the y axis.
17516      * @method setXConstraint
17517      * @param {int} iLeft the number of pixels the element can move to the left
17518      * @param {int} iRight the number of pixels the element can move to the
17519      * right
17520      * @param {int} iTickSize optional parameter for specifying that the
17521      * element
17522      * should move iTickSize pixels at a time.
17523      */
17524     setXConstraint: function(iLeft, iRight, iTickSize) {
17525         this.leftConstraint = iLeft;
17526         this.rightConstraint = iRight;
17527
17528         this.minX = this.initPageX - iLeft;
17529         this.maxX = this.initPageX + iRight;
17530         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17531
17532         this.constrainX = true;
17533     },
17534
17535     /**
17536      * Clears any constraints applied to this instance.  Also clears ticks
17537      * since they can't exist independent of a constraint at this time.
17538      * @method clearConstraints
17539      */
17540     clearConstraints: function() {
17541         this.constrainX = false;
17542         this.constrainY = false;
17543         this.clearTicks();
17544     },
17545
17546     /**
17547      * Clears any tick interval defined for this instance
17548      * @method clearTicks
17549      */
17550     clearTicks: function() {
17551         this.xTicks = null;
17552         this.yTicks = null;
17553         this.xTickSize = 0;
17554         this.yTickSize = 0;
17555     },
17556
17557     /**
17558      * By default, the element can be dragged any place on the screen.  Set
17559      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17560      * parameters if you want to lock the drag to the x axis.
17561      * @method setYConstraint
17562      * @param {int} iUp the number of pixels the element can move up
17563      * @param {int} iDown the number of pixels the element can move down
17564      * @param {int} iTickSize optional parameter for specifying that the
17565      * element should move iTickSize pixels at a time.
17566      */
17567     setYConstraint: function(iUp, iDown, iTickSize) {
17568         this.topConstraint = iUp;
17569         this.bottomConstraint = iDown;
17570
17571         this.minY = this.initPageY - iUp;
17572         this.maxY = this.initPageY + iDown;
17573         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17574
17575         this.constrainY = true;
17576
17577     },
17578
17579     /**
17580      * resetConstraints must be called if you manually reposition a dd element.
17581      * @method resetConstraints
17582      * @param {boolean} maintainOffset
17583      */
17584     resetConstraints: function() {
17585
17586
17587         // Maintain offsets if necessary
17588         if (this.initPageX || this.initPageX === 0) {
17589             // figure out how much this thing has moved
17590             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17591             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17592
17593             this.setInitPosition(dx, dy);
17594
17595         // This is the first time we have detected the element's position
17596         } else {
17597             this.setInitPosition();
17598         }
17599
17600         if (this.constrainX) {
17601             this.setXConstraint( this.leftConstraint,
17602                                  this.rightConstraint,
17603                                  this.xTickSize        );
17604         }
17605
17606         if (this.constrainY) {
17607             this.setYConstraint( this.topConstraint,
17608                                  this.bottomConstraint,
17609                                  this.yTickSize         );
17610         }
17611     },
17612
17613     /**
17614      * Normally the drag element is moved pixel by pixel, but we can specify
17615      * that it move a number of pixels at a time.  This method resolves the
17616      * location when we have it set up like this.
17617      * @method getTick
17618      * @param {int} val where we want to place the object
17619      * @param {int[]} tickArray sorted array of valid points
17620      * @return {int} the closest tick
17621      * @private
17622      */
17623     getTick: function(val, tickArray) {
17624
17625         if (!tickArray) {
17626             // If tick interval is not defined, it is effectively 1 pixel,
17627             // so we return the value passed to us.
17628             return val;
17629         } else if (tickArray[0] >= val) {
17630             // The value is lower than the first tick, so we return the first
17631             // tick.
17632             return tickArray[0];
17633         } else {
17634             for (var i=0, len=tickArray.length; i<len; ++i) {
17635                 var next = i + 1;
17636                 if (tickArray[next] && tickArray[next] >= val) {
17637                     var diff1 = val - tickArray[i];
17638                     var diff2 = tickArray[next] - val;
17639                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17640                 }
17641             }
17642
17643             // The value is larger than the last tick, so we return the last
17644             // tick.
17645             return tickArray[tickArray.length - 1];
17646         }
17647     },
17648
17649     /**
17650      * toString method
17651      * @method toString
17652      * @return {string} string representation of the dd obj
17653      */
17654     toString: function() {
17655         return ("DragDrop " + this.id);
17656     }
17657
17658 });
17659
17660 })();
17661 /*
17662  * Based on:
17663  * Ext JS Library 1.1.1
17664  * Copyright(c) 2006-2007, Ext JS, LLC.
17665  *
17666  * Originally Released Under LGPL - original licence link has changed is not relivant.
17667  *
17668  * Fork - LGPL
17669  * <script type="text/javascript">
17670  */
17671
17672
17673 /**
17674  * The drag and drop utility provides a framework for building drag and drop
17675  * applications.  In addition to enabling drag and drop for specific elements,
17676  * the drag and drop elements are tracked by the manager class, and the
17677  * interactions between the various elements are tracked during the drag and
17678  * the implementing code is notified about these important moments.
17679  */
17680
17681 // Only load the library once.  Rewriting the manager class would orphan
17682 // existing drag and drop instances.
17683 if (!Roo.dd.DragDropMgr) {
17684
17685 /**
17686  * @class Roo.dd.DragDropMgr
17687  * DragDropMgr is a singleton that tracks the element interaction for
17688  * all DragDrop items in the window.  Generally, you will not call
17689  * this class directly, but it does have helper methods that could
17690  * be useful in your DragDrop implementations.
17691  * @singleton
17692  */
17693 Roo.dd.DragDropMgr = function() {
17694
17695     var Event = Roo.EventManager;
17696
17697     return {
17698
17699         /**
17700          * Two dimensional Array of registered DragDrop objects.  The first
17701          * dimension is the DragDrop item group, the second the DragDrop
17702          * object.
17703          * @property ids
17704          * @type {string: string}
17705          * @private
17706          * @static
17707          */
17708         ids: {},
17709
17710         /**
17711          * Array of element ids defined as drag handles.  Used to determine
17712          * if the element that generated the mousedown event is actually the
17713          * handle and not the html element itself.
17714          * @property handleIds
17715          * @type {string: string}
17716          * @private
17717          * @static
17718          */
17719         handleIds: {},
17720
17721         /**
17722          * the DragDrop object that is currently being dragged
17723          * @property dragCurrent
17724          * @type DragDrop
17725          * @private
17726          * @static
17727          **/
17728         dragCurrent: null,
17729
17730         /**
17731          * the DragDrop object(s) that are being hovered over
17732          * @property dragOvers
17733          * @type Array
17734          * @private
17735          * @static
17736          */
17737         dragOvers: {},
17738
17739         /**
17740          * the X distance between the cursor and the object being dragged
17741          * @property deltaX
17742          * @type int
17743          * @private
17744          * @static
17745          */
17746         deltaX: 0,
17747
17748         /**
17749          * the Y distance between the cursor and the object being dragged
17750          * @property deltaY
17751          * @type int
17752          * @private
17753          * @static
17754          */
17755         deltaY: 0,
17756
17757         /**
17758          * Flag to determine if we should prevent the default behavior of the
17759          * events we define. By default this is true, but this can be set to
17760          * false if you need the default behavior (not recommended)
17761          * @property preventDefault
17762          * @type boolean
17763          * @static
17764          */
17765         preventDefault: true,
17766
17767         /**
17768          * Flag to determine if we should stop the propagation of the events
17769          * we generate. This is true by default but you may want to set it to
17770          * false if the html element contains other features that require the
17771          * mouse click.
17772          * @property stopPropagation
17773          * @type boolean
17774          * @static
17775          */
17776         stopPropagation: true,
17777
17778         /**
17779          * Internal flag that is set to true when drag and drop has been
17780          * intialized
17781          * @property initialized
17782          * @private
17783          * @static
17784          */
17785         initalized: false,
17786
17787         /**
17788          * All drag and drop can be disabled.
17789          * @property locked
17790          * @private
17791          * @static
17792          */
17793         locked: false,
17794
17795         /**
17796          * Called the first time an element is registered.
17797          * @method init
17798          * @private
17799          * @static
17800          */
17801         init: function() {
17802             this.initialized = true;
17803         },
17804
17805         /**
17806          * In point mode, drag and drop interaction is defined by the
17807          * location of the cursor during the drag/drop
17808          * @property POINT
17809          * @type int
17810          * @static
17811          */
17812         POINT: 0,
17813
17814         /**
17815          * In intersect mode, drag and drop interactio nis defined by the
17816          * overlap of two or more drag and drop objects.
17817          * @property INTERSECT
17818          * @type int
17819          * @static
17820          */
17821         INTERSECT: 1,
17822
17823         /**
17824          * The current drag and drop mode.  Default: POINT
17825          * @property mode
17826          * @type int
17827          * @static
17828          */
17829         mode: 0,
17830
17831         /**
17832          * Runs method on all drag and drop objects
17833          * @method _execOnAll
17834          * @private
17835          * @static
17836          */
17837         _execOnAll: function(sMethod, args) {
17838             for (var i in this.ids) {
17839                 for (var j in this.ids[i]) {
17840                     var oDD = this.ids[i][j];
17841                     if (! this.isTypeOfDD(oDD)) {
17842                         continue;
17843                     }
17844                     oDD[sMethod].apply(oDD, args);
17845                 }
17846             }
17847         },
17848
17849         /**
17850          * Drag and drop initialization.  Sets up the global event handlers
17851          * @method _onLoad
17852          * @private
17853          * @static
17854          */
17855         _onLoad: function() {
17856
17857             this.init();
17858
17859             if (!Roo.isTouch) {
17860                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17861                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17862             }
17863             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17864             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17865             
17866             Event.on(window,   "unload",    this._onUnload, this, true);
17867             Event.on(window,   "resize",    this._onResize, this, true);
17868             // Event.on(window,   "mouseout",    this._test);
17869
17870         },
17871
17872         /**
17873          * Reset constraints on all drag and drop objs
17874          * @method _onResize
17875          * @private
17876          * @static
17877          */
17878         _onResize: function(e) {
17879             this._execOnAll("resetConstraints", []);
17880         },
17881
17882         /**
17883          * Lock all drag and drop functionality
17884          * @method lock
17885          * @static
17886          */
17887         lock: function() { this.locked = true; },
17888
17889         /**
17890          * Unlock all drag and drop functionality
17891          * @method unlock
17892          * @static
17893          */
17894         unlock: function() { this.locked = false; },
17895
17896         /**
17897          * Is drag and drop locked?
17898          * @method isLocked
17899          * @return {boolean} True if drag and drop is locked, false otherwise.
17900          * @static
17901          */
17902         isLocked: function() { return this.locked; },
17903
17904         /**
17905          * Location cache that is set for all drag drop objects when a drag is
17906          * initiated, cleared when the drag is finished.
17907          * @property locationCache
17908          * @private
17909          * @static
17910          */
17911         locationCache: {},
17912
17913         /**
17914          * Set useCache to false if you want to force object the lookup of each
17915          * drag and drop linked element constantly during a drag.
17916          * @property useCache
17917          * @type boolean
17918          * @static
17919          */
17920         useCache: true,
17921
17922         /**
17923          * The number of pixels that the mouse needs to move after the
17924          * mousedown before the drag is initiated.  Default=3;
17925          * @property clickPixelThresh
17926          * @type int
17927          * @static
17928          */
17929         clickPixelThresh: 3,
17930
17931         /**
17932          * The number of milliseconds after the mousedown event to initiate the
17933          * drag if we don't get a mouseup event. Default=1000
17934          * @property clickTimeThresh
17935          * @type int
17936          * @static
17937          */
17938         clickTimeThresh: 350,
17939
17940         /**
17941          * Flag that indicates that either the drag pixel threshold or the
17942          * mousdown time threshold has been met
17943          * @property dragThreshMet
17944          * @type boolean
17945          * @private
17946          * @static
17947          */
17948         dragThreshMet: false,
17949
17950         /**
17951          * Timeout used for the click time threshold
17952          * @property clickTimeout
17953          * @type Object
17954          * @private
17955          * @static
17956          */
17957         clickTimeout: null,
17958
17959         /**
17960          * The X position of the mousedown event stored for later use when a
17961          * drag threshold is met.
17962          * @property startX
17963          * @type int
17964          * @private
17965          * @static
17966          */
17967         startX: 0,
17968
17969         /**
17970          * The Y position of the mousedown event stored for later use when a
17971          * drag threshold is met.
17972          * @property startY
17973          * @type int
17974          * @private
17975          * @static
17976          */
17977         startY: 0,
17978
17979         /**
17980          * Each DragDrop instance must be registered with the DragDropMgr.
17981          * This is executed in DragDrop.init()
17982          * @method regDragDrop
17983          * @param {DragDrop} oDD the DragDrop object to register
17984          * @param {String} sGroup the name of the group this element belongs to
17985          * @static
17986          */
17987         regDragDrop: function(oDD, sGroup) {
17988             if (!this.initialized) { this.init(); }
17989
17990             if (!this.ids[sGroup]) {
17991                 this.ids[sGroup] = {};
17992             }
17993             this.ids[sGroup][oDD.id] = oDD;
17994         },
17995
17996         /**
17997          * Removes the supplied dd instance from the supplied group. Executed
17998          * by DragDrop.removeFromGroup, so don't call this function directly.
17999          * @method removeDDFromGroup
18000          * @private
18001          * @static
18002          */
18003         removeDDFromGroup: function(oDD, sGroup) {
18004             if (!this.ids[sGroup]) {
18005                 this.ids[sGroup] = {};
18006             }
18007
18008             var obj = this.ids[sGroup];
18009             if (obj && obj[oDD.id]) {
18010                 delete obj[oDD.id];
18011             }
18012         },
18013
18014         /**
18015          * Unregisters a drag and drop item.  This is executed in
18016          * DragDrop.unreg, use that method instead of calling this directly.
18017          * @method _remove
18018          * @private
18019          * @static
18020          */
18021         _remove: function(oDD) {
18022             for (var g in oDD.groups) {
18023                 if (g && this.ids[g][oDD.id]) {
18024                     delete this.ids[g][oDD.id];
18025                 }
18026             }
18027             delete this.handleIds[oDD.id];
18028         },
18029
18030         /**
18031          * Each DragDrop handle element must be registered.  This is done
18032          * automatically when executing DragDrop.setHandleElId()
18033          * @method regHandle
18034          * @param {String} sDDId the DragDrop id this element is a handle for
18035          * @param {String} sHandleId the id of the element that is the drag
18036          * handle
18037          * @static
18038          */
18039         regHandle: function(sDDId, sHandleId) {
18040             if (!this.handleIds[sDDId]) {
18041                 this.handleIds[sDDId] = {};
18042             }
18043             this.handleIds[sDDId][sHandleId] = sHandleId;
18044         },
18045
18046         /**
18047          * Utility function to determine if a given element has been
18048          * registered as a drag drop item.
18049          * @method isDragDrop
18050          * @param {String} id the element id to check
18051          * @return {boolean} true if this element is a DragDrop item,
18052          * false otherwise
18053          * @static
18054          */
18055         isDragDrop: function(id) {
18056             return ( this.getDDById(id) ) ? true : false;
18057         },
18058
18059         /**
18060          * Returns the drag and drop instances that are in all groups the
18061          * passed in instance belongs to.
18062          * @method getRelated
18063          * @param {DragDrop} p_oDD the obj to get related data for
18064          * @param {boolean} bTargetsOnly if true, only return targetable objs
18065          * @return {DragDrop[]} the related instances
18066          * @static
18067          */
18068         getRelated: function(p_oDD, bTargetsOnly) {
18069             var oDDs = [];
18070             for (var i in p_oDD.groups) {
18071                 for (j in this.ids[i]) {
18072                     var dd = this.ids[i][j];
18073                     if (! this.isTypeOfDD(dd)) {
18074                         continue;
18075                     }
18076                     if (!bTargetsOnly || dd.isTarget) {
18077                         oDDs[oDDs.length] = dd;
18078                     }
18079                 }
18080             }
18081
18082             return oDDs;
18083         },
18084
18085         /**
18086          * Returns true if the specified dd target is a legal target for
18087          * the specifice drag obj
18088          * @method isLegalTarget
18089          * @param {DragDrop} the drag obj
18090          * @param {DragDrop} the target
18091          * @return {boolean} true if the target is a legal target for the
18092          * dd obj
18093          * @static
18094          */
18095         isLegalTarget: function (oDD, oTargetDD) {
18096             var targets = this.getRelated(oDD, true);
18097             for (var i=0, len=targets.length;i<len;++i) {
18098                 if (targets[i].id == oTargetDD.id) {
18099                     return true;
18100                 }
18101             }
18102
18103             return false;
18104         },
18105
18106         /**
18107          * My goal is to be able to transparently determine if an object is
18108          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
18109          * returns "object", oDD.constructor.toString() always returns
18110          * "DragDrop" and not the name of the subclass.  So for now it just
18111          * evaluates a well-known variable in DragDrop.
18112          * @method isTypeOfDD
18113          * @param {Object} the object to evaluate
18114          * @return {boolean} true if typeof oDD = DragDrop
18115          * @static
18116          */
18117         isTypeOfDD: function (oDD) {
18118             return (oDD && oDD.__ygDragDrop);
18119         },
18120
18121         /**
18122          * Utility function to determine if a given element has been
18123          * registered as a drag drop handle for the given Drag Drop object.
18124          * @method isHandle
18125          * @param {String} id the element id to check
18126          * @return {boolean} true if this element is a DragDrop handle, false
18127          * otherwise
18128          * @static
18129          */
18130         isHandle: function(sDDId, sHandleId) {
18131             return ( this.handleIds[sDDId] &&
18132                             this.handleIds[sDDId][sHandleId] );
18133         },
18134
18135         /**
18136          * Returns the DragDrop instance for a given id
18137          * @method getDDById
18138          * @param {String} id the id of the DragDrop object
18139          * @return {DragDrop} the drag drop object, null if it is not found
18140          * @static
18141          */
18142         getDDById: function(id) {
18143             for (var i in this.ids) {
18144                 if (this.ids[i][id]) {
18145                     return this.ids[i][id];
18146                 }
18147             }
18148             return null;
18149         },
18150
18151         /**
18152          * Fired after a registered DragDrop object gets the mousedown event.
18153          * Sets up the events required to track the object being dragged
18154          * @method handleMouseDown
18155          * @param {Event} e the event
18156          * @param oDD the DragDrop object being dragged
18157          * @private
18158          * @static
18159          */
18160         handleMouseDown: function(e, oDD) {
18161             if(Roo.QuickTips){
18162                 Roo.QuickTips.disable();
18163             }
18164             this.currentTarget = e.getTarget();
18165
18166             this.dragCurrent = oDD;
18167
18168             var el = oDD.getEl();
18169
18170             // track start position
18171             this.startX = e.getPageX();
18172             this.startY = e.getPageY();
18173
18174             this.deltaX = this.startX - el.offsetLeft;
18175             this.deltaY = this.startY - el.offsetTop;
18176
18177             this.dragThreshMet = false;
18178
18179             this.clickTimeout = setTimeout(
18180                     function() {
18181                         var DDM = Roo.dd.DDM;
18182                         DDM.startDrag(DDM.startX, DDM.startY);
18183                     },
18184                     this.clickTimeThresh );
18185         },
18186
18187         /**
18188          * Fired when either the drag pixel threshol or the mousedown hold
18189          * time threshold has been met.
18190          * @method startDrag
18191          * @param x {int} the X position of the original mousedown
18192          * @param y {int} the Y position of the original mousedown
18193          * @static
18194          */
18195         startDrag: function(x, y) {
18196             clearTimeout(this.clickTimeout);
18197             if (this.dragCurrent) {
18198                 this.dragCurrent.b4StartDrag(x, y);
18199                 this.dragCurrent.startDrag(x, y);
18200             }
18201             this.dragThreshMet = true;
18202         },
18203
18204         /**
18205          * Internal function to handle the mouseup event.  Will be invoked
18206          * from the context of the document.
18207          * @method handleMouseUp
18208          * @param {Event} e the event
18209          * @private
18210          * @static
18211          */
18212         handleMouseUp: function(e) {
18213
18214             if(Roo.QuickTips){
18215                 Roo.QuickTips.enable();
18216             }
18217             if (! this.dragCurrent) {
18218                 return;
18219             }
18220
18221             clearTimeout(this.clickTimeout);
18222
18223             if (this.dragThreshMet) {
18224                 this.fireEvents(e, true);
18225             } else {
18226             }
18227
18228             this.stopDrag(e);
18229
18230             this.stopEvent(e);
18231         },
18232
18233         /**
18234          * Utility to stop event propagation and event default, if these
18235          * features are turned on.
18236          * @method stopEvent
18237          * @param {Event} e the event as returned by this.getEvent()
18238          * @static
18239          */
18240         stopEvent: function(e){
18241             if(this.stopPropagation) {
18242                 e.stopPropagation();
18243             }
18244
18245             if (this.preventDefault) {
18246                 e.preventDefault();
18247             }
18248         },
18249
18250         /**
18251          * Internal function to clean up event handlers after the drag
18252          * operation is complete
18253          * @method stopDrag
18254          * @param {Event} e the event
18255          * @private
18256          * @static
18257          */
18258         stopDrag: function(e) {
18259             // Fire the drag end event for the item that was dragged
18260             if (this.dragCurrent) {
18261                 if (this.dragThreshMet) {
18262                     this.dragCurrent.b4EndDrag(e);
18263                     this.dragCurrent.endDrag(e);
18264                 }
18265
18266                 this.dragCurrent.onMouseUp(e);
18267             }
18268
18269             this.dragCurrent = null;
18270             this.dragOvers = {};
18271         },
18272
18273         /**
18274          * Internal function to handle the mousemove event.  Will be invoked
18275          * from the context of the html element.
18276          *
18277          * @TODO figure out what we can do about mouse events lost when the
18278          * user drags objects beyond the window boundary.  Currently we can
18279          * detect this in internet explorer by verifying that the mouse is
18280          * down during the mousemove event.  Firefox doesn't give us the
18281          * button state on the mousemove event.
18282          * @method handleMouseMove
18283          * @param {Event} e the event
18284          * @private
18285          * @static
18286          */
18287         handleMouseMove: function(e) {
18288             if (! this.dragCurrent) {
18289                 return true;
18290             }
18291
18292             // var button = e.which || e.button;
18293
18294             // check for IE mouseup outside of page boundary
18295             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18296                 this.stopEvent(e);
18297                 return this.handleMouseUp(e);
18298             }
18299
18300             if (!this.dragThreshMet) {
18301                 var diffX = Math.abs(this.startX - e.getPageX());
18302                 var diffY = Math.abs(this.startY - e.getPageY());
18303                 if (diffX > this.clickPixelThresh ||
18304                             diffY > this.clickPixelThresh) {
18305                     this.startDrag(this.startX, this.startY);
18306                 }
18307             }
18308
18309             if (this.dragThreshMet) {
18310                 this.dragCurrent.b4Drag(e);
18311                 this.dragCurrent.onDrag(e);
18312                 if(!this.dragCurrent.moveOnly){
18313                     this.fireEvents(e, false);
18314                 }
18315             }
18316
18317             this.stopEvent(e);
18318
18319             return true;
18320         },
18321
18322         /**
18323          * Iterates over all of the DragDrop elements to find ones we are
18324          * hovering over or dropping on
18325          * @method fireEvents
18326          * @param {Event} e the event
18327          * @param {boolean} isDrop is this a drop op or a mouseover op?
18328          * @private
18329          * @static
18330          */
18331         fireEvents: function(e, isDrop) {
18332             var dc = this.dragCurrent;
18333
18334             // If the user did the mouse up outside of the window, we could
18335             // get here even though we have ended the drag.
18336             if (!dc || dc.isLocked()) {
18337                 return;
18338             }
18339
18340             var pt = e.getPoint();
18341
18342             // cache the previous dragOver array
18343             var oldOvers = [];
18344
18345             var outEvts   = [];
18346             var overEvts  = [];
18347             var dropEvts  = [];
18348             var enterEvts = [];
18349
18350             // Check to see if the object(s) we were hovering over is no longer
18351             // being hovered over so we can fire the onDragOut event
18352             for (var i in this.dragOvers) {
18353
18354                 var ddo = this.dragOvers[i];
18355
18356                 if (! this.isTypeOfDD(ddo)) {
18357                     continue;
18358                 }
18359
18360                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18361                     outEvts.push( ddo );
18362                 }
18363
18364                 oldOvers[i] = true;
18365                 delete this.dragOvers[i];
18366             }
18367
18368             for (var sGroup in dc.groups) {
18369
18370                 if ("string" != typeof sGroup) {
18371                     continue;
18372                 }
18373
18374                 for (i in this.ids[sGroup]) {
18375                     var oDD = this.ids[sGroup][i];
18376                     if (! this.isTypeOfDD(oDD)) {
18377                         continue;
18378                     }
18379
18380                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18381                         if (this.isOverTarget(pt, oDD, this.mode)) {
18382                             // look for drop interactions
18383                             if (isDrop) {
18384                                 dropEvts.push( oDD );
18385                             // look for drag enter and drag over interactions
18386                             } else {
18387
18388                                 // initial drag over: dragEnter fires
18389                                 if (!oldOvers[oDD.id]) {
18390                                     enterEvts.push( oDD );
18391                                 // subsequent drag overs: dragOver fires
18392                                 } else {
18393                                     overEvts.push( oDD );
18394                                 }
18395
18396                                 this.dragOvers[oDD.id] = oDD;
18397                             }
18398                         }
18399                     }
18400                 }
18401             }
18402
18403             if (this.mode) {
18404                 if (outEvts.length) {
18405                     dc.b4DragOut(e, outEvts);
18406                     dc.onDragOut(e, outEvts);
18407                 }
18408
18409                 if (enterEvts.length) {
18410                     dc.onDragEnter(e, enterEvts);
18411                 }
18412
18413                 if (overEvts.length) {
18414                     dc.b4DragOver(e, overEvts);
18415                     dc.onDragOver(e, overEvts);
18416                 }
18417
18418                 if (dropEvts.length) {
18419                     dc.b4DragDrop(e, dropEvts);
18420                     dc.onDragDrop(e, dropEvts);
18421                 }
18422
18423             } else {
18424                 // fire dragout events
18425                 var len = 0;
18426                 for (i=0, len=outEvts.length; i<len; ++i) {
18427                     dc.b4DragOut(e, outEvts[i].id);
18428                     dc.onDragOut(e, outEvts[i].id);
18429                 }
18430
18431                 // fire enter events
18432                 for (i=0,len=enterEvts.length; i<len; ++i) {
18433                     // dc.b4DragEnter(e, oDD.id);
18434                     dc.onDragEnter(e, enterEvts[i].id);
18435                 }
18436
18437                 // fire over events
18438                 for (i=0,len=overEvts.length; i<len; ++i) {
18439                     dc.b4DragOver(e, overEvts[i].id);
18440                     dc.onDragOver(e, overEvts[i].id);
18441                 }
18442
18443                 // fire drop events
18444                 for (i=0, len=dropEvts.length; i<len; ++i) {
18445                     dc.b4DragDrop(e, dropEvts[i].id);
18446                     dc.onDragDrop(e, dropEvts[i].id);
18447                 }
18448
18449             }
18450
18451             // notify about a drop that did not find a target
18452             if (isDrop && !dropEvts.length) {
18453                 dc.onInvalidDrop(e);
18454             }
18455
18456         },
18457
18458         /**
18459          * Helper function for getting the best match from the list of drag
18460          * and drop objects returned by the drag and drop events when we are
18461          * in INTERSECT mode.  It returns either the first object that the
18462          * cursor is over, or the object that has the greatest overlap with
18463          * the dragged element.
18464          * @method getBestMatch
18465          * @param  {DragDrop[]} dds The array of drag and drop objects
18466          * targeted
18467          * @return {DragDrop}       The best single match
18468          * @static
18469          */
18470         getBestMatch: function(dds) {
18471             var winner = null;
18472             // Return null if the input is not what we expect
18473             //if (!dds || !dds.length || dds.length == 0) {
18474                // winner = null;
18475             // If there is only one item, it wins
18476             //} else if (dds.length == 1) {
18477
18478             var len = dds.length;
18479
18480             if (len == 1) {
18481                 winner = dds[0];
18482             } else {
18483                 // Loop through the targeted items
18484                 for (var i=0; i<len; ++i) {
18485                     var dd = dds[i];
18486                     // If the cursor is over the object, it wins.  If the
18487                     // cursor is over multiple matches, the first one we come
18488                     // to wins.
18489                     if (dd.cursorIsOver) {
18490                         winner = dd;
18491                         break;
18492                     // Otherwise the object with the most overlap wins
18493                     } else {
18494                         if (!winner ||
18495                             winner.overlap.getArea() < dd.overlap.getArea()) {
18496                             winner = dd;
18497                         }
18498                     }
18499                 }
18500             }
18501
18502             return winner;
18503         },
18504
18505         /**
18506          * Refreshes the cache of the top-left and bottom-right points of the
18507          * drag and drop objects in the specified group(s).  This is in the
18508          * format that is stored in the drag and drop instance, so typical
18509          * usage is:
18510          * <code>
18511          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18512          * </code>
18513          * Alternatively:
18514          * <code>
18515          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18516          * </code>
18517          * @TODO this really should be an indexed array.  Alternatively this
18518          * method could accept both.
18519          * @method refreshCache
18520          * @param {Object} groups an associative array of groups to refresh
18521          * @static
18522          */
18523         refreshCache: function(groups) {
18524             for (var sGroup in groups) {
18525                 if ("string" != typeof sGroup) {
18526                     continue;
18527                 }
18528                 for (var i in this.ids[sGroup]) {
18529                     var oDD = this.ids[sGroup][i];
18530
18531                     if (this.isTypeOfDD(oDD)) {
18532                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18533                         var loc = this.getLocation(oDD);
18534                         if (loc) {
18535                             this.locationCache[oDD.id] = loc;
18536                         } else {
18537                             delete this.locationCache[oDD.id];
18538                             // this will unregister the drag and drop object if
18539                             // the element is not in a usable state
18540                             // oDD.unreg();
18541                         }
18542                     }
18543                 }
18544             }
18545         },
18546
18547         /**
18548          * This checks to make sure an element exists and is in the DOM.  The
18549          * main purpose is to handle cases where innerHTML is used to remove
18550          * drag and drop objects from the DOM.  IE provides an 'unspecified
18551          * error' when trying to access the offsetParent of such an element
18552          * @method verifyEl
18553          * @param {HTMLElement} el the element to check
18554          * @return {boolean} true if the element looks usable
18555          * @static
18556          */
18557         verifyEl: function(el) {
18558             if (el) {
18559                 var parent;
18560                 if(Roo.isIE){
18561                     try{
18562                         parent = el.offsetParent;
18563                     }catch(e){}
18564                 }else{
18565                     parent = el.offsetParent;
18566                 }
18567                 if (parent) {
18568                     return true;
18569                 }
18570             }
18571
18572             return false;
18573         },
18574
18575         /**
18576          * Returns a Region object containing the drag and drop element's position
18577          * and size, including the padding configured for it
18578          * @method getLocation
18579          * @param {DragDrop} oDD the drag and drop object to get the
18580          *                       location for
18581          * @return {Roo.lib.Region} a Region object representing the total area
18582          *                             the element occupies, including any padding
18583          *                             the instance is configured for.
18584          * @static
18585          */
18586         getLocation: function(oDD) {
18587             if (! this.isTypeOfDD(oDD)) {
18588                 return null;
18589             }
18590
18591             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18592
18593             try {
18594                 pos= Roo.lib.Dom.getXY(el);
18595             } catch (e) { }
18596
18597             if (!pos) {
18598                 return null;
18599             }
18600
18601             x1 = pos[0];
18602             x2 = x1 + el.offsetWidth;
18603             y1 = pos[1];
18604             y2 = y1 + el.offsetHeight;
18605
18606             t = y1 - oDD.padding[0];
18607             r = x2 + oDD.padding[1];
18608             b = y2 + oDD.padding[2];
18609             l = x1 - oDD.padding[3];
18610
18611             return new Roo.lib.Region( t, r, b, l );
18612         },
18613
18614         /**
18615          * Checks the cursor location to see if it over the target
18616          * @method isOverTarget
18617          * @param {Roo.lib.Point} pt The point to evaluate
18618          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18619          * @return {boolean} true if the mouse is over the target
18620          * @private
18621          * @static
18622          */
18623         isOverTarget: function(pt, oTarget, intersect) {
18624             // use cache if available
18625             var loc = this.locationCache[oTarget.id];
18626             if (!loc || !this.useCache) {
18627                 loc = this.getLocation(oTarget);
18628                 this.locationCache[oTarget.id] = loc;
18629
18630             }
18631
18632             if (!loc) {
18633                 return false;
18634             }
18635
18636             oTarget.cursorIsOver = loc.contains( pt );
18637
18638             // DragDrop is using this as a sanity check for the initial mousedown
18639             // in this case we are done.  In POINT mode, if the drag obj has no
18640             // contraints, we are also done. Otherwise we need to evaluate the
18641             // location of the target as related to the actual location of the
18642             // dragged element.
18643             var dc = this.dragCurrent;
18644             if (!dc || !dc.getTargetCoord ||
18645                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18646                 return oTarget.cursorIsOver;
18647             }
18648
18649             oTarget.overlap = null;
18650
18651             // Get the current location of the drag element, this is the
18652             // location of the mouse event less the delta that represents
18653             // where the original mousedown happened on the element.  We
18654             // need to consider constraints and ticks as well.
18655             var pos = dc.getTargetCoord(pt.x, pt.y);
18656
18657             var el = dc.getDragEl();
18658             var curRegion = new Roo.lib.Region( pos.y,
18659                                                    pos.x + el.offsetWidth,
18660                                                    pos.y + el.offsetHeight,
18661                                                    pos.x );
18662
18663             var overlap = curRegion.intersect(loc);
18664
18665             if (overlap) {
18666                 oTarget.overlap = overlap;
18667                 return (intersect) ? true : oTarget.cursorIsOver;
18668             } else {
18669                 return false;
18670             }
18671         },
18672
18673         /**
18674          * unload event handler
18675          * @method _onUnload
18676          * @private
18677          * @static
18678          */
18679         _onUnload: function(e, me) {
18680             Roo.dd.DragDropMgr.unregAll();
18681         },
18682
18683         /**
18684          * Cleans up the drag and drop events and objects.
18685          * @method unregAll
18686          * @private
18687          * @static
18688          */
18689         unregAll: function() {
18690
18691             if (this.dragCurrent) {
18692                 this.stopDrag();
18693                 this.dragCurrent = null;
18694             }
18695
18696             this._execOnAll("unreg", []);
18697
18698             for (i in this.elementCache) {
18699                 delete this.elementCache[i];
18700             }
18701
18702             this.elementCache = {};
18703             this.ids = {};
18704         },
18705
18706         /**
18707          * A cache of DOM elements
18708          * @property elementCache
18709          * @private
18710          * @static
18711          */
18712         elementCache: {},
18713
18714         /**
18715          * Get the wrapper for the DOM element specified
18716          * @method getElWrapper
18717          * @param {String} id the id of the element to get
18718          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18719          * @private
18720          * @deprecated This wrapper isn't that useful
18721          * @static
18722          */
18723         getElWrapper: function(id) {
18724             var oWrapper = this.elementCache[id];
18725             if (!oWrapper || !oWrapper.el) {
18726                 oWrapper = this.elementCache[id] =
18727                     new this.ElementWrapper(Roo.getDom(id));
18728             }
18729             return oWrapper;
18730         },
18731
18732         /**
18733          * Returns the actual DOM element
18734          * @method getElement
18735          * @param {String} id the id of the elment to get
18736          * @return {Object} The element
18737          * @deprecated use Roo.getDom instead
18738          * @static
18739          */
18740         getElement: function(id) {
18741             return Roo.getDom(id);
18742         },
18743
18744         /**
18745          * Returns the style property for the DOM element (i.e.,
18746          * document.getElById(id).style)
18747          * @method getCss
18748          * @param {String} id the id of the elment to get
18749          * @return {Object} The style property of the element
18750          * @deprecated use Roo.getDom instead
18751          * @static
18752          */
18753         getCss: function(id) {
18754             var el = Roo.getDom(id);
18755             return (el) ? el.style : null;
18756         },
18757
18758         /**
18759          * Inner class for cached elements
18760          * @class DragDropMgr.ElementWrapper
18761          * @for DragDropMgr
18762          * @private
18763          * @deprecated
18764          */
18765         ElementWrapper: function(el) {
18766                 /**
18767                  * The element
18768                  * @property el
18769                  */
18770                 this.el = el || null;
18771                 /**
18772                  * The element id
18773                  * @property id
18774                  */
18775                 this.id = this.el && el.id;
18776                 /**
18777                  * A reference to the style property
18778                  * @property css
18779                  */
18780                 this.css = this.el && el.style;
18781             },
18782
18783         /**
18784          * Returns the X position of an html element
18785          * @method getPosX
18786          * @param el the element for which to get the position
18787          * @return {int} the X coordinate
18788          * @for DragDropMgr
18789          * @deprecated use Roo.lib.Dom.getX instead
18790          * @static
18791          */
18792         getPosX: function(el) {
18793             return Roo.lib.Dom.getX(el);
18794         },
18795
18796         /**
18797          * Returns the Y position of an html element
18798          * @method getPosY
18799          * @param el the element for which to get the position
18800          * @return {int} the Y coordinate
18801          * @deprecated use Roo.lib.Dom.getY instead
18802          * @static
18803          */
18804         getPosY: function(el) {
18805             return Roo.lib.Dom.getY(el);
18806         },
18807
18808         /**
18809          * Swap two nodes.  In IE, we use the native method, for others we
18810          * emulate the IE behavior
18811          * @method swapNode
18812          * @param n1 the first node to swap
18813          * @param n2 the other node to swap
18814          * @static
18815          */
18816         swapNode: function(n1, n2) {
18817             if (n1.swapNode) {
18818                 n1.swapNode(n2);
18819             } else {
18820                 var p = n2.parentNode;
18821                 var s = n2.nextSibling;
18822
18823                 if (s == n1) {
18824                     p.insertBefore(n1, n2);
18825                 } else if (n2 == n1.nextSibling) {
18826                     p.insertBefore(n2, n1);
18827                 } else {
18828                     n1.parentNode.replaceChild(n2, n1);
18829                     p.insertBefore(n1, s);
18830                 }
18831             }
18832         },
18833
18834         /**
18835          * Returns the current scroll position
18836          * @method getScroll
18837          * @private
18838          * @static
18839          */
18840         getScroll: function () {
18841             var t, l, dde=document.documentElement, db=document.body;
18842             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18843                 t = dde.scrollTop;
18844                 l = dde.scrollLeft;
18845             } else if (db) {
18846                 t = db.scrollTop;
18847                 l = db.scrollLeft;
18848             } else {
18849
18850             }
18851             return { top: t, left: l };
18852         },
18853
18854         /**
18855          * Returns the specified element style property
18856          * @method getStyle
18857          * @param {HTMLElement} el          the element
18858          * @param {string}      styleProp   the style property
18859          * @return {string} The value of the style property
18860          * @deprecated use Roo.lib.Dom.getStyle
18861          * @static
18862          */
18863         getStyle: function(el, styleProp) {
18864             return Roo.fly(el).getStyle(styleProp);
18865         },
18866
18867         /**
18868          * Gets the scrollTop
18869          * @method getScrollTop
18870          * @return {int} the document's scrollTop
18871          * @static
18872          */
18873         getScrollTop: function () { return this.getScroll().top; },
18874
18875         /**
18876          * Gets the scrollLeft
18877          * @method getScrollLeft
18878          * @return {int} the document's scrollTop
18879          * @static
18880          */
18881         getScrollLeft: function () { return this.getScroll().left; },
18882
18883         /**
18884          * Sets the x/y position of an element to the location of the
18885          * target element.
18886          * @method moveToEl
18887          * @param {HTMLElement} moveEl      The element to move
18888          * @param {HTMLElement} targetEl    The position reference element
18889          * @static
18890          */
18891         moveToEl: function (moveEl, targetEl) {
18892             var aCoord = Roo.lib.Dom.getXY(targetEl);
18893             Roo.lib.Dom.setXY(moveEl, aCoord);
18894         },
18895
18896         /**
18897          * Numeric array sort function
18898          * @method numericSort
18899          * @static
18900          */
18901         numericSort: function(a, b) { return (a - b); },
18902
18903         /**
18904          * Internal counter
18905          * @property _timeoutCount
18906          * @private
18907          * @static
18908          */
18909         _timeoutCount: 0,
18910
18911         /**
18912          * Trying to make the load order less important.  Without this we get
18913          * an error if this file is loaded before the Event Utility.
18914          * @method _addListeners
18915          * @private
18916          * @static
18917          */
18918         _addListeners: function() {
18919             var DDM = Roo.dd.DDM;
18920             if ( Roo.lib.Event && document ) {
18921                 DDM._onLoad();
18922             } else {
18923                 if (DDM._timeoutCount > 2000) {
18924                 } else {
18925                     setTimeout(DDM._addListeners, 10);
18926                     if (document && document.body) {
18927                         DDM._timeoutCount += 1;
18928                     }
18929                 }
18930             }
18931         },
18932
18933         /**
18934          * Recursively searches the immediate parent and all child nodes for
18935          * the handle element in order to determine wheter or not it was
18936          * clicked.
18937          * @method handleWasClicked
18938          * @param node the html element to inspect
18939          * @static
18940          */
18941         handleWasClicked: function(node, id) {
18942             if (this.isHandle(id, node.id)) {
18943                 return true;
18944             } else {
18945                 // check to see if this is a text node child of the one we want
18946                 var p = node.parentNode;
18947
18948                 while (p) {
18949                     if (this.isHandle(id, p.id)) {
18950                         return true;
18951                     } else {
18952                         p = p.parentNode;
18953                     }
18954                 }
18955             }
18956
18957             return false;
18958         }
18959
18960     };
18961
18962 }();
18963
18964 // shorter alias, save a few bytes
18965 Roo.dd.DDM = Roo.dd.DragDropMgr;
18966 Roo.dd.DDM._addListeners();
18967
18968 }/*
18969  * Based on:
18970  * Ext JS Library 1.1.1
18971  * Copyright(c) 2006-2007, Ext JS, LLC.
18972  *
18973  * Originally Released Under LGPL - original licence link has changed is not relivant.
18974  *
18975  * Fork - LGPL
18976  * <script type="text/javascript">
18977  */
18978
18979 /**
18980  * @class Roo.dd.DD
18981  * A DragDrop implementation where the linked element follows the
18982  * mouse cursor during a drag.
18983  * @extends Roo.dd.DragDrop
18984  * @constructor
18985  * @param {String} id the id of the linked element
18986  * @param {String} sGroup the group of related DragDrop items
18987  * @param {object} config an object containing configurable attributes
18988  *                Valid properties for DD:
18989  *                    scroll
18990  */
18991 Roo.dd.DD = function(id, sGroup, config) {
18992     if (id) {
18993         this.init(id, sGroup, config);
18994     }
18995 };
18996
18997 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18998
18999     /**
19000      * When set to true, the utility automatically tries to scroll the browser
19001      * window wehn a drag and drop element is dragged near the viewport boundary.
19002      * Defaults to true.
19003      * @property scroll
19004      * @type boolean
19005      */
19006     scroll: true,
19007
19008     /**
19009      * Sets the pointer offset to the distance between the linked element's top
19010      * left corner and the location the element was clicked
19011      * @method autoOffset
19012      * @param {int} iPageX the X coordinate of the click
19013      * @param {int} iPageY the Y coordinate of the click
19014      */
19015     autoOffset: function(iPageX, iPageY) {
19016         var x = iPageX - this.startPageX;
19017         var y = iPageY - this.startPageY;
19018         this.setDelta(x, y);
19019     },
19020
19021     /**
19022      * Sets the pointer offset.  You can call this directly to force the
19023      * offset to be in a particular location (e.g., pass in 0,0 to set it
19024      * to the center of the object)
19025      * @method setDelta
19026      * @param {int} iDeltaX the distance from the left
19027      * @param {int} iDeltaY the distance from the top
19028      */
19029     setDelta: function(iDeltaX, iDeltaY) {
19030         this.deltaX = iDeltaX;
19031         this.deltaY = iDeltaY;
19032     },
19033
19034     /**
19035      * Sets the drag element to the location of the mousedown or click event,
19036      * maintaining the cursor location relative to the location on the element
19037      * that was clicked.  Override this if you want to place the element in a
19038      * location other than where the cursor is.
19039      * @method setDragElPos
19040      * @param {int} iPageX the X coordinate of the mousedown or drag event
19041      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19042      */
19043     setDragElPos: function(iPageX, iPageY) {
19044         // the first time we do this, we are going to check to make sure
19045         // the element has css positioning
19046
19047         var el = this.getDragEl();
19048         this.alignElWithMouse(el, iPageX, iPageY);
19049     },
19050
19051     /**
19052      * Sets the element to the location of the mousedown or click event,
19053      * maintaining the cursor location relative to the location on the element
19054      * that was clicked.  Override this if you want to place the element in a
19055      * location other than where the cursor is.
19056      * @method alignElWithMouse
19057      * @param {HTMLElement} el the element to move
19058      * @param {int} iPageX the X coordinate of the mousedown or drag event
19059      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19060      */
19061     alignElWithMouse: function(el, iPageX, iPageY) {
19062         var oCoord = this.getTargetCoord(iPageX, iPageY);
19063         var fly = el.dom ? el : Roo.fly(el);
19064         if (!this.deltaSetXY) {
19065             var aCoord = [oCoord.x, oCoord.y];
19066             fly.setXY(aCoord);
19067             var newLeft = fly.getLeft(true);
19068             var newTop  = fly.getTop(true);
19069             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
19070         } else {
19071             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
19072         }
19073
19074         this.cachePosition(oCoord.x, oCoord.y);
19075         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
19076         return oCoord;
19077     },
19078
19079     /**
19080      * Saves the most recent position so that we can reset the constraints and
19081      * tick marks on-demand.  We need to know this so that we can calculate the
19082      * number of pixels the element is offset from its original position.
19083      * @method cachePosition
19084      * @param iPageX the current x position (optional, this just makes it so we
19085      * don't have to look it up again)
19086      * @param iPageY the current y position (optional, this just makes it so we
19087      * don't have to look it up again)
19088      */
19089     cachePosition: function(iPageX, iPageY) {
19090         if (iPageX) {
19091             this.lastPageX = iPageX;
19092             this.lastPageY = iPageY;
19093         } else {
19094             var aCoord = Roo.lib.Dom.getXY(this.getEl());
19095             this.lastPageX = aCoord[0];
19096             this.lastPageY = aCoord[1];
19097         }
19098     },
19099
19100     /**
19101      * Auto-scroll the window if the dragged object has been moved beyond the
19102      * visible window boundary.
19103      * @method autoScroll
19104      * @param {int} x the drag element's x position
19105      * @param {int} y the drag element's y position
19106      * @param {int} h the height of the drag element
19107      * @param {int} w the width of the drag element
19108      * @private
19109      */
19110     autoScroll: function(x, y, h, w) {
19111
19112         if (this.scroll) {
19113             // The client height
19114             var clientH = Roo.lib.Dom.getViewWidth();
19115
19116             // The client width
19117             var clientW = Roo.lib.Dom.getViewHeight();
19118
19119             // The amt scrolled down
19120             var st = this.DDM.getScrollTop();
19121
19122             // The amt scrolled right
19123             var sl = this.DDM.getScrollLeft();
19124
19125             // Location of the bottom of the element
19126             var bot = h + y;
19127
19128             // Location of the right of the element
19129             var right = w + x;
19130
19131             // The distance from the cursor to the bottom of the visible area,
19132             // adjusted so that we don't scroll if the cursor is beyond the
19133             // element drag constraints
19134             var toBot = (clientH + st - y - this.deltaY);
19135
19136             // The distance from the cursor to the right of the visible area
19137             var toRight = (clientW + sl - x - this.deltaX);
19138
19139
19140             // How close to the edge the cursor must be before we scroll
19141             // var thresh = (document.all) ? 100 : 40;
19142             var thresh = 40;
19143
19144             // How many pixels to scroll per autoscroll op.  This helps to reduce
19145             // clunky scrolling. IE is more sensitive about this ... it needs this
19146             // value to be higher.
19147             var scrAmt = (document.all) ? 80 : 30;
19148
19149             // Scroll down if we are near the bottom of the visible page and the
19150             // obj extends below the crease
19151             if ( bot > clientH && toBot < thresh ) {
19152                 window.scrollTo(sl, st + scrAmt);
19153             }
19154
19155             // Scroll up if the window is scrolled down and the top of the object
19156             // goes above the top border
19157             if ( y < st && st > 0 && y - st < thresh ) {
19158                 window.scrollTo(sl, st - scrAmt);
19159             }
19160
19161             // Scroll right if the obj is beyond the right border and the cursor is
19162             // near the border.
19163             if ( right > clientW && toRight < thresh ) {
19164                 window.scrollTo(sl + scrAmt, st);
19165             }
19166
19167             // Scroll left if the window has been scrolled to the right and the obj
19168             // extends past the left border
19169             if ( x < sl && sl > 0 && x - sl < thresh ) {
19170                 window.scrollTo(sl - scrAmt, st);
19171             }
19172         }
19173     },
19174
19175     /**
19176      * Finds the location the element should be placed if we want to move
19177      * it to where the mouse location less the click offset would place us.
19178      * @method getTargetCoord
19179      * @param {int} iPageX the X coordinate of the click
19180      * @param {int} iPageY the Y coordinate of the click
19181      * @return an object that contains the coordinates (Object.x and Object.y)
19182      * @private
19183      */
19184     getTargetCoord: function(iPageX, iPageY) {
19185
19186
19187         var x = iPageX - this.deltaX;
19188         var y = iPageY - this.deltaY;
19189
19190         if (this.constrainX) {
19191             if (x < this.minX) { x = this.minX; }
19192             if (x > this.maxX) { x = this.maxX; }
19193         }
19194
19195         if (this.constrainY) {
19196             if (y < this.minY) { y = this.minY; }
19197             if (y > this.maxY) { y = this.maxY; }
19198         }
19199
19200         x = this.getTick(x, this.xTicks);
19201         y = this.getTick(y, this.yTicks);
19202
19203
19204         return {x:x, y:y};
19205     },
19206
19207     /*
19208      * Sets up config options specific to this class. Overrides
19209      * Roo.dd.DragDrop, but all versions of this method through the
19210      * inheritance chain are called
19211      */
19212     applyConfig: function() {
19213         Roo.dd.DD.superclass.applyConfig.call(this);
19214         this.scroll = (this.config.scroll !== false);
19215     },
19216
19217     /*
19218      * Event that fires prior to the onMouseDown event.  Overrides
19219      * Roo.dd.DragDrop.
19220      */
19221     b4MouseDown: function(e) {
19222         // this.resetConstraints();
19223         this.autoOffset(e.getPageX(),
19224                             e.getPageY());
19225     },
19226
19227     /*
19228      * Event that fires prior to the onDrag event.  Overrides
19229      * Roo.dd.DragDrop.
19230      */
19231     b4Drag: function(e) {
19232         this.setDragElPos(e.getPageX(),
19233                             e.getPageY());
19234     },
19235
19236     toString: function() {
19237         return ("DD " + this.id);
19238     }
19239
19240     //////////////////////////////////////////////////////////////////////////
19241     // Debugging ygDragDrop events that can be overridden
19242     //////////////////////////////////////////////////////////////////////////
19243     /*
19244     startDrag: function(x, y) {
19245     },
19246
19247     onDrag: function(e) {
19248     },
19249
19250     onDragEnter: function(e, id) {
19251     },
19252
19253     onDragOver: function(e, id) {
19254     },
19255
19256     onDragOut: function(e, id) {
19257     },
19258
19259     onDragDrop: function(e, id) {
19260     },
19261
19262     endDrag: function(e) {
19263     }
19264
19265     */
19266
19267 });/*
19268  * Based on:
19269  * Ext JS Library 1.1.1
19270  * Copyright(c) 2006-2007, Ext JS, LLC.
19271  *
19272  * Originally Released Under LGPL - original licence link has changed is not relivant.
19273  *
19274  * Fork - LGPL
19275  * <script type="text/javascript">
19276  */
19277
19278 /**
19279  * @class Roo.dd.DDProxy
19280  * A DragDrop implementation that inserts an empty, bordered div into
19281  * the document that follows the cursor during drag operations.  At the time of
19282  * the click, the frame div is resized to the dimensions of the linked html
19283  * element, and moved to the exact location of the linked element.
19284  *
19285  * References to the "frame" element refer to the single proxy element that
19286  * was created to be dragged in place of all DDProxy elements on the
19287  * page.
19288  *
19289  * @extends Roo.dd.DD
19290  * @constructor
19291  * @param {String} id the id of the linked html element
19292  * @param {String} sGroup the group of related DragDrop objects
19293  * @param {object} config an object containing configurable attributes
19294  *                Valid properties for DDProxy in addition to those in DragDrop:
19295  *                   resizeFrame, centerFrame, dragElId
19296  */
19297 Roo.dd.DDProxy = function(id, sGroup, config) {
19298     if (id) {
19299         this.init(id, sGroup, config);
19300         this.initFrame();
19301     }
19302 };
19303
19304 /**
19305  * The default drag frame div id
19306  * @property Roo.dd.DDProxy.dragElId
19307  * @type String
19308  * @static
19309  */
19310 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19311
19312 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19313
19314     /**
19315      * By default we resize the drag frame to be the same size as the element
19316      * we want to drag (this is to get the frame effect).  We can turn it off
19317      * if we want a different behavior.
19318      * @property resizeFrame
19319      * @type boolean
19320      */
19321     resizeFrame: true,
19322
19323     /**
19324      * By default the frame is positioned exactly where the drag element is, so
19325      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19326      * you do not have constraints on the obj is to have the drag frame centered
19327      * around the cursor.  Set centerFrame to true for this effect.
19328      * @property centerFrame
19329      * @type boolean
19330      */
19331     centerFrame: false,
19332
19333     /**
19334      * Creates the proxy element if it does not yet exist
19335      * @method createFrame
19336      */
19337     createFrame: function() {
19338         var self = this;
19339         var body = document.body;
19340
19341         if (!body || !body.firstChild) {
19342             setTimeout( function() { self.createFrame(); }, 50 );
19343             return;
19344         }
19345
19346         var div = this.getDragEl();
19347
19348         if (!div) {
19349             div    = document.createElement("div");
19350             div.id = this.dragElId;
19351             var s  = div.style;
19352
19353             s.position   = "absolute";
19354             s.visibility = "hidden";
19355             s.cursor     = "move";
19356             s.border     = "2px solid #aaa";
19357             s.zIndex     = 999;
19358
19359             // appendChild can blow up IE if invoked prior to the window load event
19360             // while rendering a table.  It is possible there are other scenarios
19361             // that would cause this to happen as well.
19362             body.insertBefore(div, body.firstChild);
19363         }
19364     },
19365
19366     /**
19367      * Initialization for the drag frame element.  Must be called in the
19368      * constructor of all subclasses
19369      * @method initFrame
19370      */
19371     initFrame: function() {
19372         this.createFrame();
19373     },
19374
19375     applyConfig: function() {
19376         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19377
19378         this.resizeFrame = (this.config.resizeFrame !== false);
19379         this.centerFrame = (this.config.centerFrame);
19380         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19381     },
19382
19383     /**
19384      * Resizes the drag frame to the dimensions of the clicked object, positions
19385      * it over the object, and finally displays it
19386      * @method showFrame
19387      * @param {int} iPageX X click position
19388      * @param {int} iPageY Y click position
19389      * @private
19390      */
19391     showFrame: function(iPageX, iPageY) {
19392         var el = this.getEl();
19393         var dragEl = this.getDragEl();
19394         var s = dragEl.style;
19395
19396         this._resizeProxy();
19397
19398         if (this.centerFrame) {
19399             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19400                            Math.round(parseInt(s.height, 10)/2) );
19401         }
19402
19403         this.setDragElPos(iPageX, iPageY);
19404
19405         Roo.fly(dragEl).show();
19406     },
19407
19408     /**
19409      * The proxy is automatically resized to the dimensions of the linked
19410      * element when a drag is initiated, unless resizeFrame is set to false
19411      * @method _resizeProxy
19412      * @private
19413      */
19414     _resizeProxy: function() {
19415         if (this.resizeFrame) {
19416             var el = this.getEl();
19417             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19418         }
19419     },
19420
19421     // overrides Roo.dd.DragDrop
19422     b4MouseDown: function(e) {
19423         var x = e.getPageX();
19424         var y = e.getPageY();
19425         this.autoOffset(x, y);
19426         this.setDragElPos(x, y);
19427     },
19428
19429     // overrides Roo.dd.DragDrop
19430     b4StartDrag: function(x, y) {
19431         // show the drag frame
19432         this.showFrame(x, y);
19433     },
19434
19435     // overrides Roo.dd.DragDrop
19436     b4EndDrag: function(e) {
19437         Roo.fly(this.getDragEl()).hide();
19438     },
19439
19440     // overrides Roo.dd.DragDrop
19441     // By default we try to move the element to the last location of the frame.
19442     // This is so that the default behavior mirrors that of Roo.dd.DD.
19443     endDrag: function(e) {
19444
19445         var lel = this.getEl();
19446         var del = this.getDragEl();
19447
19448         // Show the drag frame briefly so we can get its position
19449         del.style.visibility = "";
19450
19451         this.beforeMove();
19452         // Hide the linked element before the move to get around a Safari
19453         // rendering bug.
19454         lel.style.visibility = "hidden";
19455         Roo.dd.DDM.moveToEl(lel, del);
19456         del.style.visibility = "hidden";
19457         lel.style.visibility = "";
19458
19459         this.afterDrag();
19460     },
19461
19462     beforeMove : function(){
19463
19464     },
19465
19466     afterDrag : function(){
19467
19468     },
19469
19470     toString: function() {
19471         return ("DDProxy " + this.id);
19472     }
19473
19474 });
19475 /*
19476  * Based on:
19477  * Ext JS Library 1.1.1
19478  * Copyright(c) 2006-2007, Ext JS, LLC.
19479  *
19480  * Originally Released Under LGPL - original licence link has changed is not relivant.
19481  *
19482  * Fork - LGPL
19483  * <script type="text/javascript">
19484  */
19485
19486  /**
19487  * @class Roo.dd.DDTarget
19488  * A DragDrop implementation that does not move, but can be a drop
19489  * target.  You would get the same result by simply omitting implementation
19490  * for the event callbacks, but this way we reduce the processing cost of the
19491  * event listener and the callbacks.
19492  * @extends Roo.dd.DragDrop
19493  * @constructor
19494  * @param {String} id the id of the element that is a drop target
19495  * @param {String} sGroup the group of related DragDrop objects
19496  * @param {object} config an object containing configurable attributes
19497  *                 Valid properties for DDTarget in addition to those in
19498  *                 DragDrop:
19499  *                    none
19500  */
19501 Roo.dd.DDTarget = function(id, sGroup, config) {
19502     if (id) {
19503         this.initTarget(id, sGroup, config);
19504     }
19505     if (config.listeners || config.events) { 
19506        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19507             listeners : config.listeners || {}, 
19508             events : config.events || {} 
19509         });    
19510     }
19511 };
19512
19513 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19514 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19515     toString: function() {
19516         return ("DDTarget " + this.id);
19517     }
19518 });
19519 /*
19520  * Based on:
19521  * Ext JS Library 1.1.1
19522  * Copyright(c) 2006-2007, Ext JS, LLC.
19523  *
19524  * Originally Released Under LGPL - original licence link has changed is not relivant.
19525  *
19526  * Fork - LGPL
19527  * <script type="text/javascript">
19528  */
19529  
19530
19531 /**
19532  * @class Roo.dd.ScrollManager
19533  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19534  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19535  * @singleton
19536  */
19537 Roo.dd.ScrollManager = function(){
19538     var ddm = Roo.dd.DragDropMgr;
19539     var els = {};
19540     var dragEl = null;
19541     var proc = {};
19542     
19543     
19544     
19545     var onStop = function(e){
19546         dragEl = null;
19547         clearProc();
19548     };
19549     
19550     var triggerRefresh = function(){
19551         if(ddm.dragCurrent){
19552              ddm.refreshCache(ddm.dragCurrent.groups);
19553         }
19554     };
19555     
19556     var doScroll = function(){
19557         if(ddm.dragCurrent){
19558             var dds = Roo.dd.ScrollManager;
19559             if(!dds.animate){
19560                 if(proc.el.scroll(proc.dir, dds.increment)){
19561                     triggerRefresh();
19562                 }
19563             }else{
19564                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19565             }
19566         }
19567     };
19568     
19569     var clearProc = function(){
19570         if(proc.id){
19571             clearInterval(proc.id);
19572         }
19573         proc.id = 0;
19574         proc.el = null;
19575         proc.dir = "";
19576     };
19577     
19578     var startProc = function(el, dir){
19579          Roo.log('scroll startproc');
19580         clearProc();
19581         proc.el = el;
19582         proc.dir = dir;
19583         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19584     };
19585     
19586     var onFire = function(e, isDrop){
19587        
19588         if(isDrop || !ddm.dragCurrent){ return; }
19589         var dds = Roo.dd.ScrollManager;
19590         if(!dragEl || dragEl != ddm.dragCurrent){
19591             dragEl = ddm.dragCurrent;
19592             // refresh regions on drag start
19593             dds.refreshCache();
19594         }
19595         
19596         var xy = Roo.lib.Event.getXY(e);
19597         var pt = new Roo.lib.Point(xy[0], xy[1]);
19598         for(var id in els){
19599             var el = els[id], r = el._region;
19600             if(r && r.contains(pt) && el.isScrollable()){
19601                 if(r.bottom - pt.y <= dds.thresh){
19602                     if(proc.el != el){
19603                         startProc(el, "down");
19604                     }
19605                     return;
19606                 }else if(r.right - pt.x <= dds.thresh){
19607                     if(proc.el != el){
19608                         startProc(el, "left");
19609                     }
19610                     return;
19611                 }else if(pt.y - r.top <= dds.thresh){
19612                     if(proc.el != el){
19613                         startProc(el, "up");
19614                     }
19615                     return;
19616                 }else if(pt.x - r.left <= dds.thresh){
19617                     if(proc.el != el){
19618                         startProc(el, "right");
19619                     }
19620                     return;
19621                 }
19622             }
19623         }
19624         clearProc();
19625     };
19626     
19627     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19628     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19629     
19630     return {
19631         /**
19632          * Registers new overflow element(s) to auto scroll
19633          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19634          */
19635         register : function(el){
19636             if(el instanceof Array){
19637                 for(var i = 0, len = el.length; i < len; i++) {
19638                         this.register(el[i]);
19639                 }
19640             }else{
19641                 el = Roo.get(el);
19642                 els[el.id] = el;
19643             }
19644             Roo.dd.ScrollManager.els = els;
19645         },
19646         
19647         /**
19648          * Unregisters overflow element(s) so they are no longer scrolled
19649          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19650          */
19651         unregister : function(el){
19652             if(el instanceof Array){
19653                 for(var i = 0, len = el.length; i < len; i++) {
19654                         this.unregister(el[i]);
19655                 }
19656             }else{
19657                 el = Roo.get(el);
19658                 delete els[el.id];
19659             }
19660         },
19661         
19662         /**
19663          * The number of pixels from the edge of a container the pointer needs to be to 
19664          * trigger scrolling (defaults to 25)
19665          * @type Number
19666          */
19667         thresh : 25,
19668         
19669         /**
19670          * The number of pixels to scroll in each scroll increment (defaults to 50)
19671          * @type Number
19672          */
19673         increment : 100,
19674         
19675         /**
19676          * The frequency of scrolls in milliseconds (defaults to 500)
19677          * @type Number
19678          */
19679         frequency : 500,
19680         
19681         /**
19682          * True to animate the scroll (defaults to true)
19683          * @type Boolean
19684          */
19685         animate: true,
19686         
19687         /**
19688          * The animation duration in seconds - 
19689          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19690          * @type Number
19691          */
19692         animDuration: .4,
19693         
19694         /**
19695          * Manually trigger a cache refresh.
19696          */
19697         refreshCache : function(){
19698             for(var id in els){
19699                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19700                     els[id]._region = els[id].getRegion();
19701                 }
19702             }
19703         }
19704     };
19705 }();/*
19706  * Based on:
19707  * Ext JS Library 1.1.1
19708  * Copyright(c) 2006-2007, Ext JS, LLC.
19709  *
19710  * Originally Released Under LGPL - original licence link has changed is not relivant.
19711  *
19712  * Fork - LGPL
19713  * <script type="text/javascript">
19714  */
19715  
19716
19717 /**
19718  * @class Roo.dd.Registry
19719  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19720  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19721  * @singleton
19722  */
19723 Roo.dd.Registry = function(){
19724     var elements = {}; 
19725     var handles = {}; 
19726     var autoIdSeed = 0;
19727
19728     var getId = function(el, autogen){
19729         if(typeof el == "string"){
19730             return el;
19731         }
19732         var id = el.id;
19733         if(!id && autogen !== false){
19734             id = "roodd-" + (++autoIdSeed);
19735             el.id = id;
19736         }
19737         return id;
19738     };
19739     
19740     return {
19741     /**
19742      * Register a drag drop element
19743      * @param {String|HTMLElement} element The id or DOM node to register
19744      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19745      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19746      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19747      * populated in the data object (if applicable):
19748      * <pre>
19749 Value      Description<br />
19750 ---------  ------------------------------------------<br />
19751 handles    Array of DOM nodes that trigger dragging<br />
19752            for the element being registered<br />
19753 isHandle   True if the element passed in triggers<br />
19754            dragging itself, else false
19755 </pre>
19756      */
19757         register : function(el, data){
19758             data = data || {};
19759             if(typeof el == "string"){
19760                 el = document.getElementById(el);
19761             }
19762             data.ddel = el;
19763             elements[getId(el)] = data;
19764             if(data.isHandle !== false){
19765                 handles[data.ddel.id] = data;
19766             }
19767             if(data.handles){
19768                 var hs = data.handles;
19769                 for(var i = 0, len = hs.length; i < len; i++){
19770                         handles[getId(hs[i])] = data;
19771                 }
19772             }
19773         },
19774
19775     /**
19776      * Unregister a drag drop element
19777      * @param {String|HTMLElement}  element The id or DOM node to unregister
19778      */
19779         unregister : function(el){
19780             var id = getId(el, false);
19781             var data = elements[id];
19782             if(data){
19783                 delete elements[id];
19784                 if(data.handles){
19785                     var hs = data.handles;
19786                     for(var i = 0, len = hs.length; i < len; i++){
19787                         delete handles[getId(hs[i], false)];
19788                     }
19789                 }
19790             }
19791         },
19792
19793     /**
19794      * Returns the handle registered for a DOM Node by id
19795      * @param {String|HTMLElement} id The DOM node or id to look up
19796      * @return {Object} handle The custom handle data
19797      */
19798         getHandle : function(id){
19799             if(typeof id != "string"){ // must be element?
19800                 id = id.id;
19801             }
19802             return handles[id];
19803         },
19804
19805     /**
19806      * Returns the handle that is registered for the DOM node that is the target of the event
19807      * @param {Event} e The event
19808      * @return {Object} handle The custom handle data
19809      */
19810         getHandleFromEvent : function(e){
19811             var t = Roo.lib.Event.getTarget(e);
19812             return t ? handles[t.id] : null;
19813         },
19814
19815     /**
19816      * Returns a custom data object that is registered for a DOM node by id
19817      * @param {String|HTMLElement} id The DOM node or id to look up
19818      * @return {Object} data The custom data
19819      */
19820         getTarget : function(id){
19821             if(typeof id != "string"){ // must be element?
19822                 id = id.id;
19823             }
19824             return elements[id];
19825         },
19826
19827     /**
19828      * Returns a custom data object that is registered for the DOM node that is the target of the event
19829      * @param {Event} e The event
19830      * @return {Object} data The custom data
19831      */
19832         getTargetFromEvent : function(e){
19833             var t = Roo.lib.Event.getTarget(e);
19834             return t ? elements[t.id] || handles[t.id] : null;
19835         }
19836     };
19837 }();/*
19838  * Based on:
19839  * Ext JS Library 1.1.1
19840  * Copyright(c) 2006-2007, Ext JS, LLC.
19841  *
19842  * Originally Released Under LGPL - original licence link has changed is not relivant.
19843  *
19844  * Fork - LGPL
19845  * <script type="text/javascript">
19846  */
19847  
19848
19849 /**
19850  * @class Roo.dd.StatusProxy
19851  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19852  * default drag proxy used by all Roo.dd components.
19853  * @constructor
19854  * @param {Object} config
19855  */
19856 Roo.dd.StatusProxy = function(config){
19857     Roo.apply(this, config);
19858     this.id = this.id || Roo.id();
19859     this.el = new Roo.Layer({
19860         dh: {
19861             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19862                 {tag: "div", cls: "x-dd-drop-icon"},
19863                 {tag: "div", cls: "x-dd-drag-ghost"}
19864             ]
19865         }, 
19866         shadow: !config || config.shadow !== false
19867     });
19868     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19869     this.dropStatus = this.dropNotAllowed;
19870 };
19871
19872 Roo.dd.StatusProxy.prototype = {
19873     /**
19874      * @cfg {String} dropAllowed
19875      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19876      */
19877     dropAllowed : "x-dd-drop-ok",
19878     /**
19879      * @cfg {String} dropNotAllowed
19880      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19881      */
19882     dropNotAllowed : "x-dd-drop-nodrop",
19883
19884     /**
19885      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19886      * over the current target element.
19887      * @param {String} cssClass The css class for the new drop status indicator image
19888      */
19889     setStatus : function(cssClass){
19890         cssClass = cssClass || this.dropNotAllowed;
19891         if(this.dropStatus != cssClass){
19892             this.el.replaceClass(this.dropStatus, cssClass);
19893             this.dropStatus = cssClass;
19894         }
19895     },
19896
19897     /**
19898      * Resets the status indicator to the default dropNotAllowed value
19899      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19900      */
19901     reset : function(clearGhost){
19902         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19903         this.dropStatus = this.dropNotAllowed;
19904         if(clearGhost){
19905             this.ghost.update("");
19906         }
19907     },
19908
19909     /**
19910      * Updates the contents of the ghost element
19911      * @param {String} html The html that will replace the current innerHTML of the ghost element
19912      */
19913     update : function(html){
19914         if(typeof html == "string"){
19915             this.ghost.update(html);
19916         }else{
19917             this.ghost.update("");
19918             html.style.margin = "0";
19919             this.ghost.dom.appendChild(html);
19920         }
19921         // ensure float = none set?? cant remember why though.
19922         var el = this.ghost.dom.firstChild;
19923                 if(el){
19924                         Roo.fly(el).setStyle('float', 'none');
19925                 }
19926     },
19927     
19928     /**
19929      * Returns the underlying proxy {@link Roo.Layer}
19930      * @return {Roo.Layer} el
19931     */
19932     getEl : function(){
19933         return this.el;
19934     },
19935
19936     /**
19937      * Returns the ghost element
19938      * @return {Roo.Element} el
19939      */
19940     getGhost : function(){
19941         return this.ghost;
19942     },
19943
19944     /**
19945      * Hides the proxy
19946      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19947      */
19948     hide : function(clear){
19949         this.el.hide();
19950         if(clear){
19951             this.reset(true);
19952         }
19953     },
19954
19955     /**
19956      * Stops the repair animation if it's currently running
19957      */
19958     stop : function(){
19959         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19960             this.anim.stop();
19961         }
19962     },
19963
19964     /**
19965      * Displays this proxy
19966      */
19967     show : function(){
19968         this.el.show();
19969     },
19970
19971     /**
19972      * Force the Layer to sync its shadow and shim positions to the element
19973      */
19974     sync : function(){
19975         this.el.sync();
19976     },
19977
19978     /**
19979      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19980      * invalid drop operation by the item being dragged.
19981      * @param {Array} xy The XY position of the element ([x, y])
19982      * @param {Function} callback The function to call after the repair is complete
19983      * @param {Object} scope The scope in which to execute the callback
19984      */
19985     repair : function(xy, callback, scope){
19986         this.callback = callback;
19987         this.scope = scope;
19988         if(xy && this.animRepair !== false){
19989             this.el.addClass("x-dd-drag-repair");
19990             this.el.hideUnders(true);
19991             this.anim = this.el.shift({
19992                 duration: this.repairDuration || .5,
19993                 easing: 'easeOut',
19994                 xy: xy,
19995                 stopFx: true,
19996                 callback: this.afterRepair,
19997                 scope: this
19998             });
19999         }else{
20000             this.afterRepair();
20001         }
20002     },
20003
20004     // private
20005     afterRepair : function(){
20006         this.hide(true);
20007         if(typeof this.callback == "function"){
20008             this.callback.call(this.scope || this);
20009         }
20010         this.callback = null;
20011         this.scope = null;
20012     }
20013 };/*
20014  * Based on:
20015  * Ext JS Library 1.1.1
20016  * Copyright(c) 2006-2007, Ext JS, LLC.
20017  *
20018  * Originally Released Under LGPL - original licence link has changed is not relivant.
20019  *
20020  * Fork - LGPL
20021  * <script type="text/javascript">
20022  */
20023
20024 /**
20025  * @class Roo.dd.DragSource
20026  * @extends Roo.dd.DDProxy
20027  * A simple class that provides the basic implementation needed to make any element draggable.
20028  * @constructor
20029  * @param {String/HTMLElement/Element} el The container element
20030  * @param {Object} config
20031  */
20032 Roo.dd.DragSource = function(el, config){
20033     this.el = Roo.get(el);
20034     this.dragData = {};
20035     
20036     Roo.apply(this, config);
20037     
20038     if(!this.proxy){
20039         this.proxy = new Roo.dd.StatusProxy();
20040     }
20041
20042     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
20043           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
20044     
20045     this.dragging = false;
20046 };
20047
20048 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
20049     /**
20050      * @cfg {String} dropAllowed
20051      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20052      */
20053     dropAllowed : "x-dd-drop-ok",
20054     /**
20055      * @cfg {String} dropNotAllowed
20056      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20057      */
20058     dropNotAllowed : "x-dd-drop-nodrop",
20059
20060     /**
20061      * Returns the data object associated with this drag source
20062      * @return {Object} data An object containing arbitrary data
20063      */
20064     getDragData : function(e){
20065         return this.dragData;
20066     },
20067
20068     // private
20069     onDragEnter : function(e, id){
20070         var target = Roo.dd.DragDropMgr.getDDById(id);
20071         this.cachedTarget = target;
20072         if(this.beforeDragEnter(target, e, id) !== false){
20073             if(target.isNotifyTarget){
20074                 var status = target.notifyEnter(this, e, this.dragData);
20075                 this.proxy.setStatus(status);
20076             }else{
20077                 this.proxy.setStatus(this.dropAllowed);
20078             }
20079             
20080             if(this.afterDragEnter){
20081                 /**
20082                  * An empty function by default, but provided so that you can perform a custom action
20083                  * when the dragged item enters the drop target by providing an implementation.
20084                  * @param {Roo.dd.DragDrop} target The drop target
20085                  * @param {Event} e The event object
20086                  * @param {String} id The id of the dragged element
20087                  * @method afterDragEnter
20088                  */
20089                 this.afterDragEnter(target, e, id);
20090             }
20091         }
20092     },
20093
20094     /**
20095      * An empty function by default, but provided so that you can perform a custom action
20096      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
20097      * @param {Roo.dd.DragDrop} target The drop target
20098      * @param {Event} e The event object
20099      * @param {String} id The id of the dragged element
20100      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20101      */
20102     beforeDragEnter : function(target, e, id){
20103         return true;
20104     },
20105
20106     // private
20107     alignElWithMouse: function() {
20108         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
20109         this.proxy.sync();
20110     },
20111
20112     // private
20113     onDragOver : function(e, id){
20114         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20115         if(this.beforeDragOver(target, e, id) !== false){
20116             if(target.isNotifyTarget){
20117                 var status = target.notifyOver(this, e, this.dragData);
20118                 this.proxy.setStatus(status);
20119             }
20120
20121             if(this.afterDragOver){
20122                 /**
20123                  * An empty function by default, but provided so that you can perform a custom action
20124                  * while the dragged item is over the drop target by providing an implementation.
20125                  * @param {Roo.dd.DragDrop} target The drop target
20126                  * @param {Event} e The event object
20127                  * @param {String} id The id of the dragged element
20128                  * @method afterDragOver
20129                  */
20130                 this.afterDragOver(target, e, id);
20131             }
20132         }
20133     },
20134
20135     /**
20136      * An empty function by default, but provided so that you can perform a custom action
20137      * while the dragged item is over the drop target and optionally cancel the onDragOver.
20138      * @param {Roo.dd.DragDrop} target The drop target
20139      * @param {Event} e The event object
20140      * @param {String} id The id of the dragged element
20141      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20142      */
20143     beforeDragOver : function(target, e, id){
20144         return true;
20145     },
20146
20147     // private
20148     onDragOut : function(e, id){
20149         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20150         if(this.beforeDragOut(target, e, id) !== false){
20151             if(target.isNotifyTarget){
20152                 target.notifyOut(this, e, this.dragData);
20153             }
20154             this.proxy.reset();
20155             if(this.afterDragOut){
20156                 /**
20157                  * An empty function by default, but provided so that you can perform a custom action
20158                  * after the dragged item is dragged out of the target without dropping.
20159                  * @param {Roo.dd.DragDrop} target The drop target
20160                  * @param {Event} e The event object
20161                  * @param {String} id The id of the dragged element
20162                  * @method afterDragOut
20163                  */
20164                 this.afterDragOut(target, e, id);
20165             }
20166         }
20167         this.cachedTarget = null;
20168     },
20169
20170     /**
20171      * An empty function by default, but provided so that you can perform a custom action before the dragged
20172      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20173      * @param {Roo.dd.DragDrop} target The drop target
20174      * @param {Event} e The event object
20175      * @param {String} id The id of the dragged element
20176      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20177      */
20178     beforeDragOut : function(target, e, id){
20179         return true;
20180     },
20181     
20182     // private
20183     onDragDrop : function(e, id){
20184         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20185         if(this.beforeDragDrop(target, e, id) !== false){
20186             if(target.isNotifyTarget){
20187                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20188                     this.onValidDrop(target, e, id);
20189                 }else{
20190                     this.onInvalidDrop(target, e, id);
20191                 }
20192             }else{
20193                 this.onValidDrop(target, e, id);
20194             }
20195             
20196             if(this.afterDragDrop){
20197                 /**
20198                  * An empty function by default, but provided so that you can perform a custom action
20199                  * after a valid drag drop has occurred by providing an implementation.
20200                  * @param {Roo.dd.DragDrop} target The drop target
20201                  * @param {Event} e The event object
20202                  * @param {String} id The id of the dropped element
20203                  * @method afterDragDrop
20204                  */
20205                 this.afterDragDrop(target, e, id);
20206             }
20207         }
20208         delete this.cachedTarget;
20209     },
20210
20211     /**
20212      * An empty function by default, but provided so that you can perform a custom action before the dragged
20213      * item is dropped onto the target and optionally cancel the onDragDrop.
20214      * @param {Roo.dd.DragDrop} target The drop target
20215      * @param {Event} e The event object
20216      * @param {String} id The id of the dragged element
20217      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20218      */
20219     beforeDragDrop : function(target, e, id){
20220         return true;
20221     },
20222
20223     // private
20224     onValidDrop : function(target, e, id){
20225         this.hideProxy();
20226         if(this.afterValidDrop){
20227             /**
20228              * An empty function by default, but provided so that you can perform a custom action
20229              * after a valid drop has occurred by providing an implementation.
20230              * @param {Object} target The target DD 
20231              * @param {Event} e The event object
20232              * @param {String} id The id of the dropped element
20233              * @method afterInvalidDrop
20234              */
20235             this.afterValidDrop(target, e, id);
20236         }
20237     },
20238
20239     // private
20240     getRepairXY : function(e, data){
20241         return this.el.getXY();  
20242     },
20243
20244     // private
20245     onInvalidDrop : function(target, e, id){
20246         this.beforeInvalidDrop(target, e, id);
20247         if(this.cachedTarget){
20248             if(this.cachedTarget.isNotifyTarget){
20249                 this.cachedTarget.notifyOut(this, e, this.dragData);
20250             }
20251             this.cacheTarget = null;
20252         }
20253         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20254
20255         if(this.afterInvalidDrop){
20256             /**
20257              * An empty function by default, but provided so that you can perform a custom action
20258              * after an invalid drop has occurred by providing an implementation.
20259              * @param {Event} e The event object
20260              * @param {String} id The id of the dropped element
20261              * @method afterInvalidDrop
20262              */
20263             this.afterInvalidDrop(e, id);
20264         }
20265     },
20266
20267     // private
20268     afterRepair : function(){
20269         if(Roo.enableFx){
20270             this.el.highlight(this.hlColor || "c3daf9");
20271         }
20272         this.dragging = false;
20273     },
20274
20275     /**
20276      * An empty function by default, but provided so that you can perform a custom action after an invalid
20277      * drop has occurred.
20278      * @param {Roo.dd.DragDrop} target The drop target
20279      * @param {Event} e The event object
20280      * @param {String} id The id of the dragged element
20281      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20282      */
20283     beforeInvalidDrop : function(target, e, id){
20284         return true;
20285     },
20286
20287     // private
20288     handleMouseDown : function(e){
20289         if(this.dragging) {
20290             return;
20291         }
20292         var data = this.getDragData(e);
20293         if(data && this.onBeforeDrag(data, e) !== false){
20294             this.dragData = data;
20295             this.proxy.stop();
20296             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20297         } 
20298     },
20299
20300     /**
20301      * An empty function by default, but provided so that you can perform a custom action before the initial
20302      * drag event begins and optionally cancel it.
20303      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20304      * @param {Event} e The event object
20305      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20306      */
20307     onBeforeDrag : function(data, e){
20308         return true;
20309     },
20310
20311     /**
20312      * An empty function by default, but provided so that you can perform a custom action once the initial
20313      * drag event has begun.  The drag cannot be canceled from this function.
20314      * @param {Number} x The x position of the click on the dragged object
20315      * @param {Number} y The y position of the click on the dragged object
20316      */
20317     onStartDrag : Roo.emptyFn,
20318
20319     // private - YUI override
20320     startDrag : function(x, y){
20321         this.proxy.reset();
20322         this.dragging = true;
20323         this.proxy.update("");
20324         this.onInitDrag(x, y);
20325         this.proxy.show();
20326     },
20327
20328     // private
20329     onInitDrag : function(x, y){
20330         var clone = this.el.dom.cloneNode(true);
20331         clone.id = Roo.id(); // prevent duplicate ids
20332         this.proxy.update(clone);
20333         this.onStartDrag(x, y);
20334         return true;
20335     },
20336
20337     /**
20338      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20339      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20340      */
20341     getProxy : function(){
20342         return this.proxy;  
20343     },
20344
20345     /**
20346      * Hides the drag source's {@link Roo.dd.StatusProxy}
20347      */
20348     hideProxy : function(){
20349         this.proxy.hide();  
20350         this.proxy.reset(true);
20351         this.dragging = false;
20352     },
20353
20354     // private
20355     triggerCacheRefresh : function(){
20356         Roo.dd.DDM.refreshCache(this.groups);
20357     },
20358
20359     // private - override to prevent hiding
20360     b4EndDrag: function(e) {
20361     },
20362
20363     // private - override to prevent moving
20364     endDrag : function(e){
20365         this.onEndDrag(this.dragData, e);
20366     },
20367
20368     // private
20369     onEndDrag : function(data, e){
20370     },
20371     
20372     // private - pin to cursor
20373     autoOffset : function(x, y) {
20374         this.setDelta(-12, -20);
20375     }    
20376 });/*
20377  * Based on:
20378  * Ext JS Library 1.1.1
20379  * Copyright(c) 2006-2007, Ext JS, LLC.
20380  *
20381  * Originally Released Under LGPL - original licence link has changed is not relivant.
20382  *
20383  * Fork - LGPL
20384  * <script type="text/javascript">
20385  */
20386
20387
20388 /**
20389  * @class Roo.dd.DropTarget
20390  * @extends Roo.dd.DDTarget
20391  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20392  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20393  * @constructor
20394  * @param {String/HTMLElement/Element} el The container element
20395  * @param {Object} config
20396  */
20397 Roo.dd.DropTarget = function(el, config){
20398     this.el = Roo.get(el);
20399     
20400     var listeners = false; ;
20401     if (config && config.listeners) {
20402         listeners= config.listeners;
20403         delete config.listeners;
20404     }
20405     Roo.apply(this, config);
20406     
20407     if(this.containerScroll){
20408         Roo.dd.ScrollManager.register(this.el);
20409     }
20410     this.addEvents( {
20411          /**
20412          * @scope Roo.dd.DropTarget
20413          */
20414          
20415          /**
20416          * @event enter
20417          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20418          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20419          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20420          * 
20421          * IMPORTANT : it should set this.overClass and this.dropAllowed
20422          * 
20423          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20424          * @param {Event} e The event
20425          * @param {Object} data An object containing arbitrary data supplied by the drag source
20426          */
20427         "enter" : true,
20428         
20429          /**
20430          * @event over
20431          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20432          * This method will be called on every mouse movement while the drag source is over the drop target.
20433          * This default implementation simply returns the dropAllowed config value.
20434          * 
20435          * IMPORTANT : it should set this.dropAllowed
20436          * 
20437          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20438          * @param {Event} e The event
20439          * @param {Object} data An object containing arbitrary data supplied by the drag source
20440          
20441          */
20442         "over" : true,
20443         /**
20444          * @event out
20445          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20446          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20447          * overClass (if any) from the drop element.
20448          * 
20449          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20450          * @param {Event} e The event
20451          * @param {Object} data An object containing arbitrary data supplied by the drag source
20452          */
20453          "out" : true,
20454          
20455         /**
20456          * @event drop
20457          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20458          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20459          * implementation that does something to process the drop event and returns true so that the drag source's
20460          * repair action does not run.
20461          * 
20462          * IMPORTANT : it should set this.success
20463          * 
20464          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20465          * @param {Event} e The event
20466          * @param {Object} data An object containing arbitrary data supplied by the drag source
20467         */
20468          "drop" : true
20469     });
20470             
20471      
20472     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20473         this.el.dom, 
20474         this.ddGroup || this.group,
20475         {
20476             isTarget: true,
20477             listeners : listeners || {} 
20478            
20479         
20480         }
20481     );
20482
20483 };
20484
20485 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20486     /**
20487      * @cfg {String} overClass
20488      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20489      */
20490      /**
20491      * @cfg {String} ddGroup
20492      * The drag drop group to handle drop events for
20493      */
20494      
20495     /**
20496      * @cfg {String} dropAllowed
20497      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20498      */
20499     dropAllowed : "x-dd-drop-ok",
20500     /**
20501      * @cfg {String} dropNotAllowed
20502      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20503      */
20504     dropNotAllowed : "x-dd-drop-nodrop",
20505     /**
20506      * @cfg {boolean} success
20507      * set this after drop listener.. 
20508      */
20509     success : false,
20510     /**
20511      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20512      * if the drop point is valid for over/enter..
20513      */
20514     valid : false,
20515     // private
20516     isTarget : true,
20517
20518     // private
20519     isNotifyTarget : true,
20520     
20521     /**
20522      * @hide
20523      */
20524     notifyEnter : function(dd, e, data)
20525     {
20526         this.valid = true;
20527         this.fireEvent('enter', dd, e, data);
20528         if(this.overClass){
20529             this.el.addClass(this.overClass);
20530         }
20531         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20532             this.valid ? this.dropAllowed : this.dropNotAllowed
20533         );
20534     },
20535
20536     /**
20537      * @hide
20538      */
20539     notifyOver : function(dd, e, data)
20540     {
20541         this.valid = true;
20542         this.fireEvent('over', dd, e, data);
20543         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20544             this.valid ? this.dropAllowed : this.dropNotAllowed
20545         );
20546     },
20547
20548     /**
20549      * @hide
20550      */
20551     notifyOut : function(dd, e, data)
20552     {
20553         this.fireEvent('out', dd, e, data);
20554         if(this.overClass){
20555             this.el.removeClass(this.overClass);
20556         }
20557     },
20558
20559     /**
20560      * @hide
20561      */
20562     notifyDrop : function(dd, e, data)
20563     {
20564         this.success = false;
20565         this.fireEvent('drop', dd, e, data);
20566         return this.success;
20567     }
20568 });/*
20569  * Based on:
20570  * Ext JS Library 1.1.1
20571  * Copyright(c) 2006-2007, Ext JS, LLC.
20572  *
20573  * Originally Released Under LGPL - original licence link has changed is not relivant.
20574  *
20575  * Fork - LGPL
20576  * <script type="text/javascript">
20577  */
20578
20579
20580 /**
20581  * @class Roo.dd.DragZone
20582  * @extends Roo.dd.DragSource
20583  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20584  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20585  * @constructor
20586  * @param {String/HTMLElement/Element} el The container element
20587  * @param {Object} config
20588  */
20589 Roo.dd.DragZone = function(el, config){
20590     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20591     if(this.containerScroll){
20592         Roo.dd.ScrollManager.register(this.el);
20593     }
20594 };
20595
20596 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20597     /**
20598      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20599      * for auto scrolling during drag operations.
20600      */
20601     /**
20602      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20603      * method after a failed drop (defaults to "c3daf9" - light blue)
20604      */
20605
20606     /**
20607      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20608      * for a valid target to drag based on the mouse down. Override this method
20609      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20610      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20611      * @param {EventObject} e The mouse down event
20612      * @return {Object} The dragData
20613      */
20614     getDragData : function(e){
20615         return Roo.dd.Registry.getHandleFromEvent(e);
20616     },
20617     
20618     /**
20619      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20620      * this.dragData.ddel
20621      * @param {Number} x The x position of the click on the dragged object
20622      * @param {Number} y The y position of the click on the dragged object
20623      * @return {Boolean} true to continue the drag, false to cancel
20624      */
20625     onInitDrag : function(x, y){
20626         this.proxy.update(this.dragData.ddel.cloneNode(true));
20627         this.onStartDrag(x, y);
20628         return true;
20629     },
20630     
20631     /**
20632      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20633      */
20634     afterRepair : function(){
20635         if(Roo.enableFx){
20636             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20637         }
20638         this.dragging = false;
20639     },
20640
20641     /**
20642      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20643      * the XY of this.dragData.ddel
20644      * @param {EventObject} e The mouse up event
20645      * @return {Array} The xy location (e.g. [100, 200])
20646      */
20647     getRepairXY : function(e){
20648         return Roo.Element.fly(this.dragData.ddel).getXY();  
20649     }
20650 });/*
20651  * Based on:
20652  * Ext JS Library 1.1.1
20653  * Copyright(c) 2006-2007, Ext JS, LLC.
20654  *
20655  * Originally Released Under LGPL - original licence link has changed is not relivant.
20656  *
20657  * Fork - LGPL
20658  * <script type="text/javascript">
20659  */
20660 /**
20661  * @class Roo.dd.DropZone
20662  * @extends Roo.dd.DropTarget
20663  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20664  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20665  * @constructor
20666  * @param {String/HTMLElement/Element} el The container element
20667  * @param {Object} config
20668  */
20669 Roo.dd.DropZone = function(el, config){
20670     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20671 };
20672
20673 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20674     /**
20675      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20676      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20677      * provide your own custom lookup.
20678      * @param {Event} e The event
20679      * @return {Object} data The custom data
20680      */
20681     getTargetFromEvent : function(e){
20682         return Roo.dd.Registry.getTargetFromEvent(e);
20683     },
20684
20685     /**
20686      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20687      * that it has registered.  This method has no default implementation and should be overridden to provide
20688      * node-specific processing if necessary.
20689      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20690      * {@link #getTargetFromEvent} for this node)
20691      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20692      * @param {Event} e The event
20693      * @param {Object} data An object containing arbitrary data supplied by the drag source
20694      */
20695     onNodeEnter : function(n, dd, e, data){
20696         
20697     },
20698
20699     /**
20700      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20701      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20702      * overridden to provide the proper feedback.
20703      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20704      * {@link #getTargetFromEvent} for this node)
20705      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20706      * @param {Event} e The event
20707      * @param {Object} data An object containing arbitrary data supplied by the drag source
20708      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20709      * underlying {@link Roo.dd.StatusProxy} can be updated
20710      */
20711     onNodeOver : function(n, dd, e, data){
20712         return this.dropAllowed;
20713     },
20714
20715     /**
20716      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20717      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20718      * node-specific processing if necessary.
20719      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20720      * {@link #getTargetFromEvent} for this node)
20721      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20722      * @param {Event} e The event
20723      * @param {Object} data An object containing arbitrary data supplied by the drag source
20724      */
20725     onNodeOut : function(n, dd, e, data){
20726         
20727     },
20728
20729     /**
20730      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20731      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20732      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20733      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20734      * {@link #getTargetFromEvent} for this node)
20735      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20736      * @param {Event} e The event
20737      * @param {Object} data An object containing arbitrary data supplied by the drag source
20738      * @return {Boolean} True if the drop was valid, else false
20739      */
20740     onNodeDrop : function(n, dd, e, data){
20741         return false;
20742     },
20743
20744     /**
20745      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20746      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20747      * it should be overridden to provide the proper feedback if necessary.
20748      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20749      * @param {Event} e The event
20750      * @param {Object} data An object containing arbitrary data supplied by the drag source
20751      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20752      * underlying {@link Roo.dd.StatusProxy} can be updated
20753      */
20754     onContainerOver : function(dd, e, data){
20755         return this.dropNotAllowed;
20756     },
20757
20758     /**
20759      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20760      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20761      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20762      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20763      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20764      * @param {Event} e The event
20765      * @param {Object} data An object containing arbitrary data supplied by the drag source
20766      * @return {Boolean} True if the drop was valid, else false
20767      */
20768     onContainerDrop : function(dd, e, data){
20769         return false;
20770     },
20771
20772     /**
20773      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20774      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20775      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20776      * you should override this method and provide a custom implementation.
20777      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20778      * @param {Event} e The event
20779      * @param {Object} data An object containing arbitrary data supplied by the drag source
20780      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20781      * underlying {@link Roo.dd.StatusProxy} can be updated
20782      */
20783     notifyEnter : function(dd, e, data){
20784         return this.dropNotAllowed;
20785     },
20786
20787     /**
20788      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20789      * This method will be called on every mouse movement while the drag source is over the drop zone.
20790      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20791      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20792      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20793      * registered node, it will call {@link #onContainerOver}.
20794      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20795      * @param {Event} e The event
20796      * @param {Object} data An object containing arbitrary data supplied by the drag source
20797      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20798      * underlying {@link Roo.dd.StatusProxy} can be updated
20799      */
20800     notifyOver : function(dd, e, data){
20801         var n = this.getTargetFromEvent(e);
20802         if(!n){ // not over valid drop target
20803             if(this.lastOverNode){
20804                 this.onNodeOut(this.lastOverNode, dd, e, data);
20805                 this.lastOverNode = null;
20806             }
20807             return this.onContainerOver(dd, e, data);
20808         }
20809         if(this.lastOverNode != n){
20810             if(this.lastOverNode){
20811                 this.onNodeOut(this.lastOverNode, dd, e, data);
20812             }
20813             this.onNodeEnter(n, dd, e, data);
20814             this.lastOverNode = n;
20815         }
20816         return this.onNodeOver(n, dd, e, data);
20817     },
20818
20819     /**
20820      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20821      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20822      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20823      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20824      * @param {Event} e The event
20825      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20826      */
20827     notifyOut : function(dd, e, data){
20828         if(this.lastOverNode){
20829             this.onNodeOut(this.lastOverNode, dd, e, data);
20830             this.lastOverNode = null;
20831         }
20832     },
20833
20834     /**
20835      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20836      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20837      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20838      * otherwise it will call {@link #onContainerDrop}.
20839      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20840      * @param {Event} e The event
20841      * @param {Object} data An object containing arbitrary data supplied by the drag source
20842      * @return {Boolean} True if the drop was valid, else false
20843      */
20844     notifyDrop : function(dd, e, data){
20845         if(this.lastOverNode){
20846             this.onNodeOut(this.lastOverNode, dd, e, data);
20847             this.lastOverNode = null;
20848         }
20849         var n = this.getTargetFromEvent(e);
20850         return n ?
20851             this.onNodeDrop(n, dd, e, data) :
20852             this.onContainerDrop(dd, e, data);
20853     },
20854
20855     // private
20856     triggerCacheRefresh : function(){
20857         Roo.dd.DDM.refreshCache(this.groups);
20858     }  
20859 });/*
20860  * Based on:
20861  * Ext JS Library 1.1.1
20862  * Copyright(c) 2006-2007, Ext JS, LLC.
20863  *
20864  * Originally Released Under LGPL - original licence link has changed is not relivant.
20865  *
20866  * Fork - LGPL
20867  * <script type="text/javascript">
20868  */
20869
20870
20871 /**
20872  * @class Roo.data.SortTypes
20873  * @singleton
20874  * Defines the default sorting (casting?) comparison functions used when sorting data.
20875  */
20876 Roo.data.SortTypes = {
20877     /**
20878      * Default sort that does nothing
20879      * @param {Mixed} s The value being converted
20880      * @return {Mixed} The comparison value
20881      */
20882     none : function(s){
20883         return s;
20884     },
20885     
20886     /**
20887      * The regular expression used to strip tags
20888      * @type {RegExp}
20889      * @property
20890      */
20891     stripTagsRE : /<\/?[^>]+>/gi,
20892     
20893     /**
20894      * Strips all HTML tags to sort on text only
20895      * @param {Mixed} s The value being converted
20896      * @return {String} The comparison value
20897      */
20898     asText : function(s){
20899         return String(s).replace(this.stripTagsRE, "");
20900     },
20901     
20902     /**
20903      * Strips all HTML tags to sort on text only - Case insensitive
20904      * @param {Mixed} s The value being converted
20905      * @return {String} The comparison value
20906      */
20907     asUCText : function(s){
20908         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20909     },
20910     
20911     /**
20912      * Case insensitive string
20913      * @param {Mixed} s The value being converted
20914      * @return {String} The comparison value
20915      */
20916     asUCString : function(s) {
20917         return String(s).toUpperCase();
20918     },
20919     
20920     /**
20921      * Date sorting
20922      * @param {Mixed} s The value being converted
20923      * @return {Number} The comparison value
20924      */
20925     asDate : function(s) {
20926         if(!s){
20927             return 0;
20928         }
20929         if(s instanceof Date){
20930             return s.getTime();
20931         }
20932         return Date.parse(String(s));
20933     },
20934     
20935     /**
20936      * Float sorting
20937      * @param {Mixed} s The value being converted
20938      * @return {Float} The comparison value
20939      */
20940     asFloat : function(s) {
20941         var val = parseFloat(String(s).replace(/,/g, ""));
20942         if(isNaN(val)) {
20943             val = 0;
20944         }
20945         return val;
20946     },
20947     
20948     /**
20949      * Integer sorting
20950      * @param {Mixed} s The value being converted
20951      * @return {Number} The comparison value
20952      */
20953     asInt : function(s) {
20954         var val = parseInt(String(s).replace(/,/g, ""));
20955         if(isNaN(val)) {
20956             val = 0;
20957         }
20958         return val;
20959     }
20960 };/*
20961  * Based on:
20962  * Ext JS Library 1.1.1
20963  * Copyright(c) 2006-2007, Ext JS, LLC.
20964  *
20965  * Originally Released Under LGPL - original licence link has changed is not relivant.
20966  *
20967  * Fork - LGPL
20968  * <script type="text/javascript">
20969  */
20970
20971 /**
20972 * @class Roo.data.Record
20973  * Instances of this class encapsulate both record <em>definition</em> information, and record
20974  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20975  * to access Records cached in an {@link Roo.data.Store} object.<br>
20976  * <p>
20977  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20978  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20979  * objects.<br>
20980  * <p>
20981  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20982  * @constructor
20983  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20984  * {@link #create}. The parameters are the same.
20985  * @param {Array} data An associative Array of data values keyed by the field name.
20986  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20987  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20988  * not specified an integer id is generated.
20989  */
20990 Roo.data.Record = function(data, id){
20991     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20992     this.data = data;
20993 };
20994
20995 /**
20996  * Generate a constructor for a specific record layout.
20997  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20998  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20999  * Each field definition object may contain the following properties: <ul>
21000  * <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,
21001  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
21002  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
21003  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
21004  * is being used, then this is a string containing the javascript expression to reference the data relative to 
21005  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
21006  * to the data item relative to the record element. If the mapping expression is the same as the field name,
21007  * this may be omitted.</p></li>
21008  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
21009  * <ul><li>auto (Default, implies no conversion)</li>
21010  * <li>string</li>
21011  * <li>int</li>
21012  * <li>float</li>
21013  * <li>boolean</li>
21014  * <li>date</li></ul></p></li>
21015  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
21016  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
21017  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
21018  * by the Reader into an object that will be stored in the Record. It is passed the
21019  * following parameters:<ul>
21020  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
21021  * </ul></p></li>
21022  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
21023  * </ul>
21024  * <br>usage:<br><pre><code>
21025 var TopicRecord = Roo.data.Record.create(
21026     {name: 'title', mapping: 'topic_title'},
21027     {name: 'author', mapping: 'username'},
21028     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
21029     {name: 'lastPost', mapping: 'post_time', type: 'date'},
21030     {name: 'lastPoster', mapping: 'user2'},
21031     {name: 'excerpt', mapping: 'post_text'}
21032 );
21033
21034 var myNewRecord = new TopicRecord({
21035     title: 'Do my job please',
21036     author: 'noobie',
21037     totalPosts: 1,
21038     lastPost: new Date(),
21039     lastPoster: 'Animal',
21040     excerpt: 'No way dude!'
21041 });
21042 myStore.add(myNewRecord);
21043 </code></pre>
21044  * @method create
21045  * @static
21046  */
21047 Roo.data.Record.create = function(o){
21048     var f = function(){
21049         f.superclass.constructor.apply(this, arguments);
21050     };
21051     Roo.extend(f, Roo.data.Record);
21052     var p = f.prototype;
21053     p.fields = new Roo.util.MixedCollection(false, function(field){
21054         return field.name;
21055     });
21056     for(var i = 0, len = o.length; i < len; i++){
21057         p.fields.add(new Roo.data.Field(o[i]));
21058     }
21059     f.getField = function(name){
21060         return p.fields.get(name);  
21061     };
21062     return f;
21063 };
21064
21065 Roo.data.Record.AUTO_ID = 1000;
21066 Roo.data.Record.EDIT = 'edit';
21067 Roo.data.Record.REJECT = 'reject';
21068 Roo.data.Record.COMMIT = 'commit';
21069
21070 Roo.data.Record.prototype = {
21071     /**
21072      * Readonly flag - true if this record has been modified.
21073      * @type Boolean
21074      */
21075     dirty : false,
21076     editing : false,
21077     error: null,
21078     modified: null,
21079
21080     // private
21081     join : function(store){
21082         this.store = store;
21083     },
21084
21085     /**
21086      * Set the named field to the specified value.
21087      * @param {String} name The name of the field to set.
21088      * @param {Object} value The value to set the field to.
21089      */
21090     set : function(name, value){
21091         if(this.data[name] == value){
21092             return;
21093         }
21094         this.dirty = true;
21095         if(!this.modified){
21096             this.modified = {};
21097         }
21098         if(typeof this.modified[name] == 'undefined'){
21099             this.modified[name] = this.data[name];
21100         }
21101         this.data[name] = value;
21102         if(!this.editing && this.store){
21103             this.store.afterEdit(this);
21104         }       
21105     },
21106
21107     /**
21108      * Get the value of the named field.
21109      * @param {String} name The name of the field to get the value of.
21110      * @return {Object} The value of the field.
21111      */
21112     get : function(name){
21113         return this.data[name]; 
21114     },
21115
21116     // private
21117     beginEdit : function(){
21118         this.editing = true;
21119         this.modified = {}; 
21120     },
21121
21122     // private
21123     cancelEdit : function(){
21124         this.editing = false;
21125         delete this.modified;
21126     },
21127
21128     // private
21129     endEdit : function(){
21130         this.editing = false;
21131         if(this.dirty && this.store){
21132             this.store.afterEdit(this);
21133         }
21134     },
21135
21136     /**
21137      * Usually called by the {@link Roo.data.Store} which owns the Record.
21138      * Rejects all changes made to the Record since either creation, or the last commit operation.
21139      * Modified fields are reverted to their original values.
21140      * <p>
21141      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21142      * of reject operations.
21143      */
21144     reject : function(){
21145         var m = this.modified;
21146         for(var n in m){
21147             if(typeof m[n] != "function"){
21148                 this.data[n] = m[n];
21149             }
21150         }
21151         this.dirty = false;
21152         delete this.modified;
21153         this.editing = false;
21154         if(this.store){
21155             this.store.afterReject(this);
21156         }
21157     },
21158
21159     /**
21160      * Usually called by the {@link Roo.data.Store} which owns the Record.
21161      * Commits all changes made to the Record since either creation, or the last commit operation.
21162      * <p>
21163      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21164      * of commit operations.
21165      */
21166     commit : function(){
21167         this.dirty = false;
21168         delete this.modified;
21169         this.editing = false;
21170         if(this.store){
21171             this.store.afterCommit(this);
21172         }
21173     },
21174
21175     // private
21176     hasError : function(){
21177         return this.error != null;
21178     },
21179
21180     // private
21181     clearError : function(){
21182         this.error = null;
21183     },
21184
21185     /**
21186      * Creates a copy of this record.
21187      * @param {String} id (optional) A new record id if you don't want to use this record's id
21188      * @return {Record}
21189      */
21190     copy : function(newId) {
21191         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21192     }
21193 };/*
21194  * Based on:
21195  * Ext JS Library 1.1.1
21196  * Copyright(c) 2006-2007, Ext JS, LLC.
21197  *
21198  * Originally Released Under LGPL - original licence link has changed is not relivant.
21199  *
21200  * Fork - LGPL
21201  * <script type="text/javascript">
21202  */
21203
21204
21205
21206 /**
21207  * @class Roo.data.Store
21208  * @extends Roo.util.Observable
21209  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21210  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21211  * <p>
21212  * 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
21213  * has no knowledge of the format of the data returned by the Proxy.<br>
21214  * <p>
21215  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21216  * instances from the data object. These records are cached and made available through accessor functions.
21217  * @constructor
21218  * Creates a new Store.
21219  * @param {Object} config A config object containing the objects needed for the Store to access data,
21220  * and read the data into Records.
21221  */
21222 Roo.data.Store = function(config){
21223     this.data = new Roo.util.MixedCollection(false);
21224     this.data.getKey = function(o){
21225         return o.id;
21226     };
21227     this.baseParams = {};
21228     // private
21229     this.paramNames = {
21230         "start" : "start",
21231         "limit" : "limit",
21232         "sort" : "sort",
21233         "dir" : "dir",
21234         "multisort" : "_multisort"
21235     };
21236
21237     if(config && config.data){
21238         this.inlineData = config.data;
21239         delete config.data;
21240     }
21241
21242     Roo.apply(this, config);
21243     
21244     if(this.reader){ // reader passed
21245         this.reader = Roo.factory(this.reader, Roo.data);
21246         this.reader.xmodule = this.xmodule || false;
21247         if(!this.recordType){
21248             this.recordType = this.reader.recordType;
21249         }
21250         if(this.reader.onMetaChange){
21251             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21252         }
21253     }
21254
21255     if(this.recordType){
21256         this.fields = this.recordType.prototype.fields;
21257     }
21258     this.modified = [];
21259
21260     this.addEvents({
21261         /**
21262          * @event datachanged
21263          * Fires when the data cache has changed, and a widget which is using this Store
21264          * as a Record cache should refresh its view.
21265          * @param {Store} this
21266          */
21267         datachanged : true,
21268         /**
21269          * @event metachange
21270          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21271          * @param {Store} this
21272          * @param {Object} meta The JSON metadata
21273          */
21274         metachange : true,
21275         /**
21276          * @event add
21277          * Fires when Records have been added to the Store
21278          * @param {Store} this
21279          * @param {Roo.data.Record[]} records The array of Records added
21280          * @param {Number} index The index at which the record(s) were added
21281          */
21282         add : true,
21283         /**
21284          * @event remove
21285          * Fires when a Record has been removed from the Store
21286          * @param {Store} this
21287          * @param {Roo.data.Record} record The Record that was removed
21288          * @param {Number} index The index at which the record was removed
21289          */
21290         remove : true,
21291         /**
21292          * @event update
21293          * Fires when a Record has been updated
21294          * @param {Store} this
21295          * @param {Roo.data.Record} record The Record that was updated
21296          * @param {String} operation The update operation being performed.  Value may be one of:
21297          * <pre><code>
21298  Roo.data.Record.EDIT
21299  Roo.data.Record.REJECT
21300  Roo.data.Record.COMMIT
21301          * </code></pre>
21302          */
21303         update : true,
21304         /**
21305          * @event clear
21306          * Fires when the data cache has been cleared.
21307          * @param {Store} this
21308          */
21309         clear : true,
21310         /**
21311          * @event beforeload
21312          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21313          * the load action will be canceled.
21314          * @param {Store} this
21315          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21316          */
21317         beforeload : true,
21318         /**
21319          * @event beforeloadadd
21320          * Fires after a new set of Records has been loaded.
21321          * @param {Store} this
21322          * @param {Roo.data.Record[]} records The Records that were loaded
21323          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21324          */
21325         beforeloadadd : true,
21326         /**
21327          * @event load
21328          * Fires after a new set of Records has been loaded, before they are added to the store.
21329          * @param {Store} this
21330          * @param {Roo.data.Record[]} records The Records that were loaded
21331          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21332          * @params {Object} return from reader
21333          */
21334         load : true,
21335         /**
21336          * @event loadexception
21337          * Fires if an exception occurs in the Proxy during loading.
21338          * Called with the signature of the Proxy's "loadexception" event.
21339          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21340          * 
21341          * @param {Proxy} 
21342          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21343          * @param {Object} load options 
21344          * @param {Object} jsonData from your request (normally this contains the Exception)
21345          */
21346         loadexception : true
21347     });
21348     
21349     if(this.proxy){
21350         this.proxy = Roo.factory(this.proxy, Roo.data);
21351         this.proxy.xmodule = this.xmodule || false;
21352         this.relayEvents(this.proxy,  ["loadexception"]);
21353     }
21354     this.sortToggle = {};
21355     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21356
21357     Roo.data.Store.superclass.constructor.call(this);
21358
21359     if(this.inlineData){
21360         this.loadData(this.inlineData);
21361         delete this.inlineData;
21362     }
21363 };
21364
21365 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21366      /**
21367     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21368     * without a remote query - used by combo/forms at present.
21369     */
21370     
21371     /**
21372     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21373     */
21374     /**
21375     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21376     */
21377     /**
21378     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21379     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21380     */
21381     /**
21382     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21383     * on any HTTP request
21384     */
21385     /**
21386     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21387     */
21388     /**
21389     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21390     */
21391     multiSort: false,
21392     /**
21393     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21394     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21395     */
21396     remoteSort : false,
21397
21398     /**
21399     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21400      * loaded or when a record is removed. (defaults to false).
21401     */
21402     pruneModifiedRecords : false,
21403
21404     // private
21405     lastOptions : null,
21406
21407     /**
21408      * Add Records to the Store and fires the add event.
21409      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21410      */
21411     add : function(records){
21412         records = [].concat(records);
21413         for(var i = 0, len = records.length; i < len; i++){
21414             records[i].join(this);
21415         }
21416         var index = this.data.length;
21417         this.data.addAll(records);
21418         this.fireEvent("add", this, records, index);
21419     },
21420
21421     /**
21422      * Remove a Record from the Store and fires the remove event.
21423      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21424      */
21425     remove : function(record){
21426         var index = this.data.indexOf(record);
21427         this.data.removeAt(index);
21428         if(this.pruneModifiedRecords){
21429             this.modified.remove(record);
21430         }
21431         this.fireEvent("remove", this, record, index);
21432     },
21433
21434     /**
21435      * Remove all Records from the Store and fires the clear event.
21436      */
21437     removeAll : function(){
21438         this.data.clear();
21439         if(this.pruneModifiedRecords){
21440             this.modified = [];
21441         }
21442         this.fireEvent("clear", this);
21443     },
21444
21445     /**
21446      * Inserts Records to the Store at the given index and fires the add event.
21447      * @param {Number} index The start index at which to insert the passed Records.
21448      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21449      */
21450     insert : function(index, records){
21451         records = [].concat(records);
21452         for(var i = 0, len = records.length; i < len; i++){
21453             this.data.insert(index, records[i]);
21454             records[i].join(this);
21455         }
21456         this.fireEvent("add", this, records, index);
21457     },
21458
21459     /**
21460      * Get the index within the cache of the passed Record.
21461      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21462      * @return {Number} The index of the passed Record. Returns -1 if not found.
21463      */
21464     indexOf : function(record){
21465         return this.data.indexOf(record);
21466     },
21467
21468     /**
21469      * Get the index within the cache of the Record with the passed id.
21470      * @param {String} id The id of the Record to find.
21471      * @return {Number} The index of the Record. Returns -1 if not found.
21472      */
21473     indexOfId : function(id){
21474         return this.data.indexOfKey(id);
21475     },
21476
21477     /**
21478      * Get the Record with the specified id.
21479      * @param {String} id The id of the Record to find.
21480      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21481      */
21482     getById : function(id){
21483         return this.data.key(id);
21484     },
21485
21486     /**
21487      * Get the Record at the specified index.
21488      * @param {Number} index The index of the Record to find.
21489      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21490      */
21491     getAt : function(index){
21492         return this.data.itemAt(index);
21493     },
21494
21495     /**
21496      * Returns a range of Records between specified indices.
21497      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21498      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21499      * @return {Roo.data.Record[]} An array of Records
21500      */
21501     getRange : function(start, end){
21502         return this.data.getRange(start, end);
21503     },
21504
21505     // private
21506     storeOptions : function(o){
21507         o = Roo.apply({}, o);
21508         delete o.callback;
21509         delete o.scope;
21510         this.lastOptions = o;
21511     },
21512
21513     /**
21514      * Loads the Record cache from the configured Proxy using the configured Reader.
21515      * <p>
21516      * If using remote paging, then the first load call must specify the <em>start</em>
21517      * and <em>limit</em> properties in the options.params property to establish the initial
21518      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21519      * <p>
21520      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21521      * and this call will return before the new data has been loaded. Perform any post-processing
21522      * in a callback function, or in a "load" event handler.</strong>
21523      * <p>
21524      * @param {Object} options An object containing properties which control loading options:<ul>
21525      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21526      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21527      * passed the following arguments:<ul>
21528      * <li>r : Roo.data.Record[]</li>
21529      * <li>options: Options object from the load call</li>
21530      * <li>success: Boolean success indicator</li></ul></li>
21531      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21532      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21533      * </ul>
21534      */
21535     load : function(options){
21536         options = options || {};
21537         if(this.fireEvent("beforeload", this, options) !== false){
21538             this.storeOptions(options);
21539             var p = Roo.apply(options.params || {}, this.baseParams);
21540             // if meta was not loaded from remote source.. try requesting it.
21541             if (!this.reader.metaFromRemote) {
21542                 p._requestMeta = 1;
21543             }
21544             if(this.sortInfo && this.remoteSort){
21545                 var pn = this.paramNames;
21546                 p[pn["sort"]] = this.sortInfo.field;
21547                 p[pn["dir"]] = this.sortInfo.direction;
21548             }
21549             if (this.multiSort) {
21550                 var pn = this.paramNames;
21551                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21552             }
21553             
21554             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21555         }
21556     },
21557
21558     /**
21559      * Reloads the Record cache from the configured Proxy using the configured Reader and
21560      * the options from the last load operation performed.
21561      * @param {Object} options (optional) An object containing properties which may override the options
21562      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21563      * the most recently used options are reused).
21564      */
21565     reload : function(options){
21566         this.load(Roo.applyIf(options||{}, this.lastOptions));
21567     },
21568
21569     // private
21570     // Called as a callback by the Reader during a load operation.
21571     loadRecords : function(o, options, success){
21572         if(!o || success === false){
21573             if(success !== false){
21574                 this.fireEvent("load", this, [], options, o);
21575             }
21576             if(options.callback){
21577                 options.callback.call(options.scope || this, [], options, false);
21578             }
21579             return;
21580         }
21581         // if data returned failure - throw an exception.
21582         if (o.success === false) {
21583             // show a message if no listener is registered.
21584             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21585                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21586             }
21587             // loadmask wil be hooked into this..
21588             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21589             return;
21590         }
21591         var r = o.records, t = o.totalRecords || r.length;
21592         
21593         this.fireEvent("beforeloadadd", this, r, options, o);
21594         
21595         if(!options || options.add !== true){
21596             if(this.pruneModifiedRecords){
21597                 this.modified = [];
21598             }
21599             for(var i = 0, len = r.length; i < len; i++){
21600                 r[i].join(this);
21601             }
21602             if(this.snapshot){
21603                 this.data = this.snapshot;
21604                 delete this.snapshot;
21605             }
21606             this.data.clear();
21607             this.data.addAll(r);
21608             this.totalLength = t;
21609             this.applySort();
21610             this.fireEvent("datachanged", this);
21611         }else{
21612             this.totalLength = Math.max(t, this.data.length+r.length);
21613             this.add(r);
21614         }
21615         this.fireEvent("load", this, r, options, o);
21616         if(options.callback){
21617             options.callback.call(options.scope || this, r, options, true);
21618         }
21619     },
21620
21621
21622     /**
21623      * Loads data from a passed data block. A Reader which understands the format of the data
21624      * must have been configured in the constructor.
21625      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21626      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21627      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21628      */
21629     loadData : function(o, append){
21630         var r = this.reader.readRecords(o);
21631         this.loadRecords(r, {add: append}, true);
21632     },
21633
21634     /**
21635      * Gets the number of cached records.
21636      * <p>
21637      * <em>If using paging, this may not be the total size of the dataset. If the data object
21638      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21639      * the data set size</em>
21640      */
21641     getCount : function(){
21642         return this.data.length || 0;
21643     },
21644
21645     /**
21646      * Gets the total number of records in the dataset as returned by the server.
21647      * <p>
21648      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21649      * the dataset size</em>
21650      */
21651     getTotalCount : function(){
21652         return this.totalLength || 0;
21653     },
21654
21655     /**
21656      * Returns the sort state of the Store as an object with two properties:
21657      * <pre><code>
21658  field {String} The name of the field by which the Records are sorted
21659  direction {String} The sort order, "ASC" or "DESC"
21660      * </code></pre>
21661      */
21662     getSortState : function(){
21663         return this.sortInfo;
21664     },
21665
21666     // private
21667     applySort : function(){
21668         if(this.sortInfo && !this.remoteSort){
21669             var s = this.sortInfo, f = s.field;
21670             var st = this.fields.get(f).sortType;
21671             var fn = function(r1, r2){
21672                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21673                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21674             };
21675             this.data.sort(s.direction, fn);
21676             if(this.snapshot && this.snapshot != this.data){
21677                 this.snapshot.sort(s.direction, fn);
21678             }
21679         }
21680     },
21681
21682     /**
21683      * Sets the default sort column and order to be used by the next load operation.
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     setDefaultSort : function(field, dir){
21688         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21689     },
21690
21691     /**
21692      * Sort the Records.
21693      * If remote sorting is used, the sort is performed on the server, and the cache is
21694      * reloaded. If local sorting is used, the cache is sorted internally.
21695      * @param {String} fieldName The name of the field to sort by.
21696      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21697      */
21698     sort : function(fieldName, dir){
21699         var f = this.fields.get(fieldName);
21700         if(!dir){
21701             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21702             
21703             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21704                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21705             }else{
21706                 dir = f.sortDir;
21707             }
21708         }
21709         this.sortToggle[f.name] = dir;
21710         this.sortInfo = {field: f.name, direction: dir};
21711         if(!this.remoteSort){
21712             this.applySort();
21713             this.fireEvent("datachanged", this);
21714         }else{
21715             this.load(this.lastOptions);
21716         }
21717     },
21718
21719     /**
21720      * Calls the specified function for each of the Records in the cache.
21721      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21722      * Returning <em>false</em> aborts and exits the iteration.
21723      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21724      */
21725     each : function(fn, scope){
21726         this.data.each(fn, scope);
21727     },
21728
21729     /**
21730      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21731      * (e.g., during paging).
21732      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21733      */
21734     getModifiedRecords : function(){
21735         return this.modified;
21736     },
21737
21738     // private
21739     createFilterFn : function(property, value, anyMatch){
21740         if(!value.exec){ // not a regex
21741             value = String(value);
21742             if(value.length == 0){
21743                 return false;
21744             }
21745             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21746         }
21747         return function(r){
21748             return value.test(r.data[property]);
21749         };
21750     },
21751
21752     /**
21753      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21754      * @param {String} property A field on your records
21755      * @param {Number} start The record index to start at (defaults to 0)
21756      * @param {Number} end The last record index to include (defaults to length - 1)
21757      * @return {Number} The sum
21758      */
21759     sum : function(property, start, end){
21760         var rs = this.data.items, v = 0;
21761         start = start || 0;
21762         end = (end || end === 0) ? end : rs.length-1;
21763
21764         for(var i = start; i <= end; i++){
21765             v += (rs[i].data[property] || 0);
21766         }
21767         return v;
21768     },
21769
21770     /**
21771      * Filter the records by a specified property.
21772      * @param {String} field A field on your records
21773      * @param {String/RegExp} value Either a string that the field
21774      * should start with or a RegExp to test against the field
21775      * @param {Boolean} anyMatch True to match any part not just the beginning
21776      */
21777     filter : function(property, value, anyMatch){
21778         var fn = this.createFilterFn(property, value, anyMatch);
21779         return fn ? this.filterBy(fn) : this.clearFilter();
21780     },
21781
21782     /**
21783      * Filter by a function. The specified function will be called with each
21784      * record in this data source. If the function returns true the record is included,
21785      * otherwise it is filtered.
21786      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21787      * @param {Object} scope (optional) The scope of the function (defaults to this)
21788      */
21789     filterBy : function(fn, scope){
21790         this.snapshot = this.snapshot || this.data;
21791         this.data = this.queryBy(fn, scope||this);
21792         this.fireEvent("datachanged", this);
21793     },
21794
21795     /**
21796      * Query the records by a specified property.
21797      * @param {String} field A field on your records
21798      * @param {String/RegExp} value Either a string that the field
21799      * should start with or a RegExp to test against the field
21800      * @param {Boolean} anyMatch True to match any part not just the beginning
21801      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21802      */
21803     query : function(property, value, anyMatch){
21804         var fn = this.createFilterFn(property, value, anyMatch);
21805         return fn ? this.queryBy(fn) : this.data.clone();
21806     },
21807
21808     /**
21809      * Query by a function. The specified function will be called with each
21810      * record in this data source. If the function returns true the record is included
21811      * in the results.
21812      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21813      * @param {Object} scope (optional) The scope of the function (defaults to this)
21814       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21815      **/
21816     queryBy : function(fn, scope){
21817         var data = this.snapshot || this.data;
21818         return data.filterBy(fn, scope||this);
21819     },
21820
21821     /**
21822      * Collects unique values for a particular dataIndex from this store.
21823      * @param {String} dataIndex The property to collect
21824      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21825      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21826      * @return {Array} An array of the unique values
21827      **/
21828     collect : function(dataIndex, allowNull, bypassFilter){
21829         var d = (bypassFilter === true && this.snapshot) ?
21830                 this.snapshot.items : this.data.items;
21831         var v, sv, r = [], l = {};
21832         for(var i = 0, len = d.length; i < len; i++){
21833             v = d[i].data[dataIndex];
21834             sv = String(v);
21835             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21836                 l[sv] = true;
21837                 r[r.length] = v;
21838             }
21839         }
21840         return r;
21841     },
21842
21843     /**
21844      * Revert to a view of the Record cache with no filtering applied.
21845      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21846      */
21847     clearFilter : function(suppressEvent){
21848         if(this.snapshot && this.snapshot != this.data){
21849             this.data = this.snapshot;
21850             delete this.snapshot;
21851             if(suppressEvent !== true){
21852                 this.fireEvent("datachanged", this);
21853             }
21854         }
21855     },
21856
21857     // private
21858     afterEdit : function(record){
21859         if(this.modified.indexOf(record) == -1){
21860             this.modified.push(record);
21861         }
21862         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21863     },
21864     
21865     // private
21866     afterReject : function(record){
21867         this.modified.remove(record);
21868         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21869     },
21870
21871     // private
21872     afterCommit : function(record){
21873         this.modified.remove(record);
21874         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21875     },
21876
21877     /**
21878      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21879      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21880      */
21881     commitChanges : 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].commit();
21886         }
21887     },
21888
21889     /**
21890      * Cancel outstanding changes on all changed records.
21891      */
21892     rejectChanges : function(){
21893         var m = this.modified.slice(0);
21894         this.modified = [];
21895         for(var i = 0, len = m.length; i < len; i++){
21896             m[i].reject();
21897         }
21898     },
21899
21900     onMetaChange : function(meta, rtype, o){
21901         this.recordType = rtype;
21902         this.fields = rtype.prototype.fields;
21903         delete this.snapshot;
21904         this.sortInfo = meta.sortInfo || this.sortInfo;
21905         this.modified = [];
21906         this.fireEvent('metachange', this, this.reader.meta);
21907     },
21908     
21909     moveIndex : function(data, type)
21910     {
21911         var index = this.indexOf(data);
21912         
21913         var newIndex = index + type;
21914         
21915         this.remove(data);
21916         
21917         this.insert(newIndex, data);
21918         
21919     }
21920 });/*
21921  * Based on:
21922  * Ext JS Library 1.1.1
21923  * Copyright(c) 2006-2007, Ext JS, LLC.
21924  *
21925  * Originally Released Under LGPL - original licence link has changed is not relivant.
21926  *
21927  * Fork - LGPL
21928  * <script type="text/javascript">
21929  */
21930
21931 /**
21932  * @class Roo.data.SimpleStore
21933  * @extends Roo.data.Store
21934  * Small helper class to make creating Stores from Array data easier.
21935  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21936  * @cfg {Array} fields An array of field definition objects, or field name strings.
21937  * @cfg {Array} data The multi-dimensional array of data
21938  * @constructor
21939  * @param {Object} config
21940  */
21941 Roo.data.SimpleStore = function(config){
21942     Roo.data.SimpleStore.superclass.constructor.call(this, {
21943         isLocal : true,
21944         reader: new Roo.data.ArrayReader({
21945                 id: config.id
21946             },
21947             Roo.data.Record.create(config.fields)
21948         ),
21949         proxy : new Roo.data.MemoryProxy(config.data)
21950     });
21951     this.load();
21952 };
21953 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21954  * Based on:
21955  * Ext JS Library 1.1.1
21956  * Copyright(c) 2006-2007, Ext JS, LLC.
21957  *
21958  * Originally Released Under LGPL - original licence link has changed is not relivant.
21959  *
21960  * Fork - LGPL
21961  * <script type="text/javascript">
21962  */
21963
21964 /**
21965 /**
21966  * @extends Roo.data.Store
21967  * @class Roo.data.JsonStore
21968  * Small helper class to make creating Stores for JSON data easier. <br/>
21969 <pre><code>
21970 var store = new Roo.data.JsonStore({
21971     url: 'get-images.php',
21972     root: 'images',
21973     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21974 });
21975 </code></pre>
21976  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21977  * JsonReader and HttpProxy (unless inline data is provided).</b>
21978  * @cfg {Array} fields An array of field definition objects, or field name strings.
21979  * @constructor
21980  * @param {Object} config
21981  */
21982 Roo.data.JsonStore = function(c){
21983     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21984         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21985         reader: new Roo.data.JsonReader(c, c.fields)
21986     }));
21987 };
21988 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21989  * Based on:
21990  * Ext JS Library 1.1.1
21991  * Copyright(c) 2006-2007, Ext JS, LLC.
21992  *
21993  * Originally Released Under LGPL - original licence link has changed is not relivant.
21994  *
21995  * Fork - LGPL
21996  * <script type="text/javascript">
21997  */
21998
21999  
22000 Roo.data.Field = function(config){
22001     if(typeof config == "string"){
22002         config = {name: config};
22003     }
22004     Roo.apply(this, config);
22005     
22006     if(!this.type){
22007         this.type = "auto";
22008     }
22009     
22010     var st = Roo.data.SortTypes;
22011     // named sortTypes are supported, here we look them up
22012     if(typeof this.sortType == "string"){
22013         this.sortType = st[this.sortType];
22014     }
22015     
22016     // set default sortType for strings and dates
22017     if(!this.sortType){
22018         switch(this.type){
22019             case "string":
22020                 this.sortType = st.asUCString;
22021                 break;
22022             case "date":
22023                 this.sortType = st.asDate;
22024                 break;
22025             default:
22026                 this.sortType = st.none;
22027         }
22028     }
22029
22030     // define once
22031     var stripRe = /[\$,%]/g;
22032
22033     // prebuilt conversion function for this field, instead of
22034     // switching every time we're reading a value
22035     if(!this.convert){
22036         var cv, dateFormat = this.dateFormat;
22037         switch(this.type){
22038             case "":
22039             case "auto":
22040             case undefined:
22041                 cv = function(v){ return v; };
22042                 break;
22043             case "string":
22044                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
22045                 break;
22046             case "int":
22047                 cv = function(v){
22048                     return v !== undefined && v !== null && v !== '' ?
22049                            parseInt(String(v).replace(stripRe, ""), 10) : '';
22050                     };
22051                 break;
22052             case "float":
22053                 cv = function(v){
22054                     return v !== undefined && v !== null && v !== '' ?
22055                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
22056                     };
22057                 break;
22058             case "bool":
22059             case "boolean":
22060                 cv = function(v){ return v === true || v === "true" || v == 1; };
22061                 break;
22062             case "date":
22063                 cv = function(v){
22064                     if(!v){
22065                         return '';
22066                     }
22067                     if(v instanceof Date){
22068                         return v;
22069                     }
22070                     if(dateFormat){
22071                         if(dateFormat == "timestamp"){
22072                             return new Date(v*1000);
22073                         }
22074                         return Date.parseDate(v, dateFormat);
22075                     }
22076                     var parsed = Date.parse(v);
22077                     return parsed ? new Date(parsed) : null;
22078                 };
22079              break;
22080             
22081         }
22082         this.convert = cv;
22083     }
22084 };
22085
22086 Roo.data.Field.prototype = {
22087     dateFormat: null,
22088     defaultValue: "",
22089     mapping: null,
22090     sortType : null,
22091     sortDir : "ASC"
22092 };/*
22093  * Based on:
22094  * Ext JS Library 1.1.1
22095  * Copyright(c) 2006-2007, Ext JS, LLC.
22096  *
22097  * Originally Released Under LGPL - original licence link has changed is not relivant.
22098  *
22099  * Fork - LGPL
22100  * <script type="text/javascript">
22101  */
22102  
22103 // Base class for reading structured data from a data source.  This class is intended to be
22104 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
22105
22106 /**
22107  * @class Roo.data.DataReader
22108  * Base class for reading structured data from a data source.  This class is intended to be
22109  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
22110  */
22111
22112 Roo.data.DataReader = function(meta, recordType){
22113     
22114     this.meta = meta;
22115     
22116     this.recordType = recordType instanceof Array ? 
22117         Roo.data.Record.create(recordType) : recordType;
22118 };
22119
22120 Roo.data.DataReader.prototype = {
22121      /**
22122      * Create an empty record
22123      * @param {Object} data (optional) - overlay some values
22124      * @return {Roo.data.Record} record created.
22125      */
22126     newRow :  function(d) {
22127         var da =  {};
22128         this.recordType.prototype.fields.each(function(c) {
22129             switch( c.type) {
22130                 case 'int' : da[c.name] = 0; break;
22131                 case 'date' : da[c.name] = new Date(); break;
22132                 case 'float' : da[c.name] = 0.0; break;
22133                 case 'boolean' : da[c.name] = false; break;
22134                 default : da[c.name] = ""; break;
22135             }
22136             
22137         });
22138         return new this.recordType(Roo.apply(da, d));
22139     }
22140     
22141 };/*
22142  * Based on:
22143  * Ext JS Library 1.1.1
22144  * Copyright(c) 2006-2007, Ext JS, LLC.
22145  *
22146  * Originally Released Under LGPL - original licence link has changed is not relivant.
22147  *
22148  * Fork - LGPL
22149  * <script type="text/javascript">
22150  */
22151
22152 /**
22153  * @class Roo.data.DataProxy
22154  * @extends Roo.data.Observable
22155  * This class is an abstract base class for implementations which provide retrieval of
22156  * unformatted data objects.<br>
22157  * <p>
22158  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
22159  * (of the appropriate type which knows how to parse the data object) to provide a block of
22160  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
22161  * <p>
22162  * Custom implementations must implement the load method as described in
22163  * {@link Roo.data.HttpProxy#load}.
22164  */
22165 Roo.data.DataProxy = function(){
22166     this.addEvents({
22167         /**
22168          * @event beforeload
22169          * Fires before a network request is made to retrieve a data object.
22170          * @param {Object} This DataProxy object.
22171          * @param {Object} params The params parameter to the load function.
22172          */
22173         beforeload : true,
22174         /**
22175          * @event load
22176          * Fires before the load method's callback is called.
22177          * @param {Object} This DataProxy object.
22178          * @param {Object} o The data object.
22179          * @param {Object} arg The callback argument object passed to the load function.
22180          */
22181         load : true,
22182         /**
22183          * @event loadexception
22184          * Fires if an Exception occurs during data retrieval.
22185          * @param {Object} This DataProxy object.
22186          * @param {Object} o The data object.
22187          * @param {Object} arg The callback argument object passed to the load function.
22188          * @param {Object} e The Exception.
22189          */
22190         loadexception : true
22191     });
22192     Roo.data.DataProxy.superclass.constructor.call(this);
22193 };
22194
22195 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22196
22197     /**
22198      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22199      */
22200 /*
22201  * Based on:
22202  * Ext JS Library 1.1.1
22203  * Copyright(c) 2006-2007, Ext JS, LLC.
22204  *
22205  * Originally Released Under LGPL - original licence link has changed is not relivant.
22206  *
22207  * Fork - LGPL
22208  * <script type="text/javascript">
22209  */
22210 /**
22211  * @class Roo.data.MemoryProxy
22212  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22213  * to the Reader when its load method is called.
22214  * @constructor
22215  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22216  */
22217 Roo.data.MemoryProxy = function(data){
22218     if (data.data) {
22219         data = data.data;
22220     }
22221     Roo.data.MemoryProxy.superclass.constructor.call(this);
22222     this.data = data;
22223 };
22224
22225 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22226     /**
22227      * Load data from the requested source (in this case an in-memory
22228      * data object passed to the constructor), read the data object into
22229      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22230      * process that block using the passed callback.
22231      * @param {Object} params This parameter is not used by the MemoryProxy class.
22232      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22233      * object into a block of Roo.data.Records.
22234      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22235      * The function must be passed <ul>
22236      * <li>The Record block object</li>
22237      * <li>The "arg" argument from the load function</li>
22238      * <li>A boolean success indicator</li>
22239      * </ul>
22240      * @param {Object} scope The scope in which to call the callback
22241      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22242      */
22243     load : function(params, reader, callback, scope, arg){
22244         params = params || {};
22245         var result;
22246         try {
22247             result = reader.readRecords(this.data);
22248         }catch(e){
22249             this.fireEvent("loadexception", this, arg, null, e);
22250             callback.call(scope, null, arg, false);
22251             return;
22252         }
22253         callback.call(scope, result, arg, true);
22254     },
22255     
22256     // private
22257     update : function(params, records){
22258         
22259     }
22260 });/*
22261  * Based on:
22262  * Ext JS Library 1.1.1
22263  * Copyright(c) 2006-2007, Ext JS, LLC.
22264  *
22265  * Originally Released Under LGPL - original licence link has changed is not relivant.
22266  *
22267  * Fork - LGPL
22268  * <script type="text/javascript">
22269  */
22270 /**
22271  * @class Roo.data.HttpProxy
22272  * @extends Roo.data.DataProxy
22273  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22274  * configured to reference a certain URL.<br><br>
22275  * <p>
22276  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22277  * from which the running page was served.<br><br>
22278  * <p>
22279  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22280  * <p>
22281  * Be aware that to enable the browser to parse an XML document, the server must set
22282  * the Content-Type header in the HTTP response to "text/xml".
22283  * @constructor
22284  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22285  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22286  * will be used to make the request.
22287  */
22288 Roo.data.HttpProxy = function(conn){
22289     Roo.data.HttpProxy.superclass.constructor.call(this);
22290     // is conn a conn config or a real conn?
22291     this.conn = conn;
22292     this.useAjax = !conn || !conn.events;
22293   
22294 };
22295
22296 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22297     // thse are take from connection...
22298     
22299     /**
22300      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22301      */
22302     /**
22303      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22304      * extra parameters to each request made by this object. (defaults to undefined)
22305      */
22306     /**
22307      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22308      *  to each request made by this object. (defaults to undefined)
22309      */
22310     /**
22311      * @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)
22312      */
22313     /**
22314      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22315      */
22316      /**
22317      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22318      * @type Boolean
22319      */
22320   
22321
22322     /**
22323      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22324      * @type Boolean
22325      */
22326     /**
22327      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22328      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22329      * a finer-grained basis than the DataProxy events.
22330      */
22331     getConnection : function(){
22332         return this.useAjax ? Roo.Ajax : this.conn;
22333     },
22334
22335     /**
22336      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22337      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22338      * process that block using the passed callback.
22339      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22340      * for the request to the remote server.
22341      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22342      * object into a block of Roo.data.Records.
22343      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22344      * The function must be passed <ul>
22345      * <li>The Record block object</li>
22346      * <li>The "arg" argument from the load function</li>
22347      * <li>A boolean success indicator</li>
22348      * </ul>
22349      * @param {Object} scope The scope in which to call the callback
22350      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22351      */
22352     load : function(params, reader, callback, scope, arg){
22353         if(this.fireEvent("beforeload", this, params) !== false){
22354             var  o = {
22355                 params : params || {},
22356                 request: {
22357                     callback : callback,
22358                     scope : scope,
22359                     arg : arg
22360                 },
22361                 reader: reader,
22362                 callback : this.loadResponse,
22363                 scope: this
22364             };
22365             if(this.useAjax){
22366                 Roo.applyIf(o, this.conn);
22367                 if(this.activeRequest){
22368                     Roo.Ajax.abort(this.activeRequest);
22369                 }
22370                 this.activeRequest = Roo.Ajax.request(o);
22371             }else{
22372                 this.conn.request(o);
22373             }
22374         }else{
22375             callback.call(scope||this, null, arg, false);
22376         }
22377     },
22378
22379     // private
22380     loadResponse : function(o, success, response){
22381         delete this.activeRequest;
22382         if(!success){
22383             this.fireEvent("loadexception", this, o, response);
22384             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22385             return;
22386         }
22387         var result;
22388         try {
22389             result = o.reader.read(response);
22390         }catch(e){
22391             this.fireEvent("loadexception", this, o, response, e);
22392             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22393             return;
22394         }
22395         
22396         this.fireEvent("load", this, o, o.request.arg);
22397         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22398     },
22399
22400     // private
22401     update : function(dataSet){
22402
22403     },
22404
22405     // private
22406     updateResponse : function(dataSet){
22407
22408     }
22409 });/*
22410  * Based on:
22411  * Ext JS Library 1.1.1
22412  * Copyright(c) 2006-2007, Ext JS, LLC.
22413  *
22414  * Originally Released Under LGPL - original licence link has changed is not relivant.
22415  *
22416  * Fork - LGPL
22417  * <script type="text/javascript">
22418  */
22419
22420 /**
22421  * @class Roo.data.ScriptTagProxy
22422  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22423  * other than the originating domain of the running page.<br><br>
22424  * <p>
22425  * <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
22426  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22427  * <p>
22428  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22429  * source code that is used as the source inside a &lt;script> tag.<br><br>
22430  * <p>
22431  * In order for the browser to process the returned data, the server must wrap the data object
22432  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22433  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22434  * depending on whether the callback name was passed:
22435  * <p>
22436  * <pre><code>
22437 boolean scriptTag = false;
22438 String cb = request.getParameter("callback");
22439 if (cb != null) {
22440     scriptTag = true;
22441     response.setContentType("text/javascript");
22442 } else {
22443     response.setContentType("application/x-json");
22444 }
22445 Writer out = response.getWriter();
22446 if (scriptTag) {
22447     out.write(cb + "(");
22448 }
22449 out.print(dataBlock.toJsonString());
22450 if (scriptTag) {
22451     out.write(");");
22452 }
22453 </pre></code>
22454  *
22455  * @constructor
22456  * @param {Object} config A configuration object.
22457  */
22458 Roo.data.ScriptTagProxy = function(config){
22459     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22460     Roo.apply(this, config);
22461     this.head = document.getElementsByTagName("head")[0];
22462 };
22463
22464 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22465
22466 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22467     /**
22468      * @cfg {String} url The URL from which to request the data object.
22469      */
22470     /**
22471      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22472      */
22473     timeout : 30000,
22474     /**
22475      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22476      * the server the name of the callback function set up by the load call to process the returned data object.
22477      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22478      * javascript output which calls this named function passing the data object as its only parameter.
22479      */
22480     callbackParam : "callback",
22481     /**
22482      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22483      * name to the request.
22484      */
22485     nocache : true,
22486
22487     /**
22488      * Load data from the configured URL, read the data object into
22489      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22490      * process that block using the passed callback.
22491      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22492      * for the request to the remote server.
22493      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22494      * object into a block of Roo.data.Records.
22495      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22496      * The function must be passed <ul>
22497      * <li>The Record block object</li>
22498      * <li>The "arg" argument from the load function</li>
22499      * <li>A boolean success indicator</li>
22500      * </ul>
22501      * @param {Object} scope The scope in which to call the callback
22502      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22503      */
22504     load : function(params, reader, callback, scope, arg){
22505         if(this.fireEvent("beforeload", this, params) !== false){
22506
22507             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22508
22509             var url = this.url;
22510             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22511             if(this.nocache){
22512                 url += "&_dc=" + (new Date().getTime());
22513             }
22514             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22515             var trans = {
22516                 id : transId,
22517                 cb : "stcCallback"+transId,
22518                 scriptId : "stcScript"+transId,
22519                 params : params,
22520                 arg : arg,
22521                 url : url,
22522                 callback : callback,
22523                 scope : scope,
22524                 reader : reader
22525             };
22526             var conn = this;
22527
22528             window[trans.cb] = function(o){
22529                 conn.handleResponse(o, trans);
22530             };
22531
22532             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22533
22534             if(this.autoAbort !== false){
22535                 this.abort();
22536             }
22537
22538             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22539
22540             var script = document.createElement("script");
22541             script.setAttribute("src", url);
22542             script.setAttribute("type", "text/javascript");
22543             script.setAttribute("id", trans.scriptId);
22544             this.head.appendChild(script);
22545
22546             this.trans = trans;
22547         }else{
22548             callback.call(scope||this, null, arg, false);
22549         }
22550     },
22551
22552     // private
22553     isLoading : function(){
22554         return this.trans ? true : false;
22555     },
22556
22557     /**
22558      * Abort the current server request.
22559      */
22560     abort : function(){
22561         if(this.isLoading()){
22562             this.destroyTrans(this.trans);
22563         }
22564     },
22565
22566     // private
22567     destroyTrans : function(trans, isLoaded){
22568         this.head.removeChild(document.getElementById(trans.scriptId));
22569         clearTimeout(trans.timeoutId);
22570         if(isLoaded){
22571             window[trans.cb] = undefined;
22572             try{
22573                 delete window[trans.cb];
22574             }catch(e){}
22575         }else{
22576             // if hasn't been loaded, wait for load to remove it to prevent script error
22577             window[trans.cb] = function(){
22578                 window[trans.cb] = undefined;
22579                 try{
22580                     delete window[trans.cb];
22581                 }catch(e){}
22582             };
22583         }
22584     },
22585
22586     // private
22587     handleResponse : function(o, trans){
22588         this.trans = false;
22589         this.destroyTrans(trans, true);
22590         var result;
22591         try {
22592             result = trans.reader.readRecords(o);
22593         }catch(e){
22594             this.fireEvent("loadexception", this, o, trans.arg, e);
22595             trans.callback.call(trans.scope||window, null, trans.arg, false);
22596             return;
22597         }
22598         this.fireEvent("load", this, o, trans.arg);
22599         trans.callback.call(trans.scope||window, result, trans.arg, true);
22600     },
22601
22602     // private
22603     handleFailure : function(trans){
22604         this.trans = false;
22605         this.destroyTrans(trans, false);
22606         this.fireEvent("loadexception", this, null, trans.arg);
22607         trans.callback.call(trans.scope||window, null, trans.arg, false);
22608     }
22609 });/*
22610  * Based on:
22611  * Ext JS Library 1.1.1
22612  * Copyright(c) 2006-2007, Ext JS, LLC.
22613  *
22614  * Originally Released Under LGPL - original licence link has changed is not relivant.
22615  *
22616  * Fork - LGPL
22617  * <script type="text/javascript">
22618  */
22619
22620 /**
22621  * @class Roo.data.JsonReader
22622  * @extends Roo.data.DataReader
22623  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22624  * based on mappings in a provided Roo.data.Record constructor.
22625  * 
22626  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22627  * in the reply previously. 
22628  * 
22629  * <p>
22630  * Example code:
22631  * <pre><code>
22632 var RecordDef = Roo.data.Record.create([
22633     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22634     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22635 ]);
22636 var myReader = new Roo.data.JsonReader({
22637     totalProperty: "results",    // The property which contains the total dataset size (optional)
22638     root: "rows",                // The property which contains an Array of row objects
22639     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22640 }, RecordDef);
22641 </code></pre>
22642  * <p>
22643  * This would consume a JSON file like this:
22644  * <pre><code>
22645 { 'results': 2, 'rows': [
22646     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22647     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22648 }
22649 </code></pre>
22650  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22651  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22652  * paged from the remote server.
22653  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22654  * @cfg {String} root name of the property which contains the Array of row objects.
22655  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22656  * @cfg {Array} fields Array of field definition objects
22657  * @constructor
22658  * Create a new JsonReader
22659  * @param {Object} meta Metadata configuration options
22660  * @param {Object} recordType Either an Array of field definition objects,
22661  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22662  */
22663 Roo.data.JsonReader = function(meta, recordType){
22664     
22665     meta = meta || {};
22666     // set some defaults:
22667     Roo.applyIf(meta, {
22668         totalProperty: 'total',
22669         successProperty : 'success',
22670         root : 'data',
22671         id : 'id'
22672     });
22673     
22674     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22675 };
22676 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22677     
22678     /**
22679      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22680      * Used by Store query builder to append _requestMeta to params.
22681      * 
22682      */
22683     metaFromRemote : false,
22684     /**
22685      * This method is only used by a DataProxy which has retrieved data from a remote server.
22686      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22687      * @return {Object} data A data block which is used by an Roo.data.Store object as
22688      * a cache of Roo.data.Records.
22689      */
22690     read : function(response){
22691         var json = response.responseText;
22692        
22693         var o = /* eval:var:o */ eval("("+json+")");
22694         if(!o) {
22695             throw {message: "JsonReader.read: Json object not found"};
22696         }
22697         
22698         if(o.metaData){
22699             
22700             delete this.ef;
22701             this.metaFromRemote = true;
22702             this.meta = o.metaData;
22703             this.recordType = Roo.data.Record.create(o.metaData.fields);
22704             this.onMetaChange(this.meta, this.recordType, o);
22705         }
22706         return this.readRecords(o);
22707     },
22708
22709     // private function a store will implement
22710     onMetaChange : function(meta, recordType, o){
22711
22712     },
22713
22714     /**
22715          * @ignore
22716          */
22717     simpleAccess: function(obj, subsc) {
22718         return obj[subsc];
22719     },
22720
22721         /**
22722          * @ignore
22723          */
22724     getJsonAccessor: function(){
22725         var re = /[\[\.]/;
22726         return function(expr) {
22727             try {
22728                 return(re.test(expr))
22729                     ? new Function("obj", "return obj." + expr)
22730                     : function(obj){
22731                         return obj[expr];
22732                     };
22733             } catch(e){}
22734             return Roo.emptyFn;
22735         };
22736     }(),
22737
22738     /**
22739      * Create a data block containing Roo.data.Records from an XML document.
22740      * @param {Object} o An object which contains an Array of row objects in the property specified
22741      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22742      * which contains the total size of the dataset.
22743      * @return {Object} data A data block which is used by an Roo.data.Store object as
22744      * a cache of Roo.data.Records.
22745      */
22746     readRecords : function(o){
22747         /**
22748          * After any data loads, the raw JSON data is available for further custom processing.
22749          * @type Object
22750          */
22751         this.o = o;
22752         var s = this.meta, Record = this.recordType,
22753             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
22754
22755 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22756         if (!this.ef) {
22757             if(s.totalProperty) {
22758                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22759                 }
22760                 if(s.successProperty) {
22761                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22762                 }
22763                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22764                 if (s.id) {
22765                         var g = this.getJsonAccessor(s.id);
22766                         this.getId = function(rec) {
22767                                 var r = g(rec);  
22768                                 return (r === undefined || r === "") ? null : r;
22769                         };
22770                 } else {
22771                         this.getId = function(){return null;};
22772                 }
22773             this.ef = [];
22774             for(var jj = 0; jj < fl; jj++){
22775                 f = fi[jj];
22776                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22777                 this.ef[jj] = this.getJsonAccessor(map);
22778             }
22779         }
22780
22781         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22782         if(s.totalProperty){
22783             var vt = parseInt(this.getTotal(o), 10);
22784             if(!isNaN(vt)){
22785                 totalRecords = vt;
22786             }
22787         }
22788         if(s.successProperty){
22789             var vs = this.getSuccess(o);
22790             if(vs === false || vs === 'false'){
22791                 success = false;
22792             }
22793         }
22794         var records = [];
22795         for(var i = 0; i < c; i++){
22796                 var n = root[i];
22797             var values = {};
22798             var id = this.getId(n);
22799             for(var j = 0; j < fl; j++){
22800                 f = fi[j];
22801             var v = this.ef[j](n);
22802             if (!f.convert) {
22803                 Roo.log('missing convert for ' + f.name);
22804                 Roo.log(f);
22805                 continue;
22806             }
22807             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22808             }
22809             var record = new Record(values, id);
22810             record.json = n;
22811             records[i] = record;
22812         }
22813         return {
22814             raw : o,
22815             success : success,
22816             records : records,
22817             totalRecords : totalRecords
22818         };
22819     }
22820 });/*
22821  * Based on:
22822  * Ext JS Library 1.1.1
22823  * Copyright(c) 2006-2007, Ext JS, LLC.
22824  *
22825  * Originally Released Under LGPL - original licence link has changed is not relivant.
22826  *
22827  * Fork - LGPL
22828  * <script type="text/javascript">
22829  */
22830
22831 /**
22832  * @class Roo.data.XmlReader
22833  * @extends Roo.data.DataReader
22834  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22835  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22836  * <p>
22837  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22838  * header in the HTTP response must be set to "text/xml".</em>
22839  * <p>
22840  * Example code:
22841  * <pre><code>
22842 var RecordDef = Roo.data.Record.create([
22843    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22844    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22845 ]);
22846 var myReader = new Roo.data.XmlReader({
22847    totalRecords: "results", // The element which contains the total dataset size (optional)
22848    record: "row",           // The repeated element which contains row information
22849    id: "id"                 // The element within the row that provides an ID for the record (optional)
22850 }, RecordDef);
22851 </code></pre>
22852  * <p>
22853  * This would consume an XML file like this:
22854  * <pre><code>
22855 &lt;?xml?>
22856 &lt;dataset>
22857  &lt;results>2&lt;/results>
22858  &lt;row>
22859    &lt;id>1&lt;/id>
22860    &lt;name>Bill&lt;/name>
22861    &lt;occupation>Gardener&lt;/occupation>
22862  &lt;/row>
22863  &lt;row>
22864    &lt;id>2&lt;/id>
22865    &lt;name>Ben&lt;/name>
22866    &lt;occupation>Horticulturalist&lt;/occupation>
22867  &lt;/row>
22868 &lt;/dataset>
22869 </code></pre>
22870  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22871  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22872  * paged from the remote server.
22873  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22874  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22875  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22876  * a record identifier value.
22877  * @constructor
22878  * Create a new XmlReader
22879  * @param {Object} meta Metadata configuration options
22880  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22881  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22882  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22883  */
22884 Roo.data.XmlReader = function(meta, recordType){
22885     meta = meta || {};
22886     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22887 };
22888 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22889     /**
22890      * This method is only used by a DataProxy which has retrieved data from a remote server.
22891          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22892          * to contain a method called 'responseXML' that returns an XML document object.
22893      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22894      * a cache of Roo.data.Records.
22895      */
22896     read : function(response){
22897         var doc = response.responseXML;
22898         if(!doc) {
22899             throw {message: "XmlReader.read: XML Document not available"};
22900         }
22901         return this.readRecords(doc);
22902     },
22903
22904     /**
22905      * Create a data block containing Roo.data.Records from an XML document.
22906          * @param {Object} doc A parsed XML document.
22907      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22908      * a cache of Roo.data.Records.
22909      */
22910     readRecords : function(doc){
22911         /**
22912          * After any data loads/reads, the raw XML Document is available for further custom processing.
22913          * @type XMLDocument
22914          */
22915         this.xmlData = doc;
22916         var root = doc.documentElement || doc;
22917         var q = Roo.DomQuery;
22918         var recordType = this.recordType, fields = recordType.prototype.fields;
22919         var sid = this.meta.id;
22920         var totalRecords = 0, success = true;
22921         if(this.meta.totalRecords){
22922             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22923         }
22924         
22925         if(this.meta.success){
22926             var sv = q.selectValue(this.meta.success, root, true);
22927             success = sv !== false && sv !== 'false';
22928         }
22929         var records = [];
22930         var ns = q.select(this.meta.record, root);
22931         for(var i = 0, len = ns.length; i < len; i++) {
22932                 var n = ns[i];
22933                 var values = {};
22934                 var id = sid ? q.selectValue(sid, n) : undefined;
22935                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22936                     var f = fields.items[j];
22937                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22938                     v = f.convert(v);
22939                     values[f.name] = v;
22940                 }
22941                 var record = new recordType(values, id);
22942                 record.node = n;
22943                 records[records.length] = record;
22944             }
22945
22946             return {
22947                 success : success,
22948                 records : records,
22949                 totalRecords : totalRecords || records.length
22950             };
22951     }
22952 });/*
22953  * Based on:
22954  * Ext JS Library 1.1.1
22955  * Copyright(c) 2006-2007, Ext JS, LLC.
22956  *
22957  * Originally Released Under LGPL - original licence link has changed is not relivant.
22958  *
22959  * Fork - LGPL
22960  * <script type="text/javascript">
22961  */
22962
22963 /**
22964  * @class Roo.data.ArrayReader
22965  * @extends Roo.data.DataReader
22966  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22967  * Each element of that Array represents a row of data fields. The
22968  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22969  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22970  * <p>
22971  * Example code:.
22972  * <pre><code>
22973 var RecordDef = Roo.data.Record.create([
22974     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22975     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22976 ]);
22977 var myReader = new Roo.data.ArrayReader({
22978     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22979 }, RecordDef);
22980 </code></pre>
22981  * <p>
22982  * This would consume an Array like this:
22983  * <pre><code>
22984 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22985   </code></pre>
22986  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22987  * @constructor
22988  * Create a new JsonReader
22989  * @param {Object} meta Metadata configuration options.
22990  * @param {Object} recordType Either an Array of field definition objects
22991  * as specified to {@link Roo.data.Record#create},
22992  * or an {@link Roo.data.Record} object
22993  * created using {@link Roo.data.Record#create}.
22994  */
22995 Roo.data.ArrayReader = function(meta, recordType){
22996     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22997 };
22998
22999 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
23000     /**
23001      * Create a data block containing Roo.data.Records from an XML document.
23002      * @param {Object} o An Array of row objects which represents the dataset.
23003      * @return {Object} data A data block which is used by an Roo.data.Store object as
23004      * a cache of Roo.data.Records.
23005      */
23006     readRecords : function(o){
23007         var sid = this.meta ? this.meta.id : null;
23008         var recordType = this.recordType, fields = recordType.prototype.fields;
23009         var records = [];
23010         var root = o;
23011             for(var i = 0; i < root.length; i++){
23012                     var n = root[i];
23013                 var values = {};
23014                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
23015                 for(var j = 0, jlen = fields.length; j < jlen; j++){
23016                 var f = fields.items[j];
23017                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
23018                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
23019                 v = f.convert(v);
23020                 values[f.name] = v;
23021             }
23022                 var record = new recordType(values, id);
23023                 record.json = n;
23024                 records[records.length] = record;
23025             }
23026             return {
23027                 records : records,
23028                 totalRecords : records.length
23029             };
23030     }
23031 });/*
23032  * Based on:
23033  * Ext JS Library 1.1.1
23034  * Copyright(c) 2006-2007, Ext JS, LLC.
23035  *
23036  * Originally Released Under LGPL - original licence link has changed is not relivant.
23037  *
23038  * Fork - LGPL
23039  * <script type="text/javascript">
23040  */
23041
23042
23043 /**
23044  * @class Roo.data.Tree
23045  * @extends Roo.util.Observable
23046  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
23047  * in the tree have most standard DOM functionality.
23048  * @constructor
23049  * @param {Node} root (optional) The root node
23050  */
23051 Roo.data.Tree = function(root){
23052    this.nodeHash = {};
23053    /**
23054     * The root node for this tree
23055     * @type Node
23056     */
23057    this.root = null;
23058    if(root){
23059        this.setRootNode(root);
23060    }
23061    this.addEvents({
23062        /**
23063         * @event append
23064         * Fires when a new child node is appended to a node in this tree.
23065         * @param {Tree} tree The owner tree
23066         * @param {Node} parent The parent node
23067         * @param {Node} node The newly appended node
23068         * @param {Number} index The index of the newly appended node
23069         */
23070        "append" : true,
23071        /**
23072         * @event remove
23073         * Fires when a child node is removed from a node in this tree.
23074         * @param {Tree} tree The owner tree
23075         * @param {Node} parent The parent node
23076         * @param {Node} node The child node removed
23077         */
23078        "remove" : true,
23079        /**
23080         * @event move
23081         * Fires when a node is moved to a new location in the tree
23082         * @param {Tree} tree The owner tree
23083         * @param {Node} node The node moved
23084         * @param {Node} oldParent The old parent of this node
23085         * @param {Node} newParent The new parent of this node
23086         * @param {Number} index The index it was moved to
23087         */
23088        "move" : true,
23089        /**
23090         * @event insert
23091         * Fires when a new child node is inserted in a node in this tree.
23092         * @param {Tree} tree The owner tree
23093         * @param {Node} parent The parent node
23094         * @param {Node} node The child node inserted
23095         * @param {Node} refNode The child node the node was inserted before
23096         */
23097        "insert" : true,
23098        /**
23099         * @event beforeappend
23100         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
23101         * @param {Tree} tree The owner tree
23102         * @param {Node} parent The parent node
23103         * @param {Node} node The child node to be appended
23104         */
23105        "beforeappend" : true,
23106        /**
23107         * @event beforeremove
23108         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
23109         * @param {Tree} tree The owner tree
23110         * @param {Node} parent The parent node
23111         * @param {Node} node The child node to be removed
23112         */
23113        "beforeremove" : true,
23114        /**
23115         * @event beforemove
23116         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
23117         * @param {Tree} tree The owner tree
23118         * @param {Node} node The node being moved
23119         * @param {Node} oldParent The parent of the node
23120         * @param {Node} newParent The new parent the node is moving to
23121         * @param {Number} index The index it is being moved to
23122         */
23123        "beforemove" : true,
23124        /**
23125         * @event beforeinsert
23126         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
23127         * @param {Tree} tree The owner tree
23128         * @param {Node} parent The parent node
23129         * @param {Node} node The child node to be inserted
23130         * @param {Node} refNode The child node the node is being inserted before
23131         */
23132        "beforeinsert" : true
23133    });
23134
23135     Roo.data.Tree.superclass.constructor.call(this);
23136 };
23137
23138 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
23139     pathSeparator: "/",
23140
23141     proxyNodeEvent : function(){
23142         return this.fireEvent.apply(this, arguments);
23143     },
23144
23145     /**
23146      * Returns the root node for this tree.
23147      * @return {Node}
23148      */
23149     getRootNode : function(){
23150         return this.root;
23151     },
23152
23153     /**
23154      * Sets the root node for this tree.
23155      * @param {Node} node
23156      * @return {Node}
23157      */
23158     setRootNode : function(node){
23159         this.root = node;
23160         node.ownerTree = this;
23161         node.isRoot = true;
23162         this.registerNode(node);
23163         return node;
23164     },
23165
23166     /**
23167      * Gets a node in this tree by its id.
23168      * @param {String} id
23169      * @return {Node}
23170      */
23171     getNodeById : function(id){
23172         return this.nodeHash[id];
23173     },
23174
23175     registerNode : function(node){
23176         this.nodeHash[node.id] = node;
23177     },
23178
23179     unregisterNode : function(node){
23180         delete this.nodeHash[node.id];
23181     },
23182
23183     toString : function(){
23184         return "[Tree"+(this.id?" "+this.id:"")+"]";
23185     }
23186 });
23187
23188 /**
23189  * @class Roo.data.Node
23190  * @extends Roo.util.Observable
23191  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23192  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23193  * @constructor
23194  * @param {Object} attributes The attributes/config for the node
23195  */
23196 Roo.data.Node = function(attributes){
23197     /**
23198      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23199      * @type {Object}
23200      */
23201     this.attributes = attributes || {};
23202     this.leaf = this.attributes.leaf;
23203     /**
23204      * The node id. @type String
23205      */
23206     this.id = this.attributes.id;
23207     if(!this.id){
23208         this.id = Roo.id(null, "ynode-");
23209         this.attributes.id = this.id;
23210     }
23211      
23212     
23213     /**
23214      * All child nodes of this node. @type Array
23215      */
23216     this.childNodes = [];
23217     if(!this.childNodes.indexOf){ // indexOf is a must
23218         this.childNodes.indexOf = function(o){
23219             for(var i = 0, len = this.length; i < len; i++){
23220                 if(this[i] == o) {
23221                     return i;
23222                 }
23223             }
23224             return -1;
23225         };
23226     }
23227     /**
23228      * The parent node for this node. @type Node
23229      */
23230     this.parentNode = null;
23231     /**
23232      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23233      */
23234     this.firstChild = null;
23235     /**
23236      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23237      */
23238     this.lastChild = null;
23239     /**
23240      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23241      */
23242     this.previousSibling = null;
23243     /**
23244      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23245      */
23246     this.nextSibling = null;
23247
23248     this.addEvents({
23249        /**
23250         * @event append
23251         * Fires when a new child node is appended
23252         * @param {Tree} tree The owner tree
23253         * @param {Node} this This node
23254         * @param {Node} node The newly appended node
23255         * @param {Number} index The index of the newly appended node
23256         */
23257        "append" : true,
23258        /**
23259         * @event remove
23260         * Fires when a child node is removed
23261         * @param {Tree} tree The owner tree
23262         * @param {Node} this This node
23263         * @param {Node} node The removed node
23264         */
23265        "remove" : true,
23266        /**
23267         * @event move
23268         * Fires when this node is moved to a new location in the tree
23269         * @param {Tree} tree The owner tree
23270         * @param {Node} this This node
23271         * @param {Node} oldParent The old parent of this node
23272         * @param {Node} newParent The new parent of this node
23273         * @param {Number} index The index it was moved to
23274         */
23275        "move" : true,
23276        /**
23277         * @event insert
23278         * Fires when a new child node is inserted.
23279         * @param {Tree} tree The owner tree
23280         * @param {Node} this This node
23281         * @param {Node} node The child node inserted
23282         * @param {Node} refNode The child node the node was inserted before
23283         */
23284        "insert" : true,
23285        /**
23286         * @event beforeappend
23287         * Fires before a new child is appended, return false to cancel the append.
23288         * @param {Tree} tree The owner tree
23289         * @param {Node} this This node
23290         * @param {Node} node The child node to be appended
23291         */
23292        "beforeappend" : true,
23293        /**
23294         * @event beforeremove
23295         * Fires before a child is removed, return false to cancel the remove.
23296         * @param {Tree} tree The owner tree
23297         * @param {Node} this This node
23298         * @param {Node} node The child node to be removed
23299         */
23300        "beforeremove" : true,
23301        /**
23302         * @event beforemove
23303         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23304         * @param {Tree} tree The owner tree
23305         * @param {Node} this This node
23306         * @param {Node} oldParent The parent of this node
23307         * @param {Node} newParent The new parent this node is moving to
23308         * @param {Number} index The index it is being moved to
23309         */
23310        "beforemove" : true,
23311        /**
23312         * @event beforeinsert
23313         * Fires before a new child is inserted, return false to cancel the insert.
23314         * @param {Tree} tree The owner tree
23315         * @param {Node} this This node
23316         * @param {Node} node The child node to be inserted
23317         * @param {Node} refNode The child node the node is being inserted before
23318         */
23319        "beforeinsert" : true
23320    });
23321     this.listeners = this.attributes.listeners;
23322     Roo.data.Node.superclass.constructor.call(this);
23323 };
23324
23325 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23326     fireEvent : function(evtName){
23327         // first do standard event for this node
23328         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23329             return false;
23330         }
23331         // then bubble it up to the tree if the event wasn't cancelled
23332         var ot = this.getOwnerTree();
23333         if(ot){
23334             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23335                 return false;
23336             }
23337         }
23338         return true;
23339     },
23340
23341     /**
23342      * Returns true if this node is a leaf
23343      * @return {Boolean}
23344      */
23345     isLeaf : function(){
23346         return this.leaf === true;
23347     },
23348
23349     // private
23350     setFirstChild : function(node){
23351         this.firstChild = node;
23352     },
23353
23354     //private
23355     setLastChild : function(node){
23356         this.lastChild = node;
23357     },
23358
23359
23360     /**
23361      * Returns true if this node is the last child of its parent
23362      * @return {Boolean}
23363      */
23364     isLast : function(){
23365        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23366     },
23367
23368     /**
23369      * Returns true if this node is the first child of its parent
23370      * @return {Boolean}
23371      */
23372     isFirst : function(){
23373        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23374     },
23375
23376     hasChildNodes : function(){
23377         return !this.isLeaf() && this.childNodes.length > 0;
23378     },
23379
23380     /**
23381      * Insert node(s) as the last child node of this node.
23382      * @param {Node/Array} node The node or Array of nodes to append
23383      * @return {Node} The appended node if single append, or null if an array was passed
23384      */
23385     appendChild : function(node){
23386         var multi = false;
23387         if(node instanceof Array){
23388             multi = node;
23389         }else if(arguments.length > 1){
23390             multi = arguments;
23391         }
23392         // if passed an array or multiple args do them one by one
23393         if(multi){
23394             for(var i = 0, len = multi.length; i < len; i++) {
23395                 this.appendChild(multi[i]);
23396             }
23397         }else{
23398             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23399                 return false;
23400             }
23401             var index = this.childNodes.length;
23402             var oldParent = node.parentNode;
23403             // it's a move, make sure we move it cleanly
23404             if(oldParent){
23405                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23406                     return false;
23407                 }
23408                 oldParent.removeChild(node);
23409             }
23410             index = this.childNodes.length;
23411             if(index == 0){
23412                 this.setFirstChild(node);
23413             }
23414             this.childNodes.push(node);
23415             node.parentNode = this;
23416             var ps = this.childNodes[index-1];
23417             if(ps){
23418                 node.previousSibling = ps;
23419                 ps.nextSibling = node;
23420             }else{
23421                 node.previousSibling = null;
23422             }
23423             node.nextSibling = null;
23424             this.setLastChild(node);
23425             node.setOwnerTree(this.getOwnerTree());
23426             this.fireEvent("append", this.ownerTree, this, node, index);
23427             if(oldParent){
23428                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23429             }
23430             return node;
23431         }
23432     },
23433
23434     /**
23435      * Removes a child node from this node.
23436      * @param {Node} node The node to remove
23437      * @return {Node} The removed node
23438      */
23439     removeChild : function(node){
23440         var index = this.childNodes.indexOf(node);
23441         if(index == -1){
23442             return false;
23443         }
23444         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23445             return false;
23446         }
23447
23448         // remove it from childNodes collection
23449         this.childNodes.splice(index, 1);
23450
23451         // update siblings
23452         if(node.previousSibling){
23453             node.previousSibling.nextSibling = node.nextSibling;
23454         }
23455         if(node.nextSibling){
23456             node.nextSibling.previousSibling = node.previousSibling;
23457         }
23458
23459         // update child refs
23460         if(this.firstChild == node){
23461             this.setFirstChild(node.nextSibling);
23462         }
23463         if(this.lastChild == node){
23464             this.setLastChild(node.previousSibling);
23465         }
23466
23467         node.setOwnerTree(null);
23468         // clear any references from the node
23469         node.parentNode = null;
23470         node.previousSibling = null;
23471         node.nextSibling = null;
23472         this.fireEvent("remove", this.ownerTree, this, node);
23473         return node;
23474     },
23475
23476     /**
23477      * Inserts the first node before the second node in this nodes childNodes collection.
23478      * @param {Node} node The node to insert
23479      * @param {Node} refNode The node to insert before (if null the node is appended)
23480      * @return {Node} The inserted node
23481      */
23482     insertBefore : function(node, refNode){
23483         if(!refNode){ // like standard Dom, refNode can be null for append
23484             return this.appendChild(node);
23485         }
23486         // nothing to do
23487         if(node == refNode){
23488             return false;
23489         }
23490
23491         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23492             return false;
23493         }
23494         var index = this.childNodes.indexOf(refNode);
23495         var oldParent = node.parentNode;
23496         var refIndex = index;
23497
23498         // when moving internally, indexes will change after remove
23499         if(oldParent == this && this.childNodes.indexOf(node) < index){
23500             refIndex--;
23501         }
23502
23503         // it's a move, make sure we move it cleanly
23504         if(oldParent){
23505             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23506                 return false;
23507             }
23508             oldParent.removeChild(node);
23509         }
23510         if(refIndex == 0){
23511             this.setFirstChild(node);
23512         }
23513         this.childNodes.splice(refIndex, 0, node);
23514         node.parentNode = this;
23515         var ps = this.childNodes[refIndex-1];
23516         if(ps){
23517             node.previousSibling = ps;
23518             ps.nextSibling = node;
23519         }else{
23520             node.previousSibling = null;
23521         }
23522         node.nextSibling = refNode;
23523         refNode.previousSibling = node;
23524         node.setOwnerTree(this.getOwnerTree());
23525         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23526         if(oldParent){
23527             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23528         }
23529         return node;
23530     },
23531
23532     /**
23533      * Returns the child node at the specified index.
23534      * @param {Number} index
23535      * @return {Node}
23536      */
23537     item : function(index){
23538         return this.childNodes[index];
23539     },
23540
23541     /**
23542      * Replaces one child node in this node with another.
23543      * @param {Node} newChild The replacement node
23544      * @param {Node} oldChild The node to replace
23545      * @return {Node} The replaced node
23546      */
23547     replaceChild : function(newChild, oldChild){
23548         this.insertBefore(newChild, oldChild);
23549         this.removeChild(oldChild);
23550         return oldChild;
23551     },
23552
23553     /**
23554      * Returns the index of a child node
23555      * @param {Node} node
23556      * @return {Number} The index of the node or -1 if it was not found
23557      */
23558     indexOf : function(child){
23559         return this.childNodes.indexOf(child);
23560     },
23561
23562     /**
23563      * Returns the tree this node is in.
23564      * @return {Tree}
23565      */
23566     getOwnerTree : function(){
23567         // if it doesn't have one, look for one
23568         if(!this.ownerTree){
23569             var p = this;
23570             while(p){
23571                 if(p.ownerTree){
23572                     this.ownerTree = p.ownerTree;
23573                     break;
23574                 }
23575                 p = p.parentNode;
23576             }
23577         }
23578         return this.ownerTree;
23579     },
23580
23581     /**
23582      * Returns depth of this node (the root node has a depth of 0)
23583      * @return {Number}
23584      */
23585     getDepth : function(){
23586         var depth = 0;
23587         var p = this;
23588         while(p.parentNode){
23589             ++depth;
23590             p = p.parentNode;
23591         }
23592         return depth;
23593     },
23594
23595     // private
23596     setOwnerTree : function(tree){
23597         // if it's move, we need to update everyone
23598         if(tree != this.ownerTree){
23599             if(this.ownerTree){
23600                 this.ownerTree.unregisterNode(this);
23601             }
23602             this.ownerTree = tree;
23603             var cs = this.childNodes;
23604             for(var i = 0, len = cs.length; i < len; i++) {
23605                 cs[i].setOwnerTree(tree);
23606             }
23607             if(tree){
23608                 tree.registerNode(this);
23609             }
23610         }
23611     },
23612
23613     /**
23614      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23615      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23616      * @return {String} The path
23617      */
23618     getPath : function(attr){
23619         attr = attr || "id";
23620         var p = this.parentNode;
23621         var b = [this.attributes[attr]];
23622         while(p){
23623             b.unshift(p.attributes[attr]);
23624             p = p.parentNode;
23625         }
23626         var sep = this.getOwnerTree().pathSeparator;
23627         return sep + b.join(sep);
23628     },
23629
23630     /**
23631      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23632      * function call will be the scope provided or the current node. The arguments to the function
23633      * will be the args provided or the current node. If the function returns false at any point,
23634      * the bubble is stopped.
23635      * @param {Function} fn The function to call
23636      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23637      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23638      */
23639     bubble : function(fn, scope, args){
23640         var p = this;
23641         while(p){
23642             if(fn.call(scope || p, args || p) === false){
23643                 break;
23644             }
23645             p = p.parentNode;
23646         }
23647     },
23648
23649     /**
23650      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23651      * function call will be the scope provided or the current node. The arguments to the function
23652      * will be the args provided or the current node. If the function returns false at any point,
23653      * the cascade is stopped on that branch.
23654      * @param {Function} fn The function to call
23655      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23656      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23657      */
23658     cascade : function(fn, scope, args){
23659         if(fn.call(scope || this, args || this) !== false){
23660             var cs = this.childNodes;
23661             for(var i = 0, len = cs.length; i < len; i++) {
23662                 cs[i].cascade(fn, scope, args);
23663             }
23664         }
23665     },
23666
23667     /**
23668      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23669      * function call will be the scope provided or the current node. The arguments to the function
23670      * will be the args provided or the current node. If the function returns false at any point,
23671      * the iteration stops.
23672      * @param {Function} fn The function to call
23673      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23674      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23675      */
23676     eachChild : function(fn, scope, args){
23677         var cs = this.childNodes;
23678         for(var i = 0, len = cs.length; i < len; i++) {
23679                 if(fn.call(scope || this, args || cs[i]) === false){
23680                     break;
23681                 }
23682         }
23683     },
23684
23685     /**
23686      * Finds the first child that has the attribute with the specified value.
23687      * @param {String} attribute The attribute name
23688      * @param {Mixed} value The value to search for
23689      * @return {Node} The found child or null if none was found
23690      */
23691     findChild : function(attribute, value){
23692         var cs = this.childNodes;
23693         for(var i = 0, len = cs.length; i < len; i++) {
23694                 if(cs[i].attributes[attribute] == value){
23695                     return cs[i];
23696                 }
23697         }
23698         return null;
23699     },
23700
23701     /**
23702      * Finds the first child by a custom function. The child matches if the function passed
23703      * returns true.
23704      * @param {Function} fn
23705      * @param {Object} scope (optional)
23706      * @return {Node} The found child or null if none was found
23707      */
23708     findChildBy : function(fn, scope){
23709         var cs = this.childNodes;
23710         for(var i = 0, len = cs.length; i < len; i++) {
23711                 if(fn.call(scope||cs[i], cs[i]) === true){
23712                     return cs[i];
23713                 }
23714         }
23715         return null;
23716     },
23717
23718     /**
23719      * Sorts this nodes children using the supplied sort function
23720      * @param {Function} fn
23721      * @param {Object} scope (optional)
23722      */
23723     sort : function(fn, scope){
23724         var cs = this.childNodes;
23725         var len = cs.length;
23726         if(len > 0){
23727             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23728             cs.sort(sortFn);
23729             for(var i = 0; i < len; i++){
23730                 var n = cs[i];
23731                 n.previousSibling = cs[i-1];
23732                 n.nextSibling = cs[i+1];
23733                 if(i == 0){
23734                     this.setFirstChild(n);
23735                 }
23736                 if(i == len-1){
23737                     this.setLastChild(n);
23738                 }
23739             }
23740         }
23741     },
23742
23743     /**
23744      * Returns true if this node is an ancestor (at any point) of the passed node.
23745      * @param {Node} node
23746      * @return {Boolean}
23747      */
23748     contains : function(node){
23749         return node.isAncestor(this);
23750     },
23751
23752     /**
23753      * Returns true if the passed node is an ancestor (at any point) of this node.
23754      * @param {Node} node
23755      * @return {Boolean}
23756      */
23757     isAncestor : function(node){
23758         var p = this.parentNode;
23759         while(p){
23760             if(p == node){
23761                 return true;
23762             }
23763             p = p.parentNode;
23764         }
23765         return false;
23766     },
23767
23768     toString : function(){
23769         return "[Node"+(this.id?" "+this.id:"")+"]";
23770     }
23771 });/*
23772  * Based on:
23773  * Ext JS Library 1.1.1
23774  * Copyright(c) 2006-2007, Ext JS, LLC.
23775  *
23776  * Originally Released Under LGPL - original licence link has changed is not relivant.
23777  *
23778  * Fork - LGPL
23779  * <script type="text/javascript">
23780  */
23781  (function(){ 
23782 /**
23783  * @class Roo.Layer
23784  * @extends Roo.Element
23785  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23786  * automatic maintaining of shadow/shim positions.
23787  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23788  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23789  * you can pass a string with a CSS class name. False turns off the shadow.
23790  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23791  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23792  * @cfg {String} cls CSS class to add to the element
23793  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23794  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23795  * @constructor
23796  * @param {Object} config An object with config options.
23797  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23798  */
23799
23800 Roo.Layer = function(config, existingEl){
23801     config = config || {};
23802     var dh = Roo.DomHelper;
23803     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23804     if(existingEl){
23805         this.dom = Roo.getDom(existingEl);
23806     }
23807     if(!this.dom){
23808         var o = config.dh || {tag: "div", cls: "x-layer"};
23809         this.dom = dh.append(pel, o);
23810     }
23811     if(config.cls){
23812         this.addClass(config.cls);
23813     }
23814     this.constrain = config.constrain !== false;
23815     this.visibilityMode = Roo.Element.VISIBILITY;
23816     if(config.id){
23817         this.id = this.dom.id = config.id;
23818     }else{
23819         this.id = Roo.id(this.dom);
23820     }
23821     this.zindex = config.zindex || this.getZIndex();
23822     this.position("absolute", this.zindex);
23823     if(config.shadow){
23824         this.shadowOffset = config.shadowOffset || 4;
23825         this.shadow = new Roo.Shadow({
23826             offset : this.shadowOffset,
23827             mode : config.shadow
23828         });
23829     }else{
23830         this.shadowOffset = 0;
23831     }
23832     this.useShim = config.shim !== false && Roo.useShims;
23833     this.useDisplay = config.useDisplay;
23834     this.hide();
23835 };
23836
23837 var supr = Roo.Element.prototype;
23838
23839 // shims are shared among layer to keep from having 100 iframes
23840 var shims = [];
23841
23842 Roo.extend(Roo.Layer, Roo.Element, {
23843
23844     getZIndex : function(){
23845         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23846     },
23847
23848     getShim : function(){
23849         if(!this.useShim){
23850             return null;
23851         }
23852         if(this.shim){
23853             return this.shim;
23854         }
23855         var shim = shims.shift();
23856         if(!shim){
23857             shim = this.createShim();
23858             shim.enableDisplayMode('block');
23859             shim.dom.style.display = 'none';
23860             shim.dom.style.visibility = 'visible';
23861         }
23862         var pn = this.dom.parentNode;
23863         if(shim.dom.parentNode != pn){
23864             pn.insertBefore(shim.dom, this.dom);
23865         }
23866         shim.setStyle('z-index', this.getZIndex()-2);
23867         this.shim = shim;
23868         return shim;
23869     },
23870
23871     hideShim : function(){
23872         if(this.shim){
23873             this.shim.setDisplayed(false);
23874             shims.push(this.shim);
23875             delete this.shim;
23876         }
23877     },
23878
23879     disableShadow : function(){
23880         if(this.shadow){
23881             this.shadowDisabled = true;
23882             this.shadow.hide();
23883             this.lastShadowOffset = this.shadowOffset;
23884             this.shadowOffset = 0;
23885         }
23886     },
23887
23888     enableShadow : function(show){
23889         if(this.shadow){
23890             this.shadowDisabled = false;
23891             this.shadowOffset = this.lastShadowOffset;
23892             delete this.lastShadowOffset;
23893             if(show){
23894                 this.sync(true);
23895             }
23896         }
23897     },
23898
23899     // private
23900     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23901     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23902     sync : function(doShow){
23903         var sw = this.shadow;
23904         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23905             var sh = this.getShim();
23906
23907             var w = this.getWidth(),
23908                 h = this.getHeight();
23909
23910             var l = this.getLeft(true),
23911                 t = this.getTop(true);
23912
23913             if(sw && !this.shadowDisabled){
23914                 if(doShow && !sw.isVisible()){
23915                     sw.show(this);
23916                 }else{
23917                     sw.realign(l, t, w, h);
23918                 }
23919                 if(sh){
23920                     if(doShow){
23921                        sh.show();
23922                     }
23923                     // fit the shim behind the shadow, so it is shimmed too
23924                     var a = sw.adjusts, s = sh.dom.style;
23925                     s.left = (Math.min(l, l+a.l))+"px";
23926                     s.top = (Math.min(t, t+a.t))+"px";
23927                     s.width = (w+a.w)+"px";
23928                     s.height = (h+a.h)+"px";
23929                 }
23930             }else if(sh){
23931                 if(doShow){
23932                    sh.show();
23933                 }
23934                 sh.setSize(w, h);
23935                 sh.setLeftTop(l, t);
23936             }
23937             
23938         }
23939     },
23940
23941     // private
23942     destroy : function(){
23943         this.hideShim();
23944         if(this.shadow){
23945             this.shadow.hide();
23946         }
23947         this.removeAllListeners();
23948         var pn = this.dom.parentNode;
23949         if(pn){
23950             pn.removeChild(this.dom);
23951         }
23952         Roo.Element.uncache(this.id);
23953     },
23954
23955     remove : function(){
23956         this.destroy();
23957     },
23958
23959     // private
23960     beginUpdate : function(){
23961         this.updating = true;
23962     },
23963
23964     // private
23965     endUpdate : function(){
23966         this.updating = false;
23967         this.sync(true);
23968     },
23969
23970     // private
23971     hideUnders : function(negOffset){
23972         if(this.shadow){
23973             this.shadow.hide();
23974         }
23975         this.hideShim();
23976     },
23977
23978     // private
23979     constrainXY : function(){
23980         if(this.constrain){
23981             var vw = Roo.lib.Dom.getViewWidth(),
23982                 vh = Roo.lib.Dom.getViewHeight();
23983             var s = Roo.get(document).getScroll();
23984
23985             var xy = this.getXY();
23986             var x = xy[0], y = xy[1];   
23987             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23988             // only move it if it needs it
23989             var moved = false;
23990             // first validate right/bottom
23991             if((x + w) > vw+s.left){
23992                 x = vw - w - this.shadowOffset;
23993                 moved = true;
23994             }
23995             if((y + h) > vh+s.top){
23996                 y = vh - h - this.shadowOffset;
23997                 moved = true;
23998             }
23999             // then make sure top/left isn't negative
24000             if(x < s.left){
24001                 x = s.left;
24002                 moved = true;
24003             }
24004             if(y < s.top){
24005                 y = s.top;
24006                 moved = true;
24007             }
24008             if(moved){
24009                 if(this.avoidY){
24010                     var ay = this.avoidY;
24011                     if(y <= ay && (y+h) >= ay){
24012                         y = ay-h-5;   
24013                     }
24014                 }
24015                 xy = [x, y];
24016                 this.storeXY(xy);
24017                 supr.setXY.call(this, xy);
24018                 this.sync();
24019             }
24020         }
24021     },
24022
24023     isVisible : function(){
24024         return this.visible;    
24025     },
24026
24027     // private
24028     showAction : function(){
24029         this.visible = true; // track visibility to prevent getStyle calls
24030         if(this.useDisplay === true){
24031             this.setDisplayed("");
24032         }else if(this.lastXY){
24033             supr.setXY.call(this, this.lastXY);
24034         }else if(this.lastLT){
24035             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
24036         }
24037     },
24038
24039     // private
24040     hideAction : function(){
24041         this.visible = false;
24042         if(this.useDisplay === true){
24043             this.setDisplayed(false);
24044         }else{
24045             this.setLeftTop(-10000,-10000);
24046         }
24047     },
24048
24049     // overridden Element method
24050     setVisible : function(v, a, d, c, e){
24051         if(v){
24052             this.showAction();
24053         }
24054         if(a && v){
24055             var cb = function(){
24056                 this.sync(true);
24057                 if(c){
24058                     c();
24059                 }
24060             }.createDelegate(this);
24061             supr.setVisible.call(this, true, true, d, cb, e);
24062         }else{
24063             if(!v){
24064                 this.hideUnders(true);
24065             }
24066             var cb = c;
24067             if(a){
24068                 cb = function(){
24069                     this.hideAction();
24070                     if(c){
24071                         c();
24072                     }
24073                 }.createDelegate(this);
24074             }
24075             supr.setVisible.call(this, v, a, d, cb, e);
24076             if(v){
24077                 this.sync(true);
24078             }else if(!a){
24079                 this.hideAction();
24080             }
24081         }
24082     },
24083
24084     storeXY : function(xy){
24085         delete this.lastLT;
24086         this.lastXY = xy;
24087     },
24088
24089     storeLeftTop : function(left, top){
24090         delete this.lastXY;
24091         this.lastLT = [left, top];
24092     },
24093
24094     // private
24095     beforeFx : function(){
24096         this.beforeAction();
24097         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
24098     },
24099
24100     // private
24101     afterFx : function(){
24102         Roo.Layer.superclass.afterFx.apply(this, arguments);
24103         this.sync(this.isVisible());
24104     },
24105
24106     // private
24107     beforeAction : function(){
24108         if(!this.updating && this.shadow){
24109             this.shadow.hide();
24110         }
24111     },
24112
24113     // overridden Element method
24114     setLeft : function(left){
24115         this.storeLeftTop(left, this.getTop(true));
24116         supr.setLeft.apply(this, arguments);
24117         this.sync();
24118     },
24119
24120     setTop : function(top){
24121         this.storeLeftTop(this.getLeft(true), top);
24122         supr.setTop.apply(this, arguments);
24123         this.sync();
24124     },
24125
24126     setLeftTop : function(left, top){
24127         this.storeLeftTop(left, top);
24128         supr.setLeftTop.apply(this, arguments);
24129         this.sync();
24130     },
24131
24132     setXY : function(xy, a, d, c, e){
24133         this.fixDisplay();
24134         this.beforeAction();
24135         this.storeXY(xy);
24136         var cb = this.createCB(c);
24137         supr.setXY.call(this, xy, a, d, cb, e);
24138         if(!a){
24139             cb();
24140         }
24141     },
24142
24143     // private
24144     createCB : function(c){
24145         var el = this;
24146         return function(){
24147             el.constrainXY();
24148             el.sync(true);
24149             if(c){
24150                 c();
24151             }
24152         };
24153     },
24154
24155     // overridden Element method
24156     setX : function(x, a, d, c, e){
24157         this.setXY([x, this.getY()], a, d, c, e);
24158     },
24159
24160     // overridden Element method
24161     setY : function(y, a, d, c, e){
24162         this.setXY([this.getX(), y], a, d, c, e);
24163     },
24164
24165     // overridden Element method
24166     setSize : function(w, h, a, d, c, e){
24167         this.beforeAction();
24168         var cb = this.createCB(c);
24169         supr.setSize.call(this, w, h, a, d, cb, e);
24170         if(!a){
24171             cb();
24172         }
24173     },
24174
24175     // overridden Element method
24176     setWidth : function(w, a, d, c, e){
24177         this.beforeAction();
24178         var cb = this.createCB(c);
24179         supr.setWidth.call(this, w, a, d, cb, e);
24180         if(!a){
24181             cb();
24182         }
24183     },
24184
24185     // overridden Element method
24186     setHeight : function(h, a, d, c, e){
24187         this.beforeAction();
24188         var cb = this.createCB(c);
24189         supr.setHeight.call(this, h, a, d, cb, e);
24190         if(!a){
24191             cb();
24192         }
24193     },
24194
24195     // overridden Element method
24196     setBounds : function(x, y, w, h, a, d, c, e){
24197         this.beforeAction();
24198         var cb = this.createCB(c);
24199         if(!a){
24200             this.storeXY([x, y]);
24201             supr.setXY.call(this, [x, y]);
24202             supr.setSize.call(this, w, h, a, d, cb, e);
24203             cb();
24204         }else{
24205             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24206         }
24207         return this;
24208     },
24209     
24210     /**
24211      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24212      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24213      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24214      * @param {Number} zindex The new z-index to set
24215      * @return {this} The Layer
24216      */
24217     setZIndex : function(zindex){
24218         this.zindex = zindex;
24219         this.setStyle("z-index", zindex + 2);
24220         if(this.shadow){
24221             this.shadow.setZIndex(zindex + 1);
24222         }
24223         if(this.shim){
24224             this.shim.setStyle("z-index", zindex);
24225         }
24226     }
24227 });
24228 })();/*
24229  * Based on:
24230  * Ext JS Library 1.1.1
24231  * Copyright(c) 2006-2007, Ext JS, LLC.
24232  *
24233  * Originally Released Under LGPL - original licence link has changed is not relivant.
24234  *
24235  * Fork - LGPL
24236  * <script type="text/javascript">
24237  */
24238
24239
24240 /**
24241  * @class Roo.Shadow
24242  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24243  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24244  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24245  * @constructor
24246  * Create a new Shadow
24247  * @param {Object} config The config object
24248  */
24249 Roo.Shadow = function(config){
24250     Roo.apply(this, config);
24251     if(typeof this.mode != "string"){
24252         this.mode = this.defaultMode;
24253     }
24254     var o = this.offset, a = {h: 0};
24255     var rad = Math.floor(this.offset/2);
24256     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24257         case "drop":
24258             a.w = 0;
24259             a.l = a.t = o;
24260             a.t -= 1;
24261             if(Roo.isIE){
24262                 a.l -= this.offset + rad;
24263                 a.t -= this.offset + rad;
24264                 a.w -= rad;
24265                 a.h -= rad;
24266                 a.t += 1;
24267             }
24268         break;
24269         case "sides":
24270             a.w = (o*2);
24271             a.l = -o;
24272             a.t = o-1;
24273             if(Roo.isIE){
24274                 a.l -= (this.offset - rad);
24275                 a.t -= this.offset + rad;
24276                 a.l += 1;
24277                 a.w -= (this.offset - rad)*2;
24278                 a.w -= rad + 1;
24279                 a.h -= 1;
24280             }
24281         break;
24282         case "frame":
24283             a.w = a.h = (o*2);
24284             a.l = a.t = -o;
24285             a.t += 1;
24286             a.h -= 2;
24287             if(Roo.isIE){
24288                 a.l -= (this.offset - rad);
24289                 a.t -= (this.offset - rad);
24290                 a.l += 1;
24291                 a.w -= (this.offset + rad + 1);
24292                 a.h -= (this.offset + rad);
24293                 a.h += 1;
24294             }
24295         break;
24296     };
24297
24298     this.adjusts = a;
24299 };
24300
24301 Roo.Shadow.prototype = {
24302     /**
24303      * @cfg {String} mode
24304      * The shadow display mode.  Supports the following options:<br />
24305      * sides: Shadow displays on both sides and bottom only<br />
24306      * frame: Shadow displays equally on all four sides<br />
24307      * drop: Traditional bottom-right drop shadow (default)
24308      */
24309     /**
24310      * @cfg {String} offset
24311      * The number of pixels to offset the shadow from the element (defaults to 4)
24312      */
24313     offset: 4,
24314
24315     // private
24316     defaultMode: "drop",
24317
24318     /**
24319      * Displays the shadow under the target element
24320      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24321      */
24322     show : function(target){
24323         target = Roo.get(target);
24324         if(!this.el){
24325             this.el = Roo.Shadow.Pool.pull();
24326             if(this.el.dom.nextSibling != target.dom){
24327                 this.el.insertBefore(target);
24328             }
24329         }
24330         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24331         if(Roo.isIE){
24332             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24333         }
24334         this.realign(
24335             target.getLeft(true),
24336             target.getTop(true),
24337             target.getWidth(),
24338             target.getHeight()
24339         );
24340         this.el.dom.style.display = "block";
24341     },
24342
24343     /**
24344      * Returns true if the shadow is visible, else false
24345      */
24346     isVisible : function(){
24347         return this.el ? true : false;  
24348     },
24349
24350     /**
24351      * Direct alignment when values are already available. Show must be called at least once before
24352      * calling this method to ensure it is initialized.
24353      * @param {Number} left The target element left position
24354      * @param {Number} top The target element top position
24355      * @param {Number} width The target element width
24356      * @param {Number} height The target element height
24357      */
24358     realign : function(l, t, w, h){
24359         if(!this.el){
24360             return;
24361         }
24362         var a = this.adjusts, d = this.el.dom, s = d.style;
24363         var iea = 0;
24364         s.left = (l+a.l)+"px";
24365         s.top = (t+a.t)+"px";
24366         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24367  
24368         if(s.width != sws || s.height != shs){
24369             s.width = sws;
24370             s.height = shs;
24371             if(!Roo.isIE){
24372                 var cn = d.childNodes;
24373                 var sww = Math.max(0, (sw-12))+"px";
24374                 cn[0].childNodes[1].style.width = sww;
24375                 cn[1].childNodes[1].style.width = sww;
24376                 cn[2].childNodes[1].style.width = sww;
24377                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24378             }
24379         }
24380     },
24381
24382     /**
24383      * Hides this shadow
24384      */
24385     hide : function(){
24386         if(this.el){
24387             this.el.dom.style.display = "none";
24388             Roo.Shadow.Pool.push(this.el);
24389             delete this.el;
24390         }
24391     },
24392
24393     /**
24394      * Adjust the z-index of this shadow
24395      * @param {Number} zindex The new z-index
24396      */
24397     setZIndex : function(z){
24398         this.zIndex = z;
24399         if(this.el){
24400             this.el.setStyle("z-index", z);
24401         }
24402     }
24403 };
24404
24405 // Private utility class that manages the internal Shadow cache
24406 Roo.Shadow.Pool = function(){
24407     var p = [];
24408     var markup = Roo.isIE ?
24409                  '<div class="x-ie-shadow"></div>' :
24410                  '<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>';
24411     return {
24412         pull : function(){
24413             var sh = p.shift();
24414             if(!sh){
24415                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24416                 sh.autoBoxAdjust = false;
24417             }
24418             return sh;
24419         },
24420
24421         push : function(sh){
24422             p.push(sh);
24423         }
24424     };
24425 }();/*
24426  * Based on:
24427  * Ext JS Library 1.1.1
24428  * Copyright(c) 2006-2007, Ext JS, LLC.
24429  *
24430  * Originally Released Under LGPL - original licence link has changed is not relivant.
24431  *
24432  * Fork - LGPL
24433  * <script type="text/javascript">
24434  */
24435
24436
24437 /**
24438  * @class Roo.SplitBar
24439  * @extends Roo.util.Observable
24440  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24441  * <br><br>
24442  * Usage:
24443  * <pre><code>
24444 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24445                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24446 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24447 split.minSize = 100;
24448 split.maxSize = 600;
24449 split.animate = true;
24450 split.on('moved', splitterMoved);
24451 </code></pre>
24452  * @constructor
24453  * Create a new SplitBar
24454  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24455  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24456  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24457  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24458                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24459                         position of the SplitBar).
24460  */
24461 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24462     
24463     /** @private */
24464     this.el = Roo.get(dragElement, true);
24465     this.el.dom.unselectable = "on";
24466     /** @private */
24467     this.resizingEl = Roo.get(resizingElement, true);
24468
24469     /**
24470      * @private
24471      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24472      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24473      * @type Number
24474      */
24475     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24476     
24477     /**
24478      * The minimum size of the resizing element. (Defaults to 0)
24479      * @type Number
24480      */
24481     this.minSize = 0;
24482     
24483     /**
24484      * The maximum size of the resizing element. (Defaults to 2000)
24485      * @type Number
24486      */
24487     this.maxSize = 2000;
24488     
24489     /**
24490      * Whether to animate the transition to the new size
24491      * @type Boolean
24492      */
24493     this.animate = false;
24494     
24495     /**
24496      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24497      * @type Boolean
24498      */
24499     this.useShim = false;
24500     
24501     /** @private */
24502     this.shim = null;
24503     
24504     if(!existingProxy){
24505         /** @private */
24506         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24507     }else{
24508         this.proxy = Roo.get(existingProxy).dom;
24509     }
24510     /** @private */
24511     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24512     
24513     /** @private */
24514     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24515     
24516     /** @private */
24517     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24518     
24519     /** @private */
24520     this.dragSpecs = {};
24521     
24522     /**
24523      * @private The adapter to use to positon and resize elements
24524      */
24525     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24526     this.adapter.init(this);
24527     
24528     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24529         /** @private */
24530         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24531         this.el.addClass("x-splitbar-h");
24532     }else{
24533         /** @private */
24534         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24535         this.el.addClass("x-splitbar-v");
24536     }
24537     
24538     this.addEvents({
24539         /**
24540          * @event resize
24541          * Fires when the splitter is moved (alias for {@link #event-moved})
24542          * @param {Roo.SplitBar} this
24543          * @param {Number} newSize the new width or height
24544          */
24545         "resize" : true,
24546         /**
24547          * @event moved
24548          * Fires when the splitter is moved
24549          * @param {Roo.SplitBar} this
24550          * @param {Number} newSize the new width or height
24551          */
24552         "moved" : true,
24553         /**
24554          * @event beforeresize
24555          * Fires before the splitter is dragged
24556          * @param {Roo.SplitBar} this
24557          */
24558         "beforeresize" : true,
24559
24560         "beforeapply" : true
24561     });
24562
24563     Roo.util.Observable.call(this);
24564 };
24565
24566 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24567     onStartProxyDrag : function(x, y){
24568         this.fireEvent("beforeresize", this);
24569         if(!this.overlay){
24570             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24571             o.unselectable();
24572             o.enableDisplayMode("block");
24573             // all splitbars share the same overlay
24574             Roo.SplitBar.prototype.overlay = o;
24575         }
24576         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24577         this.overlay.show();
24578         Roo.get(this.proxy).setDisplayed("block");
24579         var size = this.adapter.getElementSize(this);
24580         this.activeMinSize = this.getMinimumSize();;
24581         this.activeMaxSize = this.getMaximumSize();;
24582         var c1 = size - this.activeMinSize;
24583         var c2 = Math.max(this.activeMaxSize - size, 0);
24584         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24585             this.dd.resetConstraints();
24586             this.dd.setXConstraint(
24587                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24588                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24589             );
24590             this.dd.setYConstraint(0, 0);
24591         }else{
24592             this.dd.resetConstraints();
24593             this.dd.setXConstraint(0, 0);
24594             this.dd.setYConstraint(
24595                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24596                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24597             );
24598          }
24599         this.dragSpecs.startSize = size;
24600         this.dragSpecs.startPoint = [x, y];
24601         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24602     },
24603     
24604     /** 
24605      * @private Called after the drag operation by the DDProxy
24606      */
24607     onEndProxyDrag : function(e){
24608         Roo.get(this.proxy).setDisplayed(false);
24609         var endPoint = Roo.lib.Event.getXY(e);
24610         if(this.overlay){
24611             this.overlay.hide();
24612         }
24613         var newSize;
24614         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24615             newSize = this.dragSpecs.startSize + 
24616                 (this.placement == Roo.SplitBar.LEFT ?
24617                     endPoint[0] - this.dragSpecs.startPoint[0] :
24618                     this.dragSpecs.startPoint[0] - endPoint[0]
24619                 );
24620         }else{
24621             newSize = this.dragSpecs.startSize + 
24622                 (this.placement == Roo.SplitBar.TOP ?
24623                     endPoint[1] - this.dragSpecs.startPoint[1] :
24624                     this.dragSpecs.startPoint[1] - endPoint[1]
24625                 );
24626         }
24627         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24628         if(newSize != this.dragSpecs.startSize){
24629             if(this.fireEvent('beforeapply', this, newSize) !== false){
24630                 this.adapter.setElementSize(this, newSize);
24631                 this.fireEvent("moved", this, newSize);
24632                 this.fireEvent("resize", this, newSize);
24633             }
24634         }
24635     },
24636     
24637     /**
24638      * Get the adapter this SplitBar uses
24639      * @return The adapter object
24640      */
24641     getAdapter : function(){
24642         return this.adapter;
24643     },
24644     
24645     /**
24646      * Set the adapter this SplitBar uses
24647      * @param {Object} adapter A SplitBar adapter object
24648      */
24649     setAdapter : function(adapter){
24650         this.adapter = adapter;
24651         this.adapter.init(this);
24652     },
24653     
24654     /**
24655      * Gets the minimum size for the resizing element
24656      * @return {Number} The minimum size
24657      */
24658     getMinimumSize : function(){
24659         return this.minSize;
24660     },
24661     
24662     /**
24663      * Sets the minimum size for the resizing element
24664      * @param {Number} minSize The minimum size
24665      */
24666     setMinimumSize : function(minSize){
24667         this.minSize = minSize;
24668     },
24669     
24670     /**
24671      * Gets the maximum size for the resizing element
24672      * @return {Number} The maximum size
24673      */
24674     getMaximumSize : function(){
24675         return this.maxSize;
24676     },
24677     
24678     /**
24679      * Sets the maximum size for the resizing element
24680      * @param {Number} maxSize The maximum size
24681      */
24682     setMaximumSize : function(maxSize){
24683         this.maxSize = maxSize;
24684     },
24685     
24686     /**
24687      * Sets the initialize size for the resizing element
24688      * @param {Number} size The initial size
24689      */
24690     setCurrentSize : function(size){
24691         var oldAnimate = this.animate;
24692         this.animate = false;
24693         this.adapter.setElementSize(this, size);
24694         this.animate = oldAnimate;
24695     },
24696     
24697     /**
24698      * Destroy this splitbar. 
24699      * @param {Boolean} removeEl True to remove the element
24700      */
24701     destroy : function(removeEl){
24702         if(this.shim){
24703             this.shim.remove();
24704         }
24705         this.dd.unreg();
24706         this.proxy.parentNode.removeChild(this.proxy);
24707         if(removeEl){
24708             this.el.remove();
24709         }
24710     }
24711 });
24712
24713 /**
24714  * @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.
24715  */
24716 Roo.SplitBar.createProxy = function(dir){
24717     var proxy = new Roo.Element(document.createElement("div"));
24718     proxy.unselectable();
24719     var cls = 'x-splitbar-proxy';
24720     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24721     document.body.appendChild(proxy.dom);
24722     return proxy.dom;
24723 };
24724
24725 /** 
24726  * @class Roo.SplitBar.BasicLayoutAdapter
24727  * Default Adapter. It assumes the splitter and resizing element are not positioned
24728  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24729  */
24730 Roo.SplitBar.BasicLayoutAdapter = function(){
24731 };
24732
24733 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24734     // do nothing for now
24735     init : function(s){
24736     
24737     },
24738     /**
24739      * Called before drag operations to get the current size of the resizing element. 
24740      * @param {Roo.SplitBar} s The SplitBar using this adapter
24741      */
24742      getElementSize : function(s){
24743         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24744             return s.resizingEl.getWidth();
24745         }else{
24746             return s.resizingEl.getHeight();
24747         }
24748     },
24749     
24750     /**
24751      * Called after drag operations to set the size of the resizing element.
24752      * @param {Roo.SplitBar} s The SplitBar using this adapter
24753      * @param {Number} newSize The new size to set
24754      * @param {Function} onComplete A function to be invoked when resizing is complete
24755      */
24756     setElementSize : function(s, newSize, onComplete){
24757         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24758             if(!s.animate){
24759                 s.resizingEl.setWidth(newSize);
24760                 if(onComplete){
24761                     onComplete(s, newSize);
24762                 }
24763             }else{
24764                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24765             }
24766         }else{
24767             
24768             if(!s.animate){
24769                 s.resizingEl.setHeight(newSize);
24770                 if(onComplete){
24771                     onComplete(s, newSize);
24772                 }
24773             }else{
24774                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24775             }
24776         }
24777     }
24778 };
24779
24780 /** 
24781  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24782  * @extends Roo.SplitBar.BasicLayoutAdapter
24783  * Adapter that  moves the splitter element to align with the resized sizing element. 
24784  * Used with an absolute positioned SplitBar.
24785  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24786  * document.body, make sure you assign an id to the body element.
24787  */
24788 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24789     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24790     this.container = Roo.get(container);
24791 };
24792
24793 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24794     init : function(s){
24795         this.basic.init(s);
24796     },
24797     
24798     getElementSize : function(s){
24799         return this.basic.getElementSize(s);
24800     },
24801     
24802     setElementSize : function(s, newSize, onComplete){
24803         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24804     },
24805     
24806     moveSplitter : function(s){
24807         var yes = Roo.SplitBar;
24808         switch(s.placement){
24809             case yes.LEFT:
24810                 s.el.setX(s.resizingEl.getRight());
24811                 break;
24812             case yes.RIGHT:
24813                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24814                 break;
24815             case yes.TOP:
24816                 s.el.setY(s.resizingEl.getBottom());
24817                 break;
24818             case yes.BOTTOM:
24819                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24820                 break;
24821         }
24822     }
24823 };
24824
24825 /**
24826  * Orientation constant - Create a vertical SplitBar
24827  * @static
24828  * @type Number
24829  */
24830 Roo.SplitBar.VERTICAL = 1;
24831
24832 /**
24833  * Orientation constant - Create a horizontal SplitBar
24834  * @static
24835  * @type Number
24836  */
24837 Roo.SplitBar.HORIZONTAL = 2;
24838
24839 /**
24840  * Placement constant - The resizing element is to the left of the splitter element
24841  * @static
24842  * @type Number
24843  */
24844 Roo.SplitBar.LEFT = 1;
24845
24846 /**
24847  * Placement constant - The resizing element is to the right of the splitter element
24848  * @static
24849  * @type Number
24850  */
24851 Roo.SplitBar.RIGHT = 2;
24852
24853 /**
24854  * Placement constant - The resizing element is positioned above the splitter element
24855  * @static
24856  * @type Number
24857  */
24858 Roo.SplitBar.TOP = 3;
24859
24860 /**
24861  * Placement constant - The resizing element is positioned under splitter element
24862  * @static
24863  * @type Number
24864  */
24865 Roo.SplitBar.BOTTOM = 4;
24866 /*
24867  * Based on:
24868  * Ext JS Library 1.1.1
24869  * Copyright(c) 2006-2007, Ext JS, LLC.
24870  *
24871  * Originally Released Under LGPL - original licence link has changed is not relivant.
24872  *
24873  * Fork - LGPL
24874  * <script type="text/javascript">
24875  */
24876
24877 /**
24878  * @class Roo.View
24879  * @extends Roo.util.Observable
24880  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24881  * This class also supports single and multi selection modes. <br>
24882  * Create a data model bound view:
24883  <pre><code>
24884  var store = new Roo.data.Store(...);
24885
24886  var view = new Roo.View({
24887     el : "my-element",
24888     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24889  
24890     singleSelect: true,
24891     selectedClass: "ydataview-selected",
24892     store: store
24893  });
24894
24895  // listen for node click?
24896  view.on("click", function(vw, index, node, e){
24897  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24898  });
24899
24900  // load XML data
24901  dataModel.load("foobar.xml");
24902  </code></pre>
24903  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24904  * <br><br>
24905  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24906  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24907  * 
24908  * Note: old style constructor is still suported (container, template, config)
24909  * 
24910  * @constructor
24911  * Create a new View
24912  * @param {Object} config The config object
24913  * 
24914  */
24915 Roo.View = function(config, depreciated_tpl, depreciated_config){
24916     
24917     this.parent = false;
24918     
24919     if (typeof(depreciated_tpl) == 'undefined') {
24920         // new way.. - universal constructor.
24921         Roo.apply(this, config);
24922         this.el  = Roo.get(this.el);
24923     } else {
24924         // old format..
24925         this.el  = Roo.get(config);
24926         this.tpl = depreciated_tpl;
24927         Roo.apply(this, depreciated_config);
24928     }
24929     this.wrapEl  = this.el.wrap().wrap();
24930     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24931     
24932     
24933     if(typeof(this.tpl) == "string"){
24934         this.tpl = new Roo.Template(this.tpl);
24935     } else {
24936         // support xtype ctors..
24937         this.tpl = new Roo.factory(this.tpl, Roo);
24938     }
24939     
24940     
24941     this.tpl.compile();
24942     
24943     /** @private */
24944     this.addEvents({
24945         /**
24946          * @event beforeclick
24947          * Fires before a click is processed. Returns false to cancel the default action.
24948          * @param {Roo.View} this
24949          * @param {Number} index The index of the target node
24950          * @param {HTMLElement} node The target node
24951          * @param {Roo.EventObject} e The raw event object
24952          */
24953             "beforeclick" : true,
24954         /**
24955          * @event click
24956          * Fires when a template node is clicked.
24957          * @param {Roo.View} this
24958          * @param {Number} index The index of the target node
24959          * @param {HTMLElement} node The target node
24960          * @param {Roo.EventObject} e The raw event object
24961          */
24962             "click" : true,
24963         /**
24964          * @event dblclick
24965          * Fires when a template node is double clicked.
24966          * @param {Roo.View} this
24967          * @param {Number} index The index of the target node
24968          * @param {HTMLElement} node The target node
24969          * @param {Roo.EventObject} e The raw event object
24970          */
24971             "dblclick" : true,
24972         /**
24973          * @event contextmenu
24974          * Fires when a template node is right clicked.
24975          * @param {Roo.View} this
24976          * @param {Number} index The index of the target node
24977          * @param {HTMLElement} node The target node
24978          * @param {Roo.EventObject} e The raw event object
24979          */
24980             "contextmenu" : true,
24981         /**
24982          * @event selectionchange
24983          * Fires when the selected nodes change.
24984          * @param {Roo.View} this
24985          * @param {Array} selections Array of the selected nodes
24986          */
24987             "selectionchange" : true,
24988     
24989         /**
24990          * @event beforeselect
24991          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24992          * @param {Roo.View} this
24993          * @param {HTMLElement} node The node to be selected
24994          * @param {Array} selections Array of currently selected nodes
24995          */
24996             "beforeselect" : true,
24997         /**
24998          * @event preparedata
24999          * Fires on every row to render, to allow you to change the data.
25000          * @param {Roo.View} this
25001          * @param {Object} data to be rendered (change this)
25002          */
25003           "preparedata" : true
25004           
25005           
25006         });
25007
25008
25009
25010     this.el.on({
25011         "click": this.onClick,
25012         "dblclick": this.onDblClick,
25013         "contextmenu": this.onContextMenu,
25014         scope:this
25015     });
25016
25017     this.selections = [];
25018     this.nodes = [];
25019     this.cmp = new Roo.CompositeElementLite([]);
25020     if(this.store){
25021         this.store = Roo.factory(this.store, Roo.data);
25022         this.setStore(this.store, true);
25023     }
25024     
25025     if ( this.footer && this.footer.xtype) {
25026            
25027          var fctr = this.wrapEl.appendChild(document.createElement("div"));
25028         
25029         this.footer.dataSource = this.store;
25030         this.footer.container = fctr;
25031         this.footer = Roo.factory(this.footer, Roo);
25032         fctr.insertFirst(this.el);
25033         
25034         // this is a bit insane - as the paging toolbar seems to detach the el..
25035 //        dom.parentNode.parentNode.parentNode
25036          // they get detached?
25037     }
25038     
25039     
25040     Roo.View.superclass.constructor.call(this);
25041     
25042     
25043 };
25044
25045 Roo.extend(Roo.View, Roo.util.Observable, {
25046     
25047      /**
25048      * @cfg {Roo.data.Store} store Data store to load data from.
25049      */
25050     store : false,
25051     
25052     /**
25053      * @cfg {String|Roo.Element} el The container element.
25054      */
25055     el : '',
25056     
25057     /**
25058      * @cfg {String|Roo.Template} tpl The template used by this View 
25059      */
25060     tpl : false,
25061     /**
25062      * @cfg {String} dataName the named area of the template to use as the data area
25063      *                          Works with domtemplates roo-name="name"
25064      */
25065     dataName: false,
25066     /**
25067      * @cfg {String} selectedClass The css class to add to selected nodes
25068      */
25069     selectedClass : "x-view-selected",
25070      /**
25071      * @cfg {String} emptyText The empty text to show when nothing is loaded.
25072      */
25073     emptyText : "",
25074     
25075     /**
25076      * @cfg {String} text to display on mask (default Loading)
25077      */
25078     mask : false,
25079     /**
25080      * @cfg {Boolean} multiSelect Allow multiple selection
25081      */
25082     multiSelect : false,
25083     /**
25084      * @cfg {Boolean} singleSelect Allow single selection
25085      */
25086     singleSelect:  false,
25087     
25088     /**
25089      * @cfg {Boolean} toggleSelect - selecting 
25090      */
25091     toggleSelect : false,
25092     
25093     /**
25094      * @cfg {Boolean} tickable - selecting 
25095      */
25096     tickable : false,
25097     
25098     /**
25099      * Returns the element this view is bound to.
25100      * @return {Roo.Element}
25101      */
25102     getEl : function(){
25103         return this.wrapEl;
25104     },
25105     
25106     
25107
25108     /**
25109      * Refreshes the view. - called by datachanged on the store. - do not call directly.
25110      */
25111     refresh : function(){
25112         //Roo.log('refresh');
25113         var t = this.tpl;
25114         
25115         // if we are using something like 'domtemplate', then
25116         // the what gets used is:
25117         // t.applySubtemplate(NAME, data, wrapping data..)
25118         // the outer template then get' applied with
25119         //     the store 'extra data'
25120         // and the body get's added to the
25121         //      roo-name="data" node?
25122         //      <span class='roo-tpl-{name}'></span> ?????
25123         
25124         
25125         
25126         this.clearSelections();
25127         this.el.update("");
25128         var html = [];
25129         var records = this.store.getRange();
25130         if(records.length < 1) {
25131             
25132             // is this valid??  = should it render a template??
25133             
25134             this.el.update(this.emptyText);
25135             return;
25136         }
25137         var el = this.el;
25138         if (this.dataName) {
25139             this.el.update(t.apply(this.store.meta)); //????
25140             el = this.el.child('.roo-tpl-' + this.dataName);
25141         }
25142         
25143         for(var i = 0, len = records.length; i < len; i++){
25144             var data = this.prepareData(records[i].data, i, records[i]);
25145             this.fireEvent("preparedata", this, data, i, records[i]);
25146             
25147             var d = Roo.apply({}, data);
25148             
25149             if(this.tickable){
25150                 Roo.apply(d, {'roo-id' : Roo.id()});
25151                 
25152                 var _this = this;
25153             
25154                 Roo.each(this.parent.item, function(item){
25155                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
25156                         return;
25157                     }
25158                     Roo.apply(d, {'roo-data-checked' : 'checked'});
25159                 });
25160             }
25161             
25162             html[html.length] = Roo.util.Format.trim(
25163                 this.dataName ?
25164                     t.applySubtemplate(this.dataName, d, this.store.meta) :
25165                     t.apply(d)
25166             );
25167         }
25168         
25169         
25170         
25171         el.update(html.join(""));
25172         this.nodes = el.dom.childNodes;
25173         this.updateIndexes(0);
25174     },
25175     
25176
25177     /**
25178      * Function to override to reformat the data that is sent to
25179      * the template for each node.
25180      * DEPRICATED - use the preparedata event handler.
25181      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
25182      * a JSON object for an UpdateManager bound view).
25183      */
25184     prepareData : function(data, index, record)
25185     {
25186         this.fireEvent("preparedata", this, data, index, record);
25187         return data;
25188     },
25189
25190     onUpdate : function(ds, record){
25191         // Roo.log('on update');   
25192         this.clearSelections();
25193         var index = this.store.indexOf(record);
25194         var n = this.nodes[index];
25195         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25196         n.parentNode.removeChild(n);
25197         this.updateIndexes(index, index);
25198     },
25199
25200     
25201     
25202 // --------- FIXME     
25203     onAdd : function(ds, records, index)
25204     {
25205         //Roo.log(['on Add', ds, records, index] );        
25206         this.clearSelections();
25207         if(this.nodes.length == 0){
25208             this.refresh();
25209             return;
25210         }
25211         var n = this.nodes[index];
25212         for(var i = 0, len = records.length; i < len; i++){
25213             var d = this.prepareData(records[i].data, i, records[i]);
25214             if(n){
25215                 this.tpl.insertBefore(n, d);
25216             }else{
25217                 
25218                 this.tpl.append(this.el, d);
25219             }
25220         }
25221         this.updateIndexes(index);
25222     },
25223
25224     onRemove : function(ds, record, index){
25225        // Roo.log('onRemove');
25226         this.clearSelections();
25227         var el = this.dataName  ?
25228             this.el.child('.roo-tpl-' + this.dataName) :
25229             this.el; 
25230         
25231         el.dom.removeChild(this.nodes[index]);
25232         this.updateIndexes(index);
25233     },
25234
25235     /**
25236      * Refresh an individual node.
25237      * @param {Number} index
25238      */
25239     refreshNode : function(index){
25240         this.onUpdate(this.store, this.store.getAt(index));
25241     },
25242
25243     updateIndexes : function(startIndex, endIndex){
25244         var ns = this.nodes;
25245         startIndex = startIndex || 0;
25246         endIndex = endIndex || ns.length - 1;
25247         for(var i = startIndex; i <= endIndex; i++){
25248             ns[i].nodeIndex = i;
25249         }
25250     },
25251
25252     /**
25253      * Changes the data store this view uses and refresh the view.
25254      * @param {Store} store
25255      */
25256     setStore : function(store, initial){
25257         if(!initial && this.store){
25258             this.store.un("datachanged", this.refresh);
25259             this.store.un("add", this.onAdd);
25260             this.store.un("remove", this.onRemove);
25261             this.store.un("update", this.onUpdate);
25262             this.store.un("clear", this.refresh);
25263             this.store.un("beforeload", this.onBeforeLoad);
25264             this.store.un("load", this.onLoad);
25265             this.store.un("loadexception", this.onLoad);
25266         }
25267         if(store){
25268           
25269             store.on("datachanged", this.refresh, this);
25270             store.on("add", this.onAdd, this);
25271             store.on("remove", this.onRemove, this);
25272             store.on("update", this.onUpdate, this);
25273             store.on("clear", this.refresh, this);
25274             store.on("beforeload", this.onBeforeLoad, this);
25275             store.on("load", this.onLoad, this);
25276             store.on("loadexception", this.onLoad, this);
25277         }
25278         
25279         if(store){
25280             this.refresh();
25281         }
25282     },
25283     /**
25284      * onbeforeLoad - masks the loading area.
25285      *
25286      */
25287     onBeforeLoad : function(store,opts)
25288     {
25289          //Roo.log('onBeforeLoad');   
25290         if (!opts.add) {
25291             this.el.update("");
25292         }
25293         this.el.mask(this.mask ? this.mask : "Loading" ); 
25294     },
25295     onLoad : function ()
25296     {
25297         this.el.unmask();
25298     },
25299     
25300
25301     /**
25302      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25303      * @param {HTMLElement} node
25304      * @return {HTMLElement} The template node
25305      */
25306     findItemFromChild : function(node){
25307         var el = this.dataName  ?
25308             this.el.child('.roo-tpl-' + this.dataName,true) :
25309             this.el.dom; 
25310         
25311         if(!node || node.parentNode == el){
25312                     return node;
25313             }
25314             var p = node.parentNode;
25315             while(p && p != el){
25316             if(p.parentNode == el){
25317                 return p;
25318             }
25319             p = p.parentNode;
25320         }
25321             return null;
25322     },
25323
25324     /** @ignore */
25325     onClick : function(e){
25326         var item = this.findItemFromChild(e.getTarget());
25327         if(item){
25328             var index = this.indexOf(item);
25329             if(this.onItemClick(item, index, e) !== false){
25330                 this.fireEvent("click", this, index, item, e);
25331             }
25332         }else{
25333             this.clearSelections();
25334         }
25335     },
25336
25337     /** @ignore */
25338     onContextMenu : function(e){
25339         var item = this.findItemFromChild(e.getTarget());
25340         if(item){
25341             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25342         }
25343     },
25344
25345     /** @ignore */
25346     onDblClick : function(e){
25347         var item = this.findItemFromChild(e.getTarget());
25348         if(item){
25349             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25350         }
25351     },
25352
25353     onItemClick : function(item, index, e)
25354     {
25355         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25356             return false;
25357         }
25358         if (this.toggleSelect) {
25359             var m = this.isSelected(item) ? 'unselect' : 'select';
25360             //Roo.log(m);
25361             var _t = this;
25362             _t[m](item, true, false);
25363             return true;
25364         }
25365         if(this.multiSelect || this.singleSelect){
25366             if(this.multiSelect && e.shiftKey && this.lastSelection){
25367                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25368             }else{
25369                 this.select(item, this.multiSelect && e.ctrlKey);
25370                 this.lastSelection = item;
25371             }
25372             
25373             if(!this.tickable){
25374                 e.preventDefault();
25375             }
25376             
25377         }
25378         return true;
25379     },
25380
25381     /**
25382      * Get the number of selected nodes.
25383      * @return {Number}
25384      */
25385     getSelectionCount : function(){
25386         return this.selections.length;
25387     },
25388
25389     /**
25390      * Get the currently selected nodes.
25391      * @return {Array} An array of HTMLElements
25392      */
25393     getSelectedNodes : function(){
25394         return this.selections;
25395     },
25396
25397     /**
25398      * Get the indexes of the selected nodes.
25399      * @return {Array}
25400      */
25401     getSelectedIndexes : function(){
25402         var indexes = [], s = this.selections;
25403         for(var i = 0, len = s.length; i < len; i++){
25404             indexes.push(s[i].nodeIndex);
25405         }
25406         return indexes;
25407     },
25408
25409     /**
25410      * Clear all selections
25411      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25412      */
25413     clearSelections : function(suppressEvent){
25414         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25415             this.cmp.elements = this.selections;
25416             this.cmp.removeClass(this.selectedClass);
25417             this.selections = [];
25418             if(!suppressEvent){
25419                 this.fireEvent("selectionchange", this, this.selections);
25420             }
25421         }
25422     },
25423
25424     /**
25425      * Returns true if the passed node is selected
25426      * @param {HTMLElement/Number} node The node or node index
25427      * @return {Boolean}
25428      */
25429     isSelected : function(node){
25430         var s = this.selections;
25431         if(s.length < 1){
25432             return false;
25433         }
25434         node = this.getNode(node);
25435         return s.indexOf(node) !== -1;
25436     },
25437
25438     /**
25439      * Selects nodes.
25440      * @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
25441      * @param {Boolean} keepExisting (optional) true to keep existing selections
25442      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25443      */
25444     select : function(nodeInfo, keepExisting, suppressEvent){
25445         if(nodeInfo instanceof Array){
25446             if(!keepExisting){
25447                 this.clearSelections(true);
25448             }
25449             for(var i = 0, len = nodeInfo.length; i < len; i++){
25450                 this.select(nodeInfo[i], true, true);
25451             }
25452             return;
25453         } 
25454         var node = this.getNode(nodeInfo);
25455         if(!node || this.isSelected(node)){
25456             return; // already selected.
25457         }
25458         if(!keepExisting){
25459             this.clearSelections(true);
25460         }
25461         
25462         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25463             Roo.fly(node).addClass(this.selectedClass);
25464             this.selections.push(node);
25465             if(!suppressEvent){
25466                 this.fireEvent("selectionchange", this, this.selections);
25467             }
25468         }
25469         
25470         
25471     },
25472       /**
25473      * Unselects nodes.
25474      * @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
25475      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25476      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25477      */
25478     unselect : function(nodeInfo, keepExisting, suppressEvent)
25479     {
25480         if(nodeInfo instanceof Array){
25481             Roo.each(this.selections, function(s) {
25482                 this.unselect(s, nodeInfo);
25483             }, this);
25484             return;
25485         }
25486         var node = this.getNode(nodeInfo);
25487         if(!node || !this.isSelected(node)){
25488             //Roo.log("not selected");
25489             return; // not selected.
25490         }
25491         // fireevent???
25492         var ns = [];
25493         Roo.each(this.selections, function(s) {
25494             if (s == node ) {
25495                 Roo.fly(node).removeClass(this.selectedClass);
25496
25497                 return;
25498             }
25499             ns.push(s);
25500         },this);
25501         
25502         this.selections= ns;
25503         this.fireEvent("selectionchange", this, this.selections);
25504     },
25505
25506     /**
25507      * Gets a template node.
25508      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25509      * @return {HTMLElement} The node or null if it wasn't found
25510      */
25511     getNode : function(nodeInfo){
25512         if(typeof nodeInfo == "string"){
25513             return document.getElementById(nodeInfo);
25514         }else if(typeof nodeInfo == "number"){
25515             return this.nodes[nodeInfo];
25516         }
25517         return nodeInfo;
25518     },
25519
25520     /**
25521      * Gets a range template nodes.
25522      * @param {Number} startIndex
25523      * @param {Number} endIndex
25524      * @return {Array} An array of nodes
25525      */
25526     getNodes : function(start, end){
25527         var ns = this.nodes;
25528         start = start || 0;
25529         end = typeof end == "undefined" ? ns.length - 1 : end;
25530         var nodes = [];
25531         if(start <= end){
25532             for(var i = start; i <= end; i++){
25533                 nodes.push(ns[i]);
25534             }
25535         } else{
25536             for(var i = start; i >= end; i--){
25537                 nodes.push(ns[i]);
25538             }
25539         }
25540         return nodes;
25541     },
25542
25543     /**
25544      * Finds the index of the passed node
25545      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25546      * @return {Number} The index of the node or -1
25547      */
25548     indexOf : function(node){
25549         node = this.getNode(node);
25550         if(typeof node.nodeIndex == "number"){
25551             return node.nodeIndex;
25552         }
25553         var ns = this.nodes;
25554         for(var i = 0, len = ns.length; i < len; i++){
25555             if(ns[i] == node){
25556                 return i;
25557             }
25558         }
25559         return -1;
25560     }
25561 });
25562 /*
25563  * Based on:
25564  * Ext JS Library 1.1.1
25565  * Copyright(c) 2006-2007, Ext JS, LLC.
25566  *
25567  * Originally Released Under LGPL - original licence link has changed is not relivant.
25568  *
25569  * Fork - LGPL
25570  * <script type="text/javascript">
25571  */
25572
25573 /**
25574  * @class Roo.JsonView
25575  * @extends Roo.View
25576  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25577 <pre><code>
25578 var view = new Roo.JsonView({
25579     container: "my-element",
25580     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25581     multiSelect: true, 
25582     jsonRoot: "data" 
25583 });
25584
25585 // listen for node click?
25586 view.on("click", function(vw, index, node, e){
25587     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25588 });
25589
25590 // direct load of JSON data
25591 view.load("foobar.php");
25592
25593 // Example from my blog list
25594 var tpl = new Roo.Template(
25595     '&lt;div class="entry"&gt;' +
25596     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25597     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25598     "&lt;/div&gt;&lt;hr /&gt;"
25599 );
25600
25601 var moreView = new Roo.JsonView({
25602     container :  "entry-list", 
25603     template : tpl,
25604     jsonRoot: "posts"
25605 });
25606 moreView.on("beforerender", this.sortEntries, this);
25607 moreView.load({
25608     url: "/blog/get-posts.php",
25609     params: "allposts=true",
25610     text: "Loading Blog Entries..."
25611 });
25612 </code></pre>
25613
25614 * Note: old code is supported with arguments : (container, template, config)
25615
25616
25617  * @constructor
25618  * Create a new JsonView
25619  * 
25620  * @param {Object} config The config object
25621  * 
25622  */
25623 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25624     
25625     
25626     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25627
25628     var um = this.el.getUpdateManager();
25629     um.setRenderer(this);
25630     um.on("update", this.onLoad, this);
25631     um.on("failure", this.onLoadException, this);
25632
25633     /**
25634      * @event beforerender
25635      * Fires before rendering of the downloaded JSON data.
25636      * @param {Roo.JsonView} this
25637      * @param {Object} data The JSON data loaded
25638      */
25639     /**
25640      * @event load
25641      * Fires when data is loaded.
25642      * @param {Roo.JsonView} this
25643      * @param {Object} data The JSON data loaded
25644      * @param {Object} response The raw Connect response object
25645      */
25646     /**
25647      * @event loadexception
25648      * Fires when loading fails.
25649      * @param {Roo.JsonView} this
25650      * @param {Object} response The raw Connect response object
25651      */
25652     this.addEvents({
25653         'beforerender' : true,
25654         'load' : true,
25655         'loadexception' : true
25656     });
25657 };
25658 Roo.extend(Roo.JsonView, Roo.View, {
25659     /**
25660      * @type {String} The root property in the loaded JSON object that contains the data
25661      */
25662     jsonRoot : "",
25663
25664     /**
25665      * Refreshes the view.
25666      */
25667     refresh : function(){
25668         this.clearSelections();
25669         this.el.update("");
25670         var html = [];
25671         var o = this.jsonData;
25672         if(o && o.length > 0){
25673             for(var i = 0, len = o.length; i < len; i++){
25674                 var data = this.prepareData(o[i], i, o);
25675                 html[html.length] = this.tpl.apply(data);
25676             }
25677         }else{
25678             html.push(this.emptyText);
25679         }
25680         this.el.update(html.join(""));
25681         this.nodes = this.el.dom.childNodes;
25682         this.updateIndexes(0);
25683     },
25684
25685     /**
25686      * 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.
25687      * @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:
25688      <pre><code>
25689      view.load({
25690          url: "your-url.php",
25691          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25692          callback: yourFunction,
25693          scope: yourObject, //(optional scope)
25694          discardUrl: false,
25695          nocache: false,
25696          text: "Loading...",
25697          timeout: 30,
25698          scripts: false
25699      });
25700      </code></pre>
25701      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25702      * 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.
25703      * @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}
25704      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25705      * @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.
25706      */
25707     load : function(){
25708         var um = this.el.getUpdateManager();
25709         um.update.apply(um, arguments);
25710     },
25711
25712     render : function(el, response){
25713         this.clearSelections();
25714         this.el.update("");
25715         var o;
25716         try{
25717             o = Roo.util.JSON.decode(response.responseText);
25718             if(this.jsonRoot){
25719                 
25720                 o = o[this.jsonRoot];
25721             }
25722         } catch(e){
25723         }
25724         /**
25725          * The current JSON data or null
25726          */
25727         this.jsonData = o;
25728         this.beforeRender();
25729         this.refresh();
25730     },
25731
25732 /**
25733  * Get the number of records in the current JSON dataset
25734  * @return {Number}
25735  */
25736     getCount : function(){
25737         return this.jsonData ? this.jsonData.length : 0;
25738     },
25739
25740 /**
25741  * Returns the JSON object for the specified node(s)
25742  * @param {HTMLElement/Array} node The node or an array of nodes
25743  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25744  * you get the JSON object for the node
25745  */
25746     getNodeData : function(node){
25747         if(node instanceof Array){
25748             var data = [];
25749             for(var i = 0, len = node.length; i < len; i++){
25750                 data.push(this.getNodeData(node[i]));
25751             }
25752             return data;
25753         }
25754         return this.jsonData[this.indexOf(node)] || null;
25755     },
25756
25757     beforeRender : function(){
25758         this.snapshot = this.jsonData;
25759         if(this.sortInfo){
25760             this.sort.apply(this, this.sortInfo);
25761         }
25762         this.fireEvent("beforerender", this, this.jsonData);
25763     },
25764
25765     onLoad : function(el, o){
25766         this.fireEvent("load", this, this.jsonData, o);
25767     },
25768
25769     onLoadException : function(el, o){
25770         this.fireEvent("loadexception", this, o);
25771     },
25772
25773 /**
25774  * Filter the data by a specific property.
25775  * @param {String} property A property on your JSON objects
25776  * @param {String/RegExp} value Either string that the property values
25777  * should start with, or a RegExp to test against the property
25778  */
25779     filter : function(property, value){
25780         if(this.jsonData){
25781             var data = [];
25782             var ss = this.snapshot;
25783             if(typeof value == "string"){
25784                 var vlen = value.length;
25785                 if(vlen == 0){
25786                     this.clearFilter();
25787                     return;
25788                 }
25789                 value = value.toLowerCase();
25790                 for(var i = 0, len = ss.length; i < len; i++){
25791                     var o = ss[i];
25792                     if(o[property].substr(0, vlen).toLowerCase() == value){
25793                         data.push(o);
25794                     }
25795                 }
25796             } else if(value.exec){ // regex?
25797                 for(var i = 0, len = ss.length; i < len; i++){
25798                     var o = ss[i];
25799                     if(value.test(o[property])){
25800                         data.push(o);
25801                     }
25802                 }
25803             } else{
25804                 return;
25805             }
25806             this.jsonData = data;
25807             this.refresh();
25808         }
25809     },
25810
25811 /**
25812  * Filter by a function. The passed function will be called with each
25813  * object in the current dataset. If the function returns true the value is kept,
25814  * otherwise it is filtered.
25815  * @param {Function} fn
25816  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25817  */
25818     filterBy : function(fn, scope){
25819         if(this.jsonData){
25820             var data = [];
25821             var ss = this.snapshot;
25822             for(var i = 0, len = ss.length; i < len; i++){
25823                 var o = ss[i];
25824                 if(fn.call(scope || this, o)){
25825                     data.push(o);
25826                 }
25827             }
25828             this.jsonData = data;
25829             this.refresh();
25830         }
25831     },
25832
25833 /**
25834  * Clears the current filter.
25835  */
25836     clearFilter : function(){
25837         if(this.snapshot && this.jsonData != this.snapshot){
25838             this.jsonData = this.snapshot;
25839             this.refresh();
25840         }
25841     },
25842
25843
25844 /**
25845  * Sorts the data for this view and refreshes it.
25846  * @param {String} property A property on your JSON objects to sort on
25847  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25848  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25849  */
25850     sort : function(property, dir, sortType){
25851         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25852         if(this.jsonData){
25853             var p = property;
25854             var dsc = dir && dir.toLowerCase() == "desc";
25855             var f = function(o1, o2){
25856                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25857                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25858                 ;
25859                 if(v1 < v2){
25860                     return dsc ? +1 : -1;
25861                 } else if(v1 > v2){
25862                     return dsc ? -1 : +1;
25863                 } else{
25864                     return 0;
25865                 }
25866             };
25867             this.jsonData.sort(f);
25868             this.refresh();
25869             if(this.jsonData != this.snapshot){
25870                 this.snapshot.sort(f);
25871             }
25872         }
25873     }
25874 });/*
25875  * Based on:
25876  * Ext JS Library 1.1.1
25877  * Copyright(c) 2006-2007, Ext JS, LLC.
25878  *
25879  * Originally Released Under LGPL - original licence link has changed is not relivant.
25880  *
25881  * Fork - LGPL
25882  * <script type="text/javascript">
25883  */
25884  
25885
25886 /**
25887  * @class Roo.ColorPalette
25888  * @extends Roo.Component
25889  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25890  * Here's an example of typical usage:
25891  * <pre><code>
25892 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25893 cp.render('my-div');
25894
25895 cp.on('select', function(palette, selColor){
25896     // do something with selColor
25897 });
25898 </code></pre>
25899  * @constructor
25900  * Create a new ColorPalette
25901  * @param {Object} config The config object
25902  */
25903 Roo.ColorPalette = function(config){
25904     Roo.ColorPalette.superclass.constructor.call(this, config);
25905     this.addEvents({
25906         /**
25907              * @event select
25908              * Fires when a color is selected
25909              * @param {ColorPalette} this
25910              * @param {String} color The 6-digit color hex code (without the # symbol)
25911              */
25912         select: true
25913     });
25914
25915     if(this.handler){
25916         this.on("select", this.handler, this.scope, true);
25917     }
25918 };
25919 Roo.extend(Roo.ColorPalette, Roo.Component, {
25920     /**
25921      * @cfg {String} itemCls
25922      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25923      */
25924     itemCls : "x-color-palette",
25925     /**
25926      * @cfg {String} value
25927      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25928      * the hex codes are case-sensitive.
25929      */
25930     value : null,
25931     clickEvent:'click',
25932     // private
25933     ctype: "Roo.ColorPalette",
25934
25935     /**
25936      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25937      */
25938     allowReselect : false,
25939
25940     /**
25941      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25942      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25943      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25944      * of colors with the width setting until the box is symmetrical.</p>
25945      * <p>You can override individual colors if needed:</p>
25946      * <pre><code>
25947 var cp = new Roo.ColorPalette();
25948 cp.colors[0] = "FF0000";  // change the first box to red
25949 </code></pre>
25950
25951 Or you can provide a custom array of your own for complete control:
25952 <pre><code>
25953 var cp = new Roo.ColorPalette();
25954 cp.colors = ["000000", "993300", "333300"];
25955 </code></pre>
25956      * @type Array
25957      */
25958     colors : [
25959         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25960         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25961         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25962         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25963         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25964     ],
25965
25966     // private
25967     onRender : function(container, position){
25968         var t = new Roo.MasterTemplate(
25969             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25970         );
25971         var c = this.colors;
25972         for(var i = 0, len = c.length; i < len; i++){
25973             t.add([c[i]]);
25974         }
25975         var el = document.createElement("div");
25976         el.className = this.itemCls;
25977         t.overwrite(el);
25978         container.dom.insertBefore(el, position);
25979         this.el = Roo.get(el);
25980         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25981         if(this.clickEvent != 'click'){
25982             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25983         }
25984     },
25985
25986     // private
25987     afterRender : function(){
25988         Roo.ColorPalette.superclass.afterRender.call(this);
25989         if(this.value){
25990             var s = this.value;
25991             this.value = null;
25992             this.select(s);
25993         }
25994     },
25995
25996     // private
25997     handleClick : function(e, t){
25998         e.preventDefault();
25999         if(!this.disabled){
26000             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
26001             this.select(c.toUpperCase());
26002         }
26003     },
26004
26005     /**
26006      * Selects the specified color in the palette (fires the select event)
26007      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
26008      */
26009     select : function(color){
26010         color = color.replace("#", "");
26011         if(color != this.value || this.allowReselect){
26012             var el = this.el;
26013             if(this.value){
26014                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
26015             }
26016             el.child("a.color-"+color).addClass("x-color-palette-sel");
26017             this.value = color;
26018             this.fireEvent("select", this, color);
26019         }
26020     }
26021 });/*
26022  * Based on:
26023  * Ext JS Library 1.1.1
26024  * Copyright(c) 2006-2007, Ext JS, LLC.
26025  *
26026  * Originally Released Under LGPL - original licence link has changed is not relivant.
26027  *
26028  * Fork - LGPL
26029  * <script type="text/javascript">
26030  */
26031  
26032 /**
26033  * @class Roo.DatePicker
26034  * @extends Roo.Component
26035  * Simple date picker class.
26036  * @constructor
26037  * Create a new DatePicker
26038  * @param {Object} config The config object
26039  */
26040 Roo.DatePicker = function(config){
26041     Roo.DatePicker.superclass.constructor.call(this, config);
26042
26043     this.value = config && config.value ?
26044                  config.value.clearTime() : new Date().clearTime();
26045
26046     this.addEvents({
26047         /**
26048              * @event select
26049              * Fires when a date is selected
26050              * @param {DatePicker} this
26051              * @param {Date} date The selected date
26052              */
26053         'select': true,
26054         /**
26055              * @event monthchange
26056              * Fires when the displayed month changes 
26057              * @param {DatePicker} this
26058              * @param {Date} date The selected month
26059              */
26060         'monthchange': true
26061     });
26062
26063     if(this.handler){
26064         this.on("select", this.handler,  this.scope || this);
26065     }
26066     // build the disabledDatesRE
26067     if(!this.disabledDatesRE && this.disabledDates){
26068         var dd = this.disabledDates;
26069         var re = "(?:";
26070         for(var i = 0; i < dd.length; i++){
26071             re += dd[i];
26072             if(i != dd.length-1) {
26073                 re += "|";
26074             }
26075         }
26076         this.disabledDatesRE = new RegExp(re + ")");
26077     }
26078 };
26079
26080 Roo.extend(Roo.DatePicker, Roo.Component, {
26081     /**
26082      * @cfg {String} todayText
26083      * The text to display on the button that selects the current date (defaults to "Today")
26084      */
26085     todayText : "Today",
26086     /**
26087      * @cfg {String} okText
26088      * The text to display on the ok button
26089      */
26090     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
26091     /**
26092      * @cfg {String} cancelText
26093      * The text to display on the cancel button
26094      */
26095     cancelText : "Cancel",
26096     /**
26097      * @cfg {String} todayTip
26098      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
26099      */
26100     todayTip : "{0} (Spacebar)",
26101     /**
26102      * @cfg {Date} minDate
26103      * Minimum allowable date (JavaScript date object, defaults to null)
26104      */
26105     minDate : null,
26106     /**
26107      * @cfg {Date} maxDate
26108      * Maximum allowable date (JavaScript date object, defaults to null)
26109      */
26110     maxDate : null,
26111     /**
26112      * @cfg {String} minText
26113      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
26114      */
26115     minText : "This date is before the minimum date",
26116     /**
26117      * @cfg {String} maxText
26118      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
26119      */
26120     maxText : "This date is after the maximum date",
26121     /**
26122      * @cfg {String} format
26123      * The default date format string which can be overriden for localization support.  The format must be
26124      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
26125      */
26126     format : "m/d/y",
26127     /**
26128      * @cfg {Array} disabledDays
26129      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
26130      */
26131     disabledDays : null,
26132     /**
26133      * @cfg {String} disabledDaysText
26134      * The tooltip to display when the date falls on a disabled day (defaults to "")
26135      */
26136     disabledDaysText : "",
26137     /**
26138      * @cfg {RegExp} disabledDatesRE
26139      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
26140      */
26141     disabledDatesRE : null,
26142     /**
26143      * @cfg {String} disabledDatesText
26144      * The tooltip text to display when the date falls on a disabled date (defaults to "")
26145      */
26146     disabledDatesText : "",
26147     /**
26148      * @cfg {Boolean} constrainToViewport
26149      * True to constrain the date picker to the viewport (defaults to true)
26150      */
26151     constrainToViewport : true,
26152     /**
26153      * @cfg {Array} monthNames
26154      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
26155      */
26156     monthNames : Date.monthNames,
26157     /**
26158      * @cfg {Array} dayNames
26159      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
26160      */
26161     dayNames : Date.dayNames,
26162     /**
26163      * @cfg {String} nextText
26164      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
26165      */
26166     nextText: 'Next Month (Control+Right)',
26167     /**
26168      * @cfg {String} prevText
26169      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
26170      */
26171     prevText: 'Previous Month (Control+Left)',
26172     /**
26173      * @cfg {String} monthYearText
26174      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
26175      */
26176     monthYearText: 'Choose a month (Control+Up/Down to move years)',
26177     /**
26178      * @cfg {Number} startDay
26179      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
26180      */
26181     startDay : 0,
26182     /**
26183      * @cfg {Bool} showClear
26184      * Show a clear button (usefull for date form elements that can be blank.)
26185      */
26186     
26187     showClear: false,
26188     
26189     /**
26190      * Sets the value of the date field
26191      * @param {Date} value The date to set
26192      */
26193     setValue : function(value){
26194         var old = this.value;
26195         
26196         if (typeof(value) == 'string') {
26197          
26198             value = Date.parseDate(value, this.format);
26199         }
26200         if (!value) {
26201             value = new Date();
26202         }
26203         
26204         this.value = value.clearTime(true);
26205         if(this.el){
26206             this.update(this.value);
26207         }
26208     },
26209
26210     /**
26211      * Gets the current selected value of the date field
26212      * @return {Date} The selected date
26213      */
26214     getValue : function(){
26215         return this.value;
26216     },
26217
26218     // private
26219     focus : function(){
26220         if(this.el){
26221             this.update(this.activeDate);
26222         }
26223     },
26224
26225     // privateval
26226     onRender : function(container, position){
26227         
26228         var m = [
26229              '<table cellspacing="0">',
26230                 '<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>',
26231                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26232         var dn = this.dayNames;
26233         for(var i = 0; i < 7; i++){
26234             var d = this.startDay+i;
26235             if(d > 6){
26236                 d = d-7;
26237             }
26238             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26239         }
26240         m[m.length] = "</tr></thead><tbody><tr>";
26241         for(var i = 0; i < 42; i++) {
26242             if(i % 7 == 0 && i != 0){
26243                 m[m.length] = "</tr><tr>";
26244             }
26245             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26246         }
26247         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26248             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26249
26250         var el = document.createElement("div");
26251         el.className = "x-date-picker";
26252         el.innerHTML = m.join("");
26253
26254         container.dom.insertBefore(el, position);
26255
26256         this.el = Roo.get(el);
26257         this.eventEl = Roo.get(el.firstChild);
26258
26259         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26260             handler: this.showPrevMonth,
26261             scope: this,
26262             preventDefault:true,
26263             stopDefault:true
26264         });
26265
26266         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26267             handler: this.showNextMonth,
26268             scope: this,
26269             preventDefault:true,
26270             stopDefault:true
26271         });
26272
26273         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26274
26275         this.monthPicker = this.el.down('div.x-date-mp');
26276         this.monthPicker.enableDisplayMode('block');
26277         
26278         var kn = new Roo.KeyNav(this.eventEl, {
26279             "left" : function(e){
26280                 e.ctrlKey ?
26281                     this.showPrevMonth() :
26282                     this.update(this.activeDate.add("d", -1));
26283             },
26284
26285             "right" : function(e){
26286                 e.ctrlKey ?
26287                     this.showNextMonth() :
26288                     this.update(this.activeDate.add("d", 1));
26289             },
26290
26291             "up" : function(e){
26292                 e.ctrlKey ?
26293                     this.showNextYear() :
26294                     this.update(this.activeDate.add("d", -7));
26295             },
26296
26297             "down" : function(e){
26298                 e.ctrlKey ?
26299                     this.showPrevYear() :
26300                     this.update(this.activeDate.add("d", 7));
26301             },
26302
26303             "pageUp" : function(e){
26304                 this.showNextMonth();
26305             },
26306
26307             "pageDown" : function(e){
26308                 this.showPrevMonth();
26309             },
26310
26311             "enter" : function(e){
26312                 e.stopPropagation();
26313                 return true;
26314             },
26315
26316             scope : this
26317         });
26318
26319         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26320
26321         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26322
26323         this.el.unselectable();
26324         
26325         this.cells = this.el.select("table.x-date-inner tbody td");
26326         this.textNodes = this.el.query("table.x-date-inner tbody span");
26327
26328         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26329             text: "&#160;",
26330             tooltip: this.monthYearText
26331         });
26332
26333         this.mbtn.on('click', this.showMonthPicker, this);
26334         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26335
26336
26337         var today = (new Date()).dateFormat(this.format);
26338         
26339         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26340         if (this.showClear) {
26341             baseTb.add( new Roo.Toolbar.Fill());
26342         }
26343         baseTb.add({
26344             text: String.format(this.todayText, today),
26345             tooltip: String.format(this.todayTip, today),
26346             handler: this.selectToday,
26347             scope: this
26348         });
26349         
26350         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26351             
26352         //});
26353         if (this.showClear) {
26354             
26355             baseTb.add( new Roo.Toolbar.Fill());
26356             baseTb.add({
26357                 text: '&#160;',
26358                 cls: 'x-btn-icon x-btn-clear',
26359                 handler: function() {
26360                     //this.value = '';
26361                     this.fireEvent("select", this, '');
26362                 },
26363                 scope: this
26364             });
26365         }
26366         
26367         
26368         if(Roo.isIE){
26369             this.el.repaint();
26370         }
26371         this.update(this.value);
26372     },
26373
26374     createMonthPicker : function(){
26375         if(!this.monthPicker.dom.firstChild){
26376             var buf = ['<table border="0" cellspacing="0">'];
26377             for(var i = 0; i < 6; i++){
26378                 buf.push(
26379                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26380                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26381                     i == 0 ?
26382                     '<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>' :
26383                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26384                 );
26385             }
26386             buf.push(
26387                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26388                     this.okText,
26389                     '</button><button type="button" class="x-date-mp-cancel">',
26390                     this.cancelText,
26391                     '</button></td></tr>',
26392                 '</table>'
26393             );
26394             this.monthPicker.update(buf.join(''));
26395             this.monthPicker.on('click', this.onMonthClick, this);
26396             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26397
26398             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26399             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26400
26401             this.mpMonths.each(function(m, a, i){
26402                 i += 1;
26403                 if((i%2) == 0){
26404                     m.dom.xmonth = 5 + Math.round(i * .5);
26405                 }else{
26406                     m.dom.xmonth = Math.round((i-1) * .5);
26407                 }
26408             });
26409         }
26410     },
26411
26412     showMonthPicker : function(){
26413         this.createMonthPicker();
26414         var size = this.el.getSize();
26415         this.monthPicker.setSize(size);
26416         this.monthPicker.child('table').setSize(size);
26417
26418         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26419         this.updateMPMonth(this.mpSelMonth);
26420         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26421         this.updateMPYear(this.mpSelYear);
26422
26423         this.monthPicker.slideIn('t', {duration:.2});
26424     },
26425
26426     updateMPYear : function(y){
26427         this.mpyear = y;
26428         var ys = this.mpYears.elements;
26429         for(var i = 1; i <= 10; i++){
26430             var td = ys[i-1], y2;
26431             if((i%2) == 0){
26432                 y2 = y + Math.round(i * .5);
26433                 td.firstChild.innerHTML = y2;
26434                 td.xyear = y2;
26435             }else{
26436                 y2 = y - (5-Math.round(i * .5));
26437                 td.firstChild.innerHTML = y2;
26438                 td.xyear = y2;
26439             }
26440             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26441         }
26442     },
26443
26444     updateMPMonth : function(sm){
26445         this.mpMonths.each(function(m, a, i){
26446             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26447         });
26448     },
26449
26450     selectMPMonth: function(m){
26451         
26452     },
26453
26454     onMonthClick : function(e, t){
26455         e.stopEvent();
26456         var el = new Roo.Element(t), pn;
26457         if(el.is('button.x-date-mp-cancel')){
26458             this.hideMonthPicker();
26459         }
26460         else if(el.is('button.x-date-mp-ok')){
26461             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26462             this.hideMonthPicker();
26463         }
26464         else if(pn = el.up('td.x-date-mp-month', 2)){
26465             this.mpMonths.removeClass('x-date-mp-sel');
26466             pn.addClass('x-date-mp-sel');
26467             this.mpSelMonth = pn.dom.xmonth;
26468         }
26469         else if(pn = el.up('td.x-date-mp-year', 2)){
26470             this.mpYears.removeClass('x-date-mp-sel');
26471             pn.addClass('x-date-mp-sel');
26472             this.mpSelYear = pn.dom.xyear;
26473         }
26474         else if(el.is('a.x-date-mp-prev')){
26475             this.updateMPYear(this.mpyear-10);
26476         }
26477         else if(el.is('a.x-date-mp-next')){
26478             this.updateMPYear(this.mpyear+10);
26479         }
26480     },
26481
26482     onMonthDblClick : function(e, t){
26483         e.stopEvent();
26484         var el = new Roo.Element(t), pn;
26485         if(pn = el.up('td.x-date-mp-month', 2)){
26486             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26487             this.hideMonthPicker();
26488         }
26489         else if(pn = el.up('td.x-date-mp-year', 2)){
26490             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26491             this.hideMonthPicker();
26492         }
26493     },
26494
26495     hideMonthPicker : function(disableAnim){
26496         if(this.monthPicker){
26497             if(disableAnim === true){
26498                 this.monthPicker.hide();
26499             }else{
26500                 this.monthPicker.slideOut('t', {duration:.2});
26501             }
26502         }
26503     },
26504
26505     // private
26506     showPrevMonth : function(e){
26507         this.update(this.activeDate.add("mo", -1));
26508     },
26509
26510     // private
26511     showNextMonth : function(e){
26512         this.update(this.activeDate.add("mo", 1));
26513     },
26514
26515     // private
26516     showPrevYear : function(){
26517         this.update(this.activeDate.add("y", -1));
26518     },
26519
26520     // private
26521     showNextYear : function(){
26522         this.update(this.activeDate.add("y", 1));
26523     },
26524
26525     // private
26526     handleMouseWheel : function(e){
26527         var delta = e.getWheelDelta();
26528         if(delta > 0){
26529             this.showPrevMonth();
26530             e.stopEvent();
26531         } else if(delta < 0){
26532             this.showNextMonth();
26533             e.stopEvent();
26534         }
26535     },
26536
26537     // private
26538     handleDateClick : function(e, t){
26539         e.stopEvent();
26540         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26541             this.setValue(new Date(t.dateValue));
26542             this.fireEvent("select", this, this.value);
26543         }
26544     },
26545
26546     // private
26547     selectToday : function(){
26548         this.setValue(new Date().clearTime());
26549         this.fireEvent("select", this, this.value);
26550     },
26551
26552     // private
26553     update : function(date)
26554     {
26555         var vd = this.activeDate;
26556         this.activeDate = date;
26557         if(vd && this.el){
26558             var t = date.getTime();
26559             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26560                 this.cells.removeClass("x-date-selected");
26561                 this.cells.each(function(c){
26562                    if(c.dom.firstChild.dateValue == t){
26563                        c.addClass("x-date-selected");
26564                        setTimeout(function(){
26565                             try{c.dom.firstChild.focus();}catch(e){}
26566                        }, 50);
26567                        return false;
26568                    }
26569                 });
26570                 return;
26571             }
26572         }
26573         
26574         var days = date.getDaysInMonth();
26575         var firstOfMonth = date.getFirstDateOfMonth();
26576         var startingPos = firstOfMonth.getDay()-this.startDay;
26577
26578         if(startingPos <= this.startDay){
26579             startingPos += 7;
26580         }
26581
26582         var pm = date.add("mo", -1);
26583         var prevStart = pm.getDaysInMonth()-startingPos;
26584
26585         var cells = this.cells.elements;
26586         var textEls = this.textNodes;
26587         days += startingPos;
26588
26589         // convert everything to numbers so it's fast
26590         var day = 86400000;
26591         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26592         var today = new Date().clearTime().getTime();
26593         var sel = date.clearTime().getTime();
26594         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26595         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26596         var ddMatch = this.disabledDatesRE;
26597         var ddText = this.disabledDatesText;
26598         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26599         var ddaysText = this.disabledDaysText;
26600         var format = this.format;
26601
26602         var setCellClass = function(cal, cell){
26603             cell.title = "";
26604             var t = d.getTime();
26605             cell.firstChild.dateValue = t;
26606             if(t == today){
26607                 cell.className += " x-date-today";
26608                 cell.title = cal.todayText;
26609             }
26610             if(t == sel){
26611                 cell.className += " x-date-selected";
26612                 setTimeout(function(){
26613                     try{cell.firstChild.focus();}catch(e){}
26614                 }, 50);
26615             }
26616             // disabling
26617             if(t < min) {
26618                 cell.className = " x-date-disabled";
26619                 cell.title = cal.minText;
26620                 return;
26621             }
26622             if(t > max) {
26623                 cell.className = " x-date-disabled";
26624                 cell.title = cal.maxText;
26625                 return;
26626             }
26627             if(ddays){
26628                 if(ddays.indexOf(d.getDay()) != -1){
26629                     cell.title = ddaysText;
26630                     cell.className = " x-date-disabled";
26631                 }
26632             }
26633             if(ddMatch && format){
26634                 var fvalue = d.dateFormat(format);
26635                 if(ddMatch.test(fvalue)){
26636                     cell.title = ddText.replace("%0", fvalue);
26637                     cell.className = " x-date-disabled";
26638                 }
26639             }
26640         };
26641
26642         var i = 0;
26643         for(; i < startingPos; i++) {
26644             textEls[i].innerHTML = (++prevStart);
26645             d.setDate(d.getDate()+1);
26646             cells[i].className = "x-date-prevday";
26647             setCellClass(this, cells[i]);
26648         }
26649         for(; i < days; i++){
26650             intDay = i - startingPos + 1;
26651             textEls[i].innerHTML = (intDay);
26652             d.setDate(d.getDate()+1);
26653             cells[i].className = "x-date-active";
26654             setCellClass(this, cells[i]);
26655         }
26656         var extraDays = 0;
26657         for(; i < 42; i++) {
26658              textEls[i].innerHTML = (++extraDays);
26659              d.setDate(d.getDate()+1);
26660              cells[i].className = "x-date-nextday";
26661              setCellClass(this, cells[i]);
26662         }
26663
26664         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26665         this.fireEvent('monthchange', this, date);
26666         
26667         if(!this.internalRender){
26668             var main = this.el.dom.firstChild;
26669             var w = main.offsetWidth;
26670             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26671             Roo.fly(main).setWidth(w);
26672             this.internalRender = true;
26673             // opera does not respect the auto grow header center column
26674             // then, after it gets a width opera refuses to recalculate
26675             // without a second pass
26676             if(Roo.isOpera && !this.secondPass){
26677                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26678                 this.secondPass = true;
26679                 this.update.defer(10, this, [date]);
26680             }
26681         }
26682         
26683         
26684     }
26685 });        /*
26686  * Based on:
26687  * Ext JS Library 1.1.1
26688  * Copyright(c) 2006-2007, Ext JS, LLC.
26689  *
26690  * Originally Released Under LGPL - original licence link has changed is not relivant.
26691  *
26692  * Fork - LGPL
26693  * <script type="text/javascript">
26694  */
26695 /**
26696  * @class Roo.TabPanel
26697  * @extends Roo.util.Observable
26698  * A lightweight tab container.
26699  * <br><br>
26700  * Usage:
26701  * <pre><code>
26702 // basic tabs 1, built from existing content
26703 var tabs = new Roo.TabPanel("tabs1");
26704 tabs.addTab("script", "View Script");
26705 tabs.addTab("markup", "View Markup");
26706 tabs.activate("script");
26707
26708 // more advanced tabs, built from javascript
26709 var jtabs = new Roo.TabPanel("jtabs");
26710 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26711
26712 // set up the UpdateManager
26713 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26714 var updater = tab2.getUpdateManager();
26715 updater.setDefaultUrl("ajax1.htm");
26716 tab2.on('activate', updater.refresh, updater, true);
26717
26718 // Use setUrl for Ajax loading
26719 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26720 tab3.setUrl("ajax2.htm", null, true);
26721
26722 // Disabled tab
26723 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26724 tab4.disable();
26725
26726 jtabs.activate("jtabs-1");
26727  * </code></pre>
26728  * @constructor
26729  * Create a new TabPanel.
26730  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26731  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26732  */
26733 Roo.TabPanel = function(container, config){
26734     /**
26735     * The container element for this TabPanel.
26736     * @type Roo.Element
26737     */
26738     this.el = Roo.get(container, true);
26739     if(config){
26740         if(typeof config == "boolean"){
26741             this.tabPosition = config ? "bottom" : "top";
26742         }else{
26743             Roo.apply(this, config);
26744         }
26745     }
26746     if(this.tabPosition == "bottom"){
26747         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26748         this.el.addClass("x-tabs-bottom");
26749     }
26750     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26751     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26752     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26753     if(Roo.isIE){
26754         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26755     }
26756     if(this.tabPosition != "bottom"){
26757         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26758          * @type Roo.Element
26759          */
26760         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26761         this.el.addClass("x-tabs-top");
26762     }
26763     this.items = [];
26764
26765     this.bodyEl.setStyle("position", "relative");
26766
26767     this.active = null;
26768     this.activateDelegate = this.activate.createDelegate(this);
26769
26770     this.addEvents({
26771         /**
26772          * @event tabchange
26773          * Fires when the active tab changes
26774          * @param {Roo.TabPanel} this
26775          * @param {Roo.TabPanelItem} activePanel The new active tab
26776          */
26777         "tabchange": true,
26778         /**
26779          * @event beforetabchange
26780          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26781          * @param {Roo.TabPanel} this
26782          * @param {Object} e Set cancel to true on this object to cancel the tab change
26783          * @param {Roo.TabPanelItem} tab The tab being changed to
26784          */
26785         "beforetabchange" : true
26786     });
26787
26788     Roo.EventManager.onWindowResize(this.onResize, this);
26789     this.cpad = this.el.getPadding("lr");
26790     this.hiddenCount = 0;
26791
26792
26793     // toolbar on the tabbar support...
26794     if (this.toolbar) {
26795         var tcfg = this.toolbar;
26796         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26797         this.toolbar = new Roo.Toolbar(tcfg);
26798         if (Roo.isSafari) {
26799             var tbl = tcfg.container.child('table', true);
26800             tbl.setAttribute('width', '100%');
26801         }
26802         
26803     }
26804    
26805
26806
26807     Roo.TabPanel.superclass.constructor.call(this);
26808 };
26809
26810 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26811     /*
26812      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26813      */
26814     tabPosition : "top",
26815     /*
26816      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26817      */
26818     currentTabWidth : 0,
26819     /*
26820      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26821      */
26822     minTabWidth : 40,
26823     /*
26824      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26825      */
26826     maxTabWidth : 250,
26827     /*
26828      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26829      */
26830     preferredTabWidth : 175,
26831     /*
26832      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26833      */
26834     resizeTabs : false,
26835     /*
26836      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26837      */
26838     monitorResize : true,
26839     /*
26840      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26841      */
26842     toolbar : false,
26843
26844     /**
26845      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26846      * @param {String} id The id of the div to use <b>or create</b>
26847      * @param {String} text The text for the tab
26848      * @param {String} content (optional) Content to put in the TabPanelItem body
26849      * @param {Boolean} closable (optional) True to create a close icon on the tab
26850      * @return {Roo.TabPanelItem} The created TabPanelItem
26851      */
26852     addTab : function(id, text, content, closable){
26853         var item = new Roo.TabPanelItem(this, id, text, closable);
26854         this.addTabItem(item);
26855         if(content){
26856             item.setContent(content);
26857         }
26858         return item;
26859     },
26860
26861     /**
26862      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26863      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26864      * @return {Roo.TabPanelItem}
26865      */
26866     getTab : function(id){
26867         return this.items[id];
26868     },
26869
26870     /**
26871      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26872      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26873      */
26874     hideTab : function(id){
26875         var t = this.items[id];
26876         if(!t.isHidden()){
26877            t.setHidden(true);
26878            this.hiddenCount++;
26879            this.autoSizeTabs();
26880         }
26881     },
26882
26883     /**
26884      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26885      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26886      */
26887     unhideTab : function(id){
26888         var t = this.items[id];
26889         if(t.isHidden()){
26890            t.setHidden(false);
26891            this.hiddenCount--;
26892            this.autoSizeTabs();
26893         }
26894     },
26895
26896     /**
26897      * Adds an existing {@link Roo.TabPanelItem}.
26898      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26899      */
26900     addTabItem : function(item){
26901         this.items[item.id] = item;
26902         this.items.push(item);
26903         if(this.resizeTabs){
26904            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26905            this.autoSizeTabs();
26906         }else{
26907             item.autoSize();
26908         }
26909     },
26910
26911     /**
26912      * Removes a {@link Roo.TabPanelItem}.
26913      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26914      */
26915     removeTab : function(id){
26916         var items = this.items;
26917         var tab = items[id];
26918         if(!tab) { return; }
26919         var index = items.indexOf(tab);
26920         if(this.active == tab && items.length > 1){
26921             var newTab = this.getNextAvailable(index);
26922             if(newTab) {
26923                 newTab.activate();
26924             }
26925         }
26926         this.stripEl.dom.removeChild(tab.pnode.dom);
26927         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26928             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26929         }
26930         items.splice(index, 1);
26931         delete this.items[tab.id];
26932         tab.fireEvent("close", tab);
26933         tab.purgeListeners();
26934         this.autoSizeTabs();
26935     },
26936
26937     getNextAvailable : function(start){
26938         var items = this.items;
26939         var index = start;
26940         // look for a next tab that will slide over to
26941         // replace the one being removed
26942         while(index < items.length){
26943             var item = items[++index];
26944             if(item && !item.isHidden()){
26945                 return item;
26946             }
26947         }
26948         // if one isn't found select the previous tab (on the left)
26949         index = start;
26950         while(index >= 0){
26951             var item = items[--index];
26952             if(item && !item.isHidden()){
26953                 return item;
26954             }
26955         }
26956         return null;
26957     },
26958
26959     /**
26960      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26961      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26962      */
26963     disableTab : function(id){
26964         var tab = this.items[id];
26965         if(tab && this.active != tab){
26966             tab.disable();
26967         }
26968     },
26969
26970     /**
26971      * Enables a {@link Roo.TabPanelItem} that is disabled.
26972      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26973      */
26974     enableTab : function(id){
26975         var tab = this.items[id];
26976         tab.enable();
26977     },
26978
26979     /**
26980      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26981      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26982      * @return {Roo.TabPanelItem} The TabPanelItem.
26983      */
26984     activate : function(id){
26985         var tab = this.items[id];
26986         if(!tab){
26987             return null;
26988         }
26989         if(tab == this.active || tab.disabled){
26990             return tab;
26991         }
26992         var e = {};
26993         this.fireEvent("beforetabchange", this, e, tab);
26994         if(e.cancel !== true && !tab.disabled){
26995             if(this.active){
26996                 this.active.hide();
26997             }
26998             this.active = this.items[id];
26999             this.active.show();
27000             this.fireEvent("tabchange", this, this.active);
27001         }
27002         return tab;
27003     },
27004
27005     /**
27006      * Gets the active {@link Roo.TabPanelItem}.
27007      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
27008      */
27009     getActiveTab : function(){
27010         return this.active;
27011     },
27012
27013     /**
27014      * Updates the tab body element to fit the height of the container element
27015      * for overflow scrolling
27016      * @param {Number} targetHeight (optional) Override the starting height from the elements height
27017      */
27018     syncHeight : function(targetHeight){
27019         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
27020         var bm = this.bodyEl.getMargins();
27021         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
27022         this.bodyEl.setHeight(newHeight);
27023         return newHeight;
27024     },
27025
27026     onResize : function(){
27027         if(this.monitorResize){
27028             this.autoSizeTabs();
27029         }
27030     },
27031
27032     /**
27033      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
27034      */
27035     beginUpdate : function(){
27036         this.updating = true;
27037     },
27038
27039     /**
27040      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
27041      */
27042     endUpdate : function(){
27043         this.updating = false;
27044         this.autoSizeTabs();
27045     },
27046
27047     /**
27048      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
27049      */
27050     autoSizeTabs : function(){
27051         var count = this.items.length;
27052         var vcount = count - this.hiddenCount;
27053         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
27054             return;
27055         }
27056         var w = Math.max(this.el.getWidth() - this.cpad, 10);
27057         var availWidth = Math.floor(w / vcount);
27058         var b = this.stripBody;
27059         if(b.getWidth() > w){
27060             var tabs = this.items;
27061             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
27062             if(availWidth < this.minTabWidth){
27063                 /*if(!this.sleft){    // incomplete scrolling code
27064                     this.createScrollButtons();
27065                 }
27066                 this.showScroll();
27067                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
27068             }
27069         }else{
27070             if(this.currentTabWidth < this.preferredTabWidth){
27071                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
27072             }
27073         }
27074     },
27075
27076     /**
27077      * Returns the number of tabs in this TabPanel.
27078      * @return {Number}
27079      */
27080      getCount : function(){
27081          return this.items.length;
27082      },
27083
27084     /**
27085      * Resizes all the tabs to the passed width
27086      * @param {Number} The new width
27087      */
27088     setTabWidth : function(width){
27089         this.currentTabWidth = width;
27090         for(var i = 0, len = this.items.length; i < len; i++) {
27091                 if(!this.items[i].isHidden()) {
27092                 this.items[i].setWidth(width);
27093             }
27094         }
27095     },
27096
27097     /**
27098      * Destroys this TabPanel
27099      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
27100      */
27101     destroy : function(removeEl){
27102         Roo.EventManager.removeResizeListener(this.onResize, this);
27103         for(var i = 0, len = this.items.length; i < len; i++){
27104             this.items[i].purgeListeners();
27105         }
27106         if(removeEl === true){
27107             this.el.update("");
27108             this.el.remove();
27109         }
27110     }
27111 });
27112
27113 /**
27114  * @class Roo.TabPanelItem
27115  * @extends Roo.util.Observable
27116  * Represents an individual item (tab plus body) in a TabPanel.
27117  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
27118  * @param {String} id The id of this TabPanelItem
27119  * @param {String} text The text for the tab of this TabPanelItem
27120  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
27121  */
27122 Roo.TabPanelItem = function(tabPanel, id, text, closable){
27123     /**
27124      * The {@link Roo.TabPanel} this TabPanelItem belongs to
27125      * @type Roo.TabPanel
27126      */
27127     this.tabPanel = tabPanel;
27128     /**
27129      * The id for this TabPanelItem
27130      * @type String
27131      */
27132     this.id = id;
27133     /** @private */
27134     this.disabled = false;
27135     /** @private */
27136     this.text = text;
27137     /** @private */
27138     this.loaded = false;
27139     this.closable = closable;
27140
27141     /**
27142      * The body element for this TabPanelItem.
27143      * @type Roo.Element
27144      */
27145     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
27146     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
27147     this.bodyEl.setStyle("display", "block");
27148     this.bodyEl.setStyle("zoom", "1");
27149     this.hideAction();
27150
27151     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
27152     /** @private */
27153     this.el = Roo.get(els.el, true);
27154     this.inner = Roo.get(els.inner, true);
27155     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
27156     this.pnode = Roo.get(els.el.parentNode, true);
27157     this.el.on("mousedown", this.onTabMouseDown, this);
27158     this.el.on("click", this.onTabClick, this);
27159     /** @private */
27160     if(closable){
27161         var c = Roo.get(els.close, true);
27162         c.dom.title = this.closeText;
27163         c.addClassOnOver("close-over");
27164         c.on("click", this.closeClick, this);
27165      }
27166
27167     this.addEvents({
27168          /**
27169          * @event activate
27170          * Fires when this tab becomes the active tab.
27171          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27172          * @param {Roo.TabPanelItem} this
27173          */
27174         "activate": true,
27175         /**
27176          * @event beforeclose
27177          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
27178          * @param {Roo.TabPanelItem} this
27179          * @param {Object} e Set cancel to true on this object to cancel the close.
27180          */
27181         "beforeclose": true,
27182         /**
27183          * @event close
27184          * Fires when this tab is closed.
27185          * @param {Roo.TabPanelItem} this
27186          */
27187          "close": true,
27188         /**
27189          * @event deactivate
27190          * Fires when this tab is no longer the active tab.
27191          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27192          * @param {Roo.TabPanelItem} this
27193          */
27194          "deactivate" : true
27195     });
27196     this.hidden = false;
27197
27198     Roo.TabPanelItem.superclass.constructor.call(this);
27199 };
27200
27201 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27202     purgeListeners : function(){
27203        Roo.util.Observable.prototype.purgeListeners.call(this);
27204        this.el.removeAllListeners();
27205     },
27206     /**
27207      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27208      */
27209     show : function(){
27210         this.pnode.addClass("on");
27211         this.showAction();
27212         if(Roo.isOpera){
27213             this.tabPanel.stripWrap.repaint();
27214         }
27215         this.fireEvent("activate", this.tabPanel, this);
27216     },
27217
27218     /**
27219      * Returns true if this tab is the active tab.
27220      * @return {Boolean}
27221      */
27222     isActive : function(){
27223         return this.tabPanel.getActiveTab() == this;
27224     },
27225
27226     /**
27227      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27228      */
27229     hide : function(){
27230         this.pnode.removeClass("on");
27231         this.hideAction();
27232         this.fireEvent("deactivate", this.tabPanel, this);
27233     },
27234
27235     hideAction : function(){
27236         this.bodyEl.hide();
27237         this.bodyEl.setStyle("position", "absolute");
27238         this.bodyEl.setLeft("-20000px");
27239         this.bodyEl.setTop("-20000px");
27240     },
27241
27242     showAction : function(){
27243         this.bodyEl.setStyle("position", "relative");
27244         this.bodyEl.setTop("");
27245         this.bodyEl.setLeft("");
27246         this.bodyEl.show();
27247     },
27248
27249     /**
27250      * Set the tooltip for the tab.
27251      * @param {String} tooltip The tab's tooltip
27252      */
27253     setTooltip : function(text){
27254         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27255             this.textEl.dom.qtip = text;
27256             this.textEl.dom.removeAttribute('title');
27257         }else{
27258             this.textEl.dom.title = text;
27259         }
27260     },
27261
27262     onTabClick : function(e){
27263         e.preventDefault();
27264         this.tabPanel.activate(this.id);
27265     },
27266
27267     onTabMouseDown : function(e){
27268         e.preventDefault();
27269         this.tabPanel.activate(this.id);
27270     },
27271
27272     getWidth : function(){
27273         return this.inner.getWidth();
27274     },
27275
27276     setWidth : function(width){
27277         var iwidth = width - this.pnode.getPadding("lr");
27278         this.inner.setWidth(iwidth);
27279         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27280         this.pnode.setWidth(width);
27281     },
27282
27283     /**
27284      * Show or hide the tab
27285      * @param {Boolean} hidden True to hide or false to show.
27286      */
27287     setHidden : function(hidden){
27288         this.hidden = hidden;
27289         this.pnode.setStyle("display", hidden ? "none" : "");
27290     },
27291
27292     /**
27293      * Returns true if this tab is "hidden"
27294      * @return {Boolean}
27295      */
27296     isHidden : function(){
27297         return this.hidden;
27298     },
27299
27300     /**
27301      * Returns the text for this tab
27302      * @return {String}
27303      */
27304     getText : function(){
27305         return this.text;
27306     },
27307
27308     autoSize : function(){
27309         //this.el.beginMeasure();
27310         this.textEl.setWidth(1);
27311         /*
27312          *  #2804 [new] Tabs in Roojs
27313          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
27314          */
27315         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
27316         //this.el.endMeasure();
27317     },
27318
27319     /**
27320      * Sets the text for the tab (Note: this also sets the tooltip text)
27321      * @param {String} text The tab's text and tooltip
27322      */
27323     setText : function(text){
27324         this.text = text;
27325         this.textEl.update(text);
27326         this.setTooltip(text);
27327         if(!this.tabPanel.resizeTabs){
27328             this.autoSize();
27329         }
27330     },
27331     /**
27332      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27333      */
27334     activate : function(){
27335         this.tabPanel.activate(this.id);
27336     },
27337
27338     /**
27339      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27340      */
27341     disable : function(){
27342         if(this.tabPanel.active != this){
27343             this.disabled = true;
27344             this.pnode.addClass("disabled");
27345         }
27346     },
27347
27348     /**
27349      * Enables this TabPanelItem if it was previously disabled.
27350      */
27351     enable : function(){
27352         this.disabled = false;
27353         this.pnode.removeClass("disabled");
27354     },
27355
27356     /**
27357      * Sets the content for this TabPanelItem.
27358      * @param {String} content The content
27359      * @param {Boolean} loadScripts true to look for and load scripts
27360      */
27361     setContent : function(content, loadScripts){
27362         this.bodyEl.update(content, loadScripts);
27363     },
27364
27365     /**
27366      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27367      * @return {Roo.UpdateManager} The UpdateManager
27368      */
27369     getUpdateManager : function(){
27370         return this.bodyEl.getUpdateManager();
27371     },
27372
27373     /**
27374      * Set a URL to be used to load the content for this TabPanelItem.
27375      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27376      * @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)
27377      * @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)
27378      * @return {Roo.UpdateManager} The UpdateManager
27379      */
27380     setUrl : function(url, params, loadOnce){
27381         if(this.refreshDelegate){
27382             this.un('activate', this.refreshDelegate);
27383         }
27384         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27385         this.on("activate", this.refreshDelegate);
27386         return this.bodyEl.getUpdateManager();
27387     },
27388
27389     /** @private */
27390     _handleRefresh : function(url, params, loadOnce){
27391         if(!loadOnce || !this.loaded){
27392             var updater = this.bodyEl.getUpdateManager();
27393             updater.update(url, params, this._setLoaded.createDelegate(this));
27394         }
27395     },
27396
27397     /**
27398      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27399      *   Will fail silently if the setUrl method has not been called.
27400      *   This does not activate the panel, just updates its content.
27401      */
27402     refresh : function(){
27403         if(this.refreshDelegate){
27404            this.loaded = false;
27405            this.refreshDelegate();
27406         }
27407     },
27408
27409     /** @private */
27410     _setLoaded : function(){
27411         this.loaded = true;
27412     },
27413
27414     /** @private */
27415     closeClick : function(e){
27416         var o = {};
27417         e.stopEvent();
27418         this.fireEvent("beforeclose", this, o);
27419         if(o.cancel !== true){
27420             this.tabPanel.removeTab(this.id);
27421         }
27422     },
27423     /**
27424      * The text displayed in the tooltip for the close icon.
27425      * @type String
27426      */
27427     closeText : "Close this tab"
27428 });
27429
27430 /** @private */
27431 Roo.TabPanel.prototype.createStrip = function(container){
27432     var strip = document.createElement("div");
27433     strip.className = "x-tabs-wrap";
27434     container.appendChild(strip);
27435     return strip;
27436 };
27437 /** @private */
27438 Roo.TabPanel.prototype.createStripList = function(strip){
27439     // div wrapper for retard IE
27440     // returns the "tr" element.
27441     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27442         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27443         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27444     return strip.firstChild.firstChild.firstChild.firstChild;
27445 };
27446 /** @private */
27447 Roo.TabPanel.prototype.createBody = function(container){
27448     var body = document.createElement("div");
27449     Roo.id(body, "tab-body");
27450     Roo.fly(body).addClass("x-tabs-body");
27451     container.appendChild(body);
27452     return body;
27453 };
27454 /** @private */
27455 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27456     var body = Roo.getDom(id);
27457     if(!body){
27458         body = document.createElement("div");
27459         body.id = id;
27460     }
27461     Roo.fly(body).addClass("x-tabs-item-body");
27462     bodyEl.insertBefore(body, bodyEl.firstChild);
27463     return body;
27464 };
27465 /** @private */
27466 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27467     var td = document.createElement("td");
27468     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27469     //stripEl.appendChild(td);
27470     if(closable){
27471         td.className = "x-tabs-closable";
27472         if(!this.closeTpl){
27473             this.closeTpl = new Roo.Template(
27474                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27475                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27476                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27477             );
27478         }
27479         var el = this.closeTpl.overwrite(td, {"text": text});
27480         var close = el.getElementsByTagName("div")[0];
27481         var inner = el.getElementsByTagName("em")[0];
27482         return {"el": el, "close": close, "inner": inner};
27483     } else {
27484         if(!this.tabTpl){
27485             this.tabTpl = new Roo.Template(
27486                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27487                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27488             );
27489         }
27490         var el = this.tabTpl.overwrite(td, {"text": text});
27491         var inner = el.getElementsByTagName("em")[0];
27492         return {"el": el, "inner": inner};
27493     }
27494 };/*
27495  * Based on:
27496  * Ext JS Library 1.1.1
27497  * Copyright(c) 2006-2007, Ext JS, LLC.
27498  *
27499  * Originally Released Under LGPL - original licence link has changed is not relivant.
27500  *
27501  * Fork - LGPL
27502  * <script type="text/javascript">
27503  */
27504
27505 /**
27506  * @class Roo.Button
27507  * @extends Roo.util.Observable
27508  * Simple Button class
27509  * @cfg {String} text The button text
27510  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27511  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27512  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27513  * @cfg {Object} scope The scope of the handler
27514  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27515  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27516  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27517  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27518  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27519  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27520    applies if enableToggle = true)
27521  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27522  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27523   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27524  * @constructor
27525  * Create a new button
27526  * @param {Object} config The config object
27527  */
27528 Roo.Button = function(renderTo, config)
27529 {
27530     if (!config) {
27531         config = renderTo;
27532         renderTo = config.renderTo || false;
27533     }
27534     
27535     Roo.apply(this, config);
27536     this.addEvents({
27537         /**
27538              * @event click
27539              * Fires when this button is clicked
27540              * @param {Button} this
27541              * @param {EventObject} e The click event
27542              */
27543             "click" : true,
27544         /**
27545              * @event toggle
27546              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27547              * @param {Button} this
27548              * @param {Boolean} pressed
27549              */
27550             "toggle" : true,
27551         /**
27552              * @event mouseover
27553              * Fires when the mouse hovers over the button
27554              * @param {Button} this
27555              * @param {Event} e The event object
27556              */
27557         'mouseover' : true,
27558         /**
27559              * @event mouseout
27560              * Fires when the mouse exits the button
27561              * @param {Button} this
27562              * @param {Event} e The event object
27563              */
27564         'mouseout': true,
27565          /**
27566              * @event render
27567              * Fires when the button is rendered
27568              * @param {Button} this
27569              */
27570         'render': true
27571     });
27572     if(this.menu){
27573         this.menu = Roo.menu.MenuMgr.get(this.menu);
27574     }
27575     // register listeners first!!  - so render can be captured..
27576     Roo.util.Observable.call(this);
27577     if(renderTo){
27578         this.render(renderTo);
27579     }
27580     
27581   
27582 };
27583
27584 Roo.extend(Roo.Button, Roo.util.Observable, {
27585     /**
27586      * 
27587      */
27588     
27589     /**
27590      * Read-only. True if this button is hidden
27591      * @type Boolean
27592      */
27593     hidden : false,
27594     /**
27595      * Read-only. True if this button is disabled
27596      * @type Boolean
27597      */
27598     disabled : false,
27599     /**
27600      * Read-only. True if this button is pressed (only if enableToggle = true)
27601      * @type Boolean
27602      */
27603     pressed : false,
27604
27605     /**
27606      * @cfg {Number} tabIndex 
27607      * The DOM tabIndex for this button (defaults to undefined)
27608      */
27609     tabIndex : undefined,
27610
27611     /**
27612      * @cfg {Boolean} enableToggle
27613      * True to enable pressed/not pressed toggling (defaults to false)
27614      */
27615     enableToggle: false,
27616     /**
27617      * @cfg {Mixed} menu
27618      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27619      */
27620     menu : undefined,
27621     /**
27622      * @cfg {String} menuAlign
27623      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27624      */
27625     menuAlign : "tl-bl?",
27626
27627     /**
27628      * @cfg {String} iconCls
27629      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27630      */
27631     iconCls : undefined,
27632     /**
27633      * @cfg {String} type
27634      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27635      */
27636     type : 'button',
27637
27638     // private
27639     menuClassTarget: 'tr',
27640
27641     /**
27642      * @cfg {String} clickEvent
27643      * The type of event to map to the button's event handler (defaults to 'click')
27644      */
27645     clickEvent : 'click',
27646
27647     /**
27648      * @cfg {Boolean} handleMouseEvents
27649      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27650      */
27651     handleMouseEvents : true,
27652
27653     /**
27654      * @cfg {String} tooltipType
27655      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27656      */
27657     tooltipType : 'qtip',
27658
27659     /**
27660      * @cfg {String} cls
27661      * A CSS class to apply to the button's main element.
27662      */
27663     
27664     /**
27665      * @cfg {Roo.Template} template (Optional)
27666      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27667      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27668      * require code modifications if required elements (e.g. a button) aren't present.
27669      */
27670
27671     // private
27672     render : function(renderTo){
27673         var btn;
27674         if(this.hideParent){
27675             this.parentEl = Roo.get(renderTo);
27676         }
27677         if(!this.dhconfig){
27678             if(!this.template){
27679                 if(!Roo.Button.buttonTemplate){
27680                     // hideous table template
27681                     Roo.Button.buttonTemplate = new Roo.Template(
27682                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27683                         '<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>',
27684                         "</tr></tbody></table>");
27685                 }
27686                 this.template = Roo.Button.buttonTemplate;
27687             }
27688             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27689             var btnEl = btn.child("button:first");
27690             btnEl.on('focus', this.onFocus, this);
27691             btnEl.on('blur', this.onBlur, this);
27692             if(this.cls){
27693                 btn.addClass(this.cls);
27694             }
27695             if(this.icon){
27696                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27697             }
27698             if(this.iconCls){
27699                 btnEl.addClass(this.iconCls);
27700                 if(!this.cls){
27701                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27702                 }
27703             }
27704             if(this.tabIndex !== undefined){
27705                 btnEl.dom.tabIndex = this.tabIndex;
27706             }
27707             if(this.tooltip){
27708                 if(typeof this.tooltip == 'object'){
27709                     Roo.QuickTips.tips(Roo.apply({
27710                           target: btnEl.id
27711                     }, this.tooltip));
27712                 } else {
27713                     btnEl.dom[this.tooltipType] = this.tooltip;
27714                 }
27715             }
27716         }else{
27717             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27718         }
27719         this.el = btn;
27720         if(this.id){
27721             this.el.dom.id = this.el.id = this.id;
27722         }
27723         if(this.menu){
27724             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27725             this.menu.on("show", this.onMenuShow, this);
27726             this.menu.on("hide", this.onMenuHide, this);
27727         }
27728         btn.addClass("x-btn");
27729         if(Roo.isIE && !Roo.isIE7){
27730             this.autoWidth.defer(1, this);
27731         }else{
27732             this.autoWidth();
27733         }
27734         if(this.handleMouseEvents){
27735             btn.on("mouseover", this.onMouseOver, this);
27736             btn.on("mouseout", this.onMouseOut, this);
27737             btn.on("mousedown", this.onMouseDown, this);
27738         }
27739         btn.on(this.clickEvent, this.onClick, this);
27740         //btn.on("mouseup", this.onMouseUp, this);
27741         if(this.hidden){
27742             this.hide();
27743         }
27744         if(this.disabled){
27745             this.disable();
27746         }
27747         Roo.ButtonToggleMgr.register(this);
27748         if(this.pressed){
27749             this.el.addClass("x-btn-pressed");
27750         }
27751         if(this.repeat){
27752             var repeater = new Roo.util.ClickRepeater(btn,
27753                 typeof this.repeat == "object" ? this.repeat : {}
27754             );
27755             repeater.on("click", this.onClick,  this);
27756         }
27757         
27758         this.fireEvent('render', this);
27759         
27760     },
27761     /**
27762      * Returns the button's underlying element
27763      * @return {Roo.Element} The element
27764      */
27765     getEl : function(){
27766         return this.el;  
27767     },
27768     
27769     /**
27770      * Destroys this Button and removes any listeners.
27771      */
27772     destroy : function(){
27773         Roo.ButtonToggleMgr.unregister(this);
27774         this.el.removeAllListeners();
27775         this.purgeListeners();
27776         this.el.remove();
27777     },
27778
27779     // private
27780     autoWidth : function(){
27781         if(this.el){
27782             this.el.setWidth("auto");
27783             if(Roo.isIE7 && Roo.isStrict){
27784                 var ib = this.el.child('button');
27785                 if(ib && ib.getWidth() > 20){
27786                     ib.clip();
27787                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27788                 }
27789             }
27790             if(this.minWidth){
27791                 if(this.hidden){
27792                     this.el.beginMeasure();
27793                 }
27794                 if(this.el.getWidth() < this.minWidth){
27795                     this.el.setWidth(this.minWidth);
27796                 }
27797                 if(this.hidden){
27798                     this.el.endMeasure();
27799                 }
27800             }
27801         }
27802     },
27803
27804     /**
27805      * Assigns this button's click handler
27806      * @param {Function} handler The function to call when the button is clicked
27807      * @param {Object} scope (optional) Scope for the function passed in
27808      */
27809     setHandler : function(handler, scope){
27810         this.handler = handler;
27811         this.scope = scope;  
27812     },
27813     
27814     /**
27815      * Sets this button's text
27816      * @param {String} text The button text
27817      */
27818     setText : function(text){
27819         this.text = text;
27820         if(this.el){
27821             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27822         }
27823         this.autoWidth();
27824     },
27825     
27826     /**
27827      * Gets the text for this button
27828      * @return {String} The button text
27829      */
27830     getText : function(){
27831         return this.text;  
27832     },
27833     
27834     /**
27835      * Show this button
27836      */
27837     show: function(){
27838         this.hidden = false;
27839         if(this.el){
27840             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27841         }
27842     },
27843     
27844     /**
27845      * Hide this button
27846      */
27847     hide: function(){
27848         this.hidden = true;
27849         if(this.el){
27850             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27851         }
27852     },
27853     
27854     /**
27855      * Convenience function for boolean show/hide
27856      * @param {Boolean} visible True to show, false to hide
27857      */
27858     setVisible: function(visible){
27859         if(visible) {
27860             this.show();
27861         }else{
27862             this.hide();
27863         }
27864     },
27865     
27866     /**
27867      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27868      * @param {Boolean} state (optional) Force a particular state
27869      */
27870     toggle : function(state){
27871         state = state === undefined ? !this.pressed : state;
27872         if(state != this.pressed){
27873             if(state){
27874                 this.el.addClass("x-btn-pressed");
27875                 this.pressed = true;
27876                 this.fireEvent("toggle", this, true);
27877             }else{
27878                 this.el.removeClass("x-btn-pressed");
27879                 this.pressed = false;
27880                 this.fireEvent("toggle", this, false);
27881             }
27882             if(this.toggleHandler){
27883                 this.toggleHandler.call(this.scope || this, this, state);
27884             }
27885         }
27886     },
27887     
27888     /**
27889      * Focus the button
27890      */
27891     focus : function(){
27892         this.el.child('button:first').focus();
27893     },
27894     
27895     /**
27896      * Disable this button
27897      */
27898     disable : function(){
27899         if(this.el){
27900             this.el.addClass("x-btn-disabled");
27901         }
27902         this.disabled = true;
27903     },
27904     
27905     /**
27906      * Enable this button
27907      */
27908     enable : function(){
27909         if(this.el){
27910             this.el.removeClass("x-btn-disabled");
27911         }
27912         this.disabled = false;
27913     },
27914
27915     /**
27916      * Convenience function for boolean enable/disable
27917      * @param {Boolean} enabled True to enable, false to disable
27918      */
27919     setDisabled : function(v){
27920         this[v !== true ? "enable" : "disable"]();
27921     },
27922
27923     // private
27924     onClick : function(e)
27925     {
27926         if(e){
27927             e.preventDefault();
27928         }
27929         if(e.button != 0){
27930             return;
27931         }
27932         if(!this.disabled){
27933             if(this.enableToggle){
27934                 this.toggle();
27935             }
27936             if(this.menu && !this.menu.isVisible()){
27937                 this.menu.show(this.el, this.menuAlign);
27938             }
27939             this.fireEvent("click", this, e);
27940             if(this.handler){
27941                 this.el.removeClass("x-btn-over");
27942                 this.handler.call(this.scope || this, this, e);
27943             }
27944         }
27945     },
27946     // private
27947     onMouseOver : function(e){
27948         if(!this.disabled){
27949             this.el.addClass("x-btn-over");
27950             this.fireEvent('mouseover', this, e);
27951         }
27952     },
27953     // private
27954     onMouseOut : function(e){
27955         if(!e.within(this.el,  true)){
27956             this.el.removeClass("x-btn-over");
27957             this.fireEvent('mouseout', this, e);
27958         }
27959     },
27960     // private
27961     onFocus : function(e){
27962         if(!this.disabled){
27963             this.el.addClass("x-btn-focus");
27964         }
27965     },
27966     // private
27967     onBlur : function(e){
27968         this.el.removeClass("x-btn-focus");
27969     },
27970     // private
27971     onMouseDown : function(e){
27972         if(!this.disabled && e.button == 0){
27973             this.el.addClass("x-btn-click");
27974             Roo.get(document).on('mouseup', this.onMouseUp, this);
27975         }
27976     },
27977     // private
27978     onMouseUp : function(e){
27979         if(e.button == 0){
27980             this.el.removeClass("x-btn-click");
27981             Roo.get(document).un('mouseup', this.onMouseUp, this);
27982         }
27983     },
27984     // private
27985     onMenuShow : function(e){
27986         this.el.addClass("x-btn-menu-active");
27987     },
27988     // private
27989     onMenuHide : function(e){
27990         this.el.removeClass("x-btn-menu-active");
27991     }   
27992 });
27993
27994 // Private utility class used by Button
27995 Roo.ButtonToggleMgr = function(){
27996    var groups = {};
27997    
27998    function toggleGroup(btn, state){
27999        if(state){
28000            var g = groups[btn.toggleGroup];
28001            for(var i = 0, l = g.length; i < l; i++){
28002                if(g[i] != btn){
28003                    g[i].toggle(false);
28004                }
28005            }
28006        }
28007    }
28008    
28009    return {
28010        register : function(btn){
28011            if(!btn.toggleGroup){
28012                return;
28013            }
28014            var g = groups[btn.toggleGroup];
28015            if(!g){
28016                g = groups[btn.toggleGroup] = [];
28017            }
28018            g.push(btn);
28019            btn.on("toggle", toggleGroup);
28020        },
28021        
28022        unregister : function(btn){
28023            if(!btn.toggleGroup){
28024                return;
28025            }
28026            var g = groups[btn.toggleGroup];
28027            if(g){
28028                g.remove(btn);
28029                btn.un("toggle", toggleGroup);
28030            }
28031        }
28032    };
28033 }();/*
28034  * Based on:
28035  * Ext JS Library 1.1.1
28036  * Copyright(c) 2006-2007, Ext JS, LLC.
28037  *
28038  * Originally Released Under LGPL - original licence link has changed is not relivant.
28039  *
28040  * Fork - LGPL
28041  * <script type="text/javascript">
28042  */
28043  
28044 /**
28045  * @class Roo.SplitButton
28046  * @extends Roo.Button
28047  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
28048  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
28049  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
28050  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
28051  * @cfg {String} arrowTooltip The title attribute of the arrow
28052  * @constructor
28053  * Create a new menu button
28054  * @param {String/HTMLElement/Element} renderTo The element to append the button to
28055  * @param {Object} config The config object
28056  */
28057 Roo.SplitButton = function(renderTo, config){
28058     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
28059     /**
28060      * @event arrowclick
28061      * Fires when this button's arrow is clicked
28062      * @param {SplitButton} this
28063      * @param {EventObject} e The click event
28064      */
28065     this.addEvents({"arrowclick":true});
28066 };
28067
28068 Roo.extend(Roo.SplitButton, Roo.Button, {
28069     render : function(renderTo){
28070         // this is one sweet looking template!
28071         var tpl = new Roo.Template(
28072             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
28073             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
28074             '<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>',
28075             "</tbody></table></td><td>",
28076             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
28077             '<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>',
28078             "</tbody></table></td></tr></table>"
28079         );
28080         var btn = tpl.append(renderTo, [this.text, this.type], true);
28081         var btnEl = btn.child("button");
28082         if(this.cls){
28083             btn.addClass(this.cls);
28084         }
28085         if(this.icon){
28086             btnEl.setStyle('background-image', 'url(' +this.icon +')');
28087         }
28088         if(this.iconCls){
28089             btnEl.addClass(this.iconCls);
28090             if(!this.cls){
28091                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
28092             }
28093         }
28094         this.el = btn;
28095         if(this.handleMouseEvents){
28096             btn.on("mouseover", this.onMouseOver, this);
28097             btn.on("mouseout", this.onMouseOut, this);
28098             btn.on("mousedown", this.onMouseDown, this);
28099             btn.on("mouseup", this.onMouseUp, this);
28100         }
28101         btn.on(this.clickEvent, this.onClick, this);
28102         if(this.tooltip){
28103             if(typeof this.tooltip == 'object'){
28104                 Roo.QuickTips.tips(Roo.apply({
28105                       target: btnEl.id
28106                 }, this.tooltip));
28107             } else {
28108                 btnEl.dom[this.tooltipType] = this.tooltip;
28109             }
28110         }
28111         if(this.arrowTooltip){
28112             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
28113         }
28114         if(this.hidden){
28115             this.hide();
28116         }
28117         if(this.disabled){
28118             this.disable();
28119         }
28120         if(this.pressed){
28121             this.el.addClass("x-btn-pressed");
28122         }
28123         if(Roo.isIE && !Roo.isIE7){
28124             this.autoWidth.defer(1, this);
28125         }else{
28126             this.autoWidth();
28127         }
28128         if(this.menu){
28129             this.menu.on("show", this.onMenuShow, this);
28130             this.menu.on("hide", this.onMenuHide, this);
28131         }
28132         this.fireEvent('render', this);
28133     },
28134
28135     // private
28136     autoWidth : function(){
28137         if(this.el){
28138             var tbl = this.el.child("table:first");
28139             var tbl2 = this.el.child("table:last");
28140             this.el.setWidth("auto");
28141             tbl.setWidth("auto");
28142             if(Roo.isIE7 && Roo.isStrict){
28143                 var ib = this.el.child('button:first');
28144                 if(ib && ib.getWidth() > 20){
28145                     ib.clip();
28146                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
28147                 }
28148             }
28149             if(this.minWidth){
28150                 if(this.hidden){
28151                     this.el.beginMeasure();
28152                 }
28153                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
28154                     tbl.setWidth(this.minWidth-tbl2.getWidth());
28155                 }
28156                 if(this.hidden){
28157                     this.el.endMeasure();
28158                 }
28159             }
28160             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
28161         } 
28162     },
28163     /**
28164      * Sets this button's click handler
28165      * @param {Function} handler The function to call when the button is clicked
28166      * @param {Object} scope (optional) Scope for the function passed above
28167      */
28168     setHandler : function(handler, scope){
28169         this.handler = handler;
28170         this.scope = scope;  
28171     },
28172     
28173     /**
28174      * Sets this button's arrow click handler
28175      * @param {Function} handler The function to call when the arrow is clicked
28176      * @param {Object} scope (optional) Scope for the function passed above
28177      */
28178     setArrowHandler : function(handler, scope){
28179         this.arrowHandler = handler;
28180         this.scope = scope;  
28181     },
28182     
28183     /**
28184      * Focus the button
28185      */
28186     focus : function(){
28187         if(this.el){
28188             this.el.child("button:first").focus();
28189         }
28190     },
28191
28192     // private
28193     onClick : function(e){
28194         e.preventDefault();
28195         if(!this.disabled){
28196             if(e.getTarget(".x-btn-menu-arrow-wrap")){
28197                 if(this.menu && !this.menu.isVisible()){
28198                     this.menu.show(this.el, this.menuAlign);
28199                 }
28200                 this.fireEvent("arrowclick", this, e);
28201                 if(this.arrowHandler){
28202                     this.arrowHandler.call(this.scope || this, this, e);
28203                 }
28204             }else{
28205                 this.fireEvent("click", this, e);
28206                 if(this.handler){
28207                     this.handler.call(this.scope || this, this, e);
28208                 }
28209             }
28210         }
28211     },
28212     // private
28213     onMouseDown : function(e){
28214         if(!this.disabled){
28215             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28216         }
28217     },
28218     // private
28219     onMouseUp : function(e){
28220         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28221     }   
28222 });
28223
28224
28225 // backwards compat
28226 Roo.MenuButton = Roo.SplitButton;/*
28227  * Based on:
28228  * Ext JS Library 1.1.1
28229  * Copyright(c) 2006-2007, Ext JS, LLC.
28230  *
28231  * Originally Released Under LGPL - original licence link has changed is not relivant.
28232  *
28233  * Fork - LGPL
28234  * <script type="text/javascript">
28235  */
28236
28237 /**
28238  * @class Roo.Toolbar
28239  * Basic Toolbar class.
28240  * @constructor
28241  * Creates a new Toolbar
28242  * @param {Object} container The config object
28243  */ 
28244 Roo.Toolbar = function(container, buttons, config)
28245 {
28246     /// old consturctor format still supported..
28247     if(container instanceof Array){ // omit the container for later rendering
28248         buttons = container;
28249         config = buttons;
28250         container = null;
28251     }
28252     if (typeof(container) == 'object' && container.xtype) {
28253         config = container;
28254         container = config.container;
28255         buttons = config.buttons || []; // not really - use items!!
28256     }
28257     var xitems = [];
28258     if (config && config.items) {
28259         xitems = config.items;
28260         delete config.items;
28261     }
28262     Roo.apply(this, config);
28263     this.buttons = buttons;
28264     
28265     if(container){
28266         this.render(container);
28267     }
28268     this.xitems = xitems;
28269     Roo.each(xitems, function(b) {
28270         this.add(b);
28271     }, this);
28272     
28273 };
28274
28275 Roo.Toolbar.prototype = {
28276     /**
28277      * @cfg {Array} items
28278      * array of button configs or elements to add (will be converted to a MixedCollection)
28279      */
28280     
28281     /**
28282      * @cfg {String/HTMLElement/Element} container
28283      * The id or element that will contain the toolbar
28284      */
28285     // private
28286     render : function(ct){
28287         this.el = Roo.get(ct);
28288         if(this.cls){
28289             this.el.addClass(this.cls);
28290         }
28291         // using a table allows for vertical alignment
28292         // 100% width is needed by Safari...
28293         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28294         this.tr = this.el.child("tr", true);
28295         var autoId = 0;
28296         this.items = new Roo.util.MixedCollection(false, function(o){
28297             return o.id || ("item" + (++autoId));
28298         });
28299         if(this.buttons){
28300             this.add.apply(this, this.buttons);
28301             delete this.buttons;
28302         }
28303     },
28304
28305     /**
28306      * Adds element(s) to the toolbar -- this function takes a variable number of 
28307      * arguments of mixed type and adds them to the toolbar.
28308      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28309      * <ul>
28310      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28311      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28312      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28313      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28314      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28315      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28316      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28317      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28318      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28319      * </ul>
28320      * @param {Mixed} arg2
28321      * @param {Mixed} etc.
28322      */
28323     add : function(){
28324         var a = arguments, l = a.length;
28325         for(var i = 0; i < l; i++){
28326             this._add(a[i]);
28327         }
28328     },
28329     // private..
28330     _add : function(el) {
28331         
28332         if (el.xtype) {
28333             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28334         }
28335         
28336         if (el.applyTo){ // some kind of form field
28337             return this.addField(el);
28338         } 
28339         if (el.render){ // some kind of Toolbar.Item
28340             return this.addItem(el);
28341         }
28342         if (typeof el == "string"){ // string
28343             if(el == "separator" || el == "-"){
28344                 return this.addSeparator();
28345             }
28346             if (el == " "){
28347                 return this.addSpacer();
28348             }
28349             if(el == "->"){
28350                 return this.addFill();
28351             }
28352             return this.addText(el);
28353             
28354         }
28355         if(el.tagName){ // element
28356             return this.addElement(el);
28357         }
28358         if(typeof el == "object"){ // must be button config?
28359             return this.addButton(el);
28360         }
28361         // and now what?!?!
28362         return false;
28363         
28364     },
28365     
28366     /**
28367      * Add an Xtype element
28368      * @param {Object} xtype Xtype Object
28369      * @return {Object} created Object
28370      */
28371     addxtype : function(e){
28372         return this.add(e);  
28373     },
28374     
28375     /**
28376      * Returns the Element for this toolbar.
28377      * @return {Roo.Element}
28378      */
28379     getEl : function(){
28380         return this.el;  
28381     },
28382     
28383     /**
28384      * Adds a separator
28385      * @return {Roo.Toolbar.Item} The separator item
28386      */
28387     addSeparator : function(){
28388         return this.addItem(new Roo.Toolbar.Separator());
28389     },
28390
28391     /**
28392      * Adds a spacer element
28393      * @return {Roo.Toolbar.Spacer} The spacer item
28394      */
28395     addSpacer : function(){
28396         return this.addItem(new Roo.Toolbar.Spacer());
28397     },
28398
28399     /**
28400      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28401      * @return {Roo.Toolbar.Fill} The fill item
28402      */
28403     addFill : function(){
28404         return this.addItem(new Roo.Toolbar.Fill());
28405     },
28406
28407     /**
28408      * Adds any standard HTML element to the toolbar
28409      * @param {String/HTMLElement/Element} el The element or id of the element to add
28410      * @return {Roo.Toolbar.Item} The element's item
28411      */
28412     addElement : function(el){
28413         return this.addItem(new Roo.Toolbar.Item(el));
28414     },
28415     /**
28416      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28417      * @type Roo.util.MixedCollection  
28418      */
28419     items : false,
28420      
28421     /**
28422      * Adds any Toolbar.Item or subclass
28423      * @param {Roo.Toolbar.Item} item
28424      * @return {Roo.Toolbar.Item} The item
28425      */
28426     addItem : function(item){
28427         var td = this.nextBlock();
28428         item.render(td);
28429         this.items.add(item);
28430         return item;
28431     },
28432     
28433     /**
28434      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28435      * @param {Object/Array} config A button config or array of configs
28436      * @return {Roo.Toolbar.Button/Array}
28437      */
28438     addButton : function(config){
28439         if(config instanceof Array){
28440             var buttons = [];
28441             for(var i = 0, len = config.length; i < len; i++) {
28442                 buttons.push(this.addButton(config[i]));
28443             }
28444             return buttons;
28445         }
28446         var b = config;
28447         if(!(config instanceof Roo.Toolbar.Button)){
28448             b = config.split ?
28449                 new Roo.Toolbar.SplitButton(config) :
28450                 new Roo.Toolbar.Button(config);
28451         }
28452         var td = this.nextBlock();
28453         b.render(td);
28454         this.items.add(b);
28455         return b;
28456     },
28457     
28458     /**
28459      * Adds text to the toolbar
28460      * @param {String} text The text to add
28461      * @return {Roo.Toolbar.Item} The element's item
28462      */
28463     addText : function(text){
28464         return this.addItem(new Roo.Toolbar.TextItem(text));
28465     },
28466     
28467     /**
28468      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28469      * @param {Number} index The index where the item is to be inserted
28470      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28471      * @return {Roo.Toolbar.Button/Item}
28472      */
28473     insertButton : function(index, item){
28474         if(item instanceof Array){
28475             var buttons = [];
28476             for(var i = 0, len = item.length; i < len; i++) {
28477                buttons.push(this.insertButton(index + i, item[i]));
28478             }
28479             return buttons;
28480         }
28481         if (!(item instanceof Roo.Toolbar.Button)){
28482            item = new Roo.Toolbar.Button(item);
28483         }
28484         var td = document.createElement("td");
28485         this.tr.insertBefore(td, this.tr.childNodes[index]);
28486         item.render(td);
28487         this.items.insert(index, item);
28488         return item;
28489     },
28490     
28491     /**
28492      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28493      * @param {Object} config
28494      * @return {Roo.Toolbar.Item} The element's item
28495      */
28496     addDom : function(config, returnEl){
28497         var td = this.nextBlock();
28498         Roo.DomHelper.overwrite(td, config);
28499         var ti = new Roo.Toolbar.Item(td.firstChild);
28500         ti.render(td);
28501         this.items.add(ti);
28502         return ti;
28503     },
28504
28505     /**
28506      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28507      * @type Roo.util.MixedCollection  
28508      */
28509     fields : false,
28510     
28511     /**
28512      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28513      * Note: the field should not have been rendered yet. For a field that has already been
28514      * rendered, use {@link #addElement}.
28515      * @param {Roo.form.Field} field
28516      * @return {Roo.ToolbarItem}
28517      */
28518      
28519       
28520     addField : function(field) {
28521         if (!this.fields) {
28522             var autoId = 0;
28523             this.fields = new Roo.util.MixedCollection(false, function(o){
28524                 return o.id || ("item" + (++autoId));
28525             });
28526
28527         }
28528         
28529         var td = this.nextBlock();
28530         field.render(td);
28531         var ti = new Roo.Toolbar.Item(td.firstChild);
28532         ti.render(td);
28533         this.items.add(ti);
28534         this.fields.add(field);
28535         return ti;
28536     },
28537     /**
28538      * Hide the toolbar
28539      * @method hide
28540      */
28541      
28542       
28543     hide : function()
28544     {
28545         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28546         this.el.child('div').hide();
28547     },
28548     /**
28549      * Show the toolbar
28550      * @method show
28551      */
28552     show : function()
28553     {
28554         this.el.child('div').show();
28555     },
28556       
28557     // private
28558     nextBlock : function(){
28559         var td = document.createElement("td");
28560         this.tr.appendChild(td);
28561         return td;
28562     },
28563
28564     // private
28565     destroy : function(){
28566         if(this.items){ // rendered?
28567             Roo.destroy.apply(Roo, this.items.items);
28568         }
28569         if(this.fields){ // rendered?
28570             Roo.destroy.apply(Roo, this.fields.items);
28571         }
28572         Roo.Element.uncache(this.el, this.tr);
28573     }
28574 };
28575
28576 /**
28577  * @class Roo.Toolbar.Item
28578  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28579  * @constructor
28580  * Creates a new Item
28581  * @param {HTMLElement} el 
28582  */
28583 Roo.Toolbar.Item = function(el){
28584     var cfg = {};
28585     if (typeof (el.xtype) != 'undefined') {
28586         cfg = el;
28587         el = cfg.el;
28588     }
28589     
28590     this.el = Roo.getDom(el);
28591     this.id = Roo.id(this.el);
28592     this.hidden = false;
28593     
28594     this.addEvents({
28595          /**
28596              * @event render
28597              * Fires when the button is rendered
28598              * @param {Button} this
28599              */
28600         'render': true
28601     });
28602     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
28603 };
28604 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
28605 //Roo.Toolbar.Item.prototype = {
28606     
28607     /**
28608      * Get this item's HTML Element
28609      * @return {HTMLElement}
28610      */
28611     getEl : function(){
28612        return this.el;  
28613     },
28614
28615     // private
28616     render : function(td){
28617         
28618          this.td = td;
28619         td.appendChild(this.el);
28620         
28621         this.fireEvent('render', this);
28622     },
28623     
28624     /**
28625      * Removes and destroys this item.
28626      */
28627     destroy : function(){
28628         this.td.parentNode.removeChild(this.td);
28629     },
28630     
28631     /**
28632      * Shows this item.
28633      */
28634     show: function(){
28635         this.hidden = false;
28636         this.td.style.display = "";
28637     },
28638     
28639     /**
28640      * Hides this item.
28641      */
28642     hide: function(){
28643         this.hidden = true;
28644         this.td.style.display = "none";
28645     },
28646     
28647     /**
28648      * Convenience function for boolean show/hide.
28649      * @param {Boolean} visible true to show/false to hide
28650      */
28651     setVisible: function(visible){
28652         if(visible) {
28653             this.show();
28654         }else{
28655             this.hide();
28656         }
28657     },
28658     
28659     /**
28660      * Try to focus this item.
28661      */
28662     focus : function(){
28663         Roo.fly(this.el).focus();
28664     },
28665     
28666     /**
28667      * Disables this item.
28668      */
28669     disable : function(){
28670         Roo.fly(this.td).addClass("x-item-disabled");
28671         this.disabled = true;
28672         this.el.disabled = true;
28673     },
28674     
28675     /**
28676      * Enables this item.
28677      */
28678     enable : function(){
28679         Roo.fly(this.td).removeClass("x-item-disabled");
28680         this.disabled = false;
28681         this.el.disabled = false;
28682     }
28683 });
28684
28685
28686 /**
28687  * @class Roo.Toolbar.Separator
28688  * @extends Roo.Toolbar.Item
28689  * A simple toolbar separator class
28690  * @constructor
28691  * Creates a new Separator
28692  */
28693 Roo.Toolbar.Separator = function(cfg){
28694     
28695     var s = document.createElement("span");
28696     s.className = "ytb-sep";
28697     if (cfg) {
28698         cfg.el = s;
28699     }
28700     
28701     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
28702 };
28703 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28704     enable:Roo.emptyFn,
28705     disable:Roo.emptyFn,
28706     focus:Roo.emptyFn
28707 });
28708
28709 /**
28710  * @class Roo.Toolbar.Spacer
28711  * @extends Roo.Toolbar.Item
28712  * A simple element that adds extra horizontal space to a toolbar.
28713  * @constructor
28714  * Creates a new Spacer
28715  */
28716 Roo.Toolbar.Spacer = function(cfg){
28717     var s = document.createElement("div");
28718     s.className = "ytb-spacer";
28719     if (cfg) {
28720         cfg.el = s;
28721     }
28722     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
28723 };
28724 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28725     enable:Roo.emptyFn,
28726     disable:Roo.emptyFn,
28727     focus:Roo.emptyFn
28728 });
28729
28730 /**
28731  * @class Roo.Toolbar.Fill
28732  * @extends Roo.Toolbar.Spacer
28733  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28734  * @constructor
28735  * Creates a new Spacer
28736  */
28737 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28738     // private
28739     render : function(td){
28740         td.style.width = '100%';
28741         Roo.Toolbar.Fill.superclass.render.call(this, td);
28742     }
28743 });
28744
28745 /**
28746  * @class Roo.Toolbar.TextItem
28747  * @extends Roo.Toolbar.Item
28748  * A simple class that renders text directly into a toolbar.
28749  * @constructor
28750  * Creates a new TextItem
28751  * @param {String} text
28752  */
28753 Roo.Toolbar.TextItem = function(cfg){
28754     var  text = cfg || "";
28755     if (typeof(cfg) == 'object') {
28756         text = cfg.text || "";
28757     }  else {
28758         cfg = null;
28759     }
28760     var s = document.createElement("span");
28761     s.className = "ytb-text";
28762     s.innerHTML = text;
28763     if (cfg) {
28764         cfg.el  = s;
28765     }
28766     
28767     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
28768 };
28769 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28770     
28771      
28772     enable:Roo.emptyFn,
28773     disable:Roo.emptyFn,
28774     focus:Roo.emptyFn
28775 });
28776
28777 /**
28778  * @class Roo.Toolbar.Button
28779  * @extends Roo.Button
28780  * A button that renders into a toolbar.
28781  * @constructor
28782  * Creates a new Button
28783  * @param {Object} config A standard {@link Roo.Button} config object
28784  */
28785 Roo.Toolbar.Button = function(config){
28786     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28787 };
28788 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28789     render : function(td){
28790         this.td = td;
28791         Roo.Toolbar.Button.superclass.render.call(this, td);
28792     },
28793     
28794     /**
28795      * Removes and destroys this button
28796      */
28797     destroy : function(){
28798         Roo.Toolbar.Button.superclass.destroy.call(this);
28799         this.td.parentNode.removeChild(this.td);
28800     },
28801     
28802     /**
28803      * Shows this button
28804      */
28805     show: function(){
28806         this.hidden = false;
28807         this.td.style.display = "";
28808     },
28809     
28810     /**
28811      * Hides this button
28812      */
28813     hide: function(){
28814         this.hidden = true;
28815         this.td.style.display = "none";
28816     },
28817
28818     /**
28819      * Disables this item
28820      */
28821     disable : function(){
28822         Roo.fly(this.td).addClass("x-item-disabled");
28823         this.disabled = true;
28824     },
28825
28826     /**
28827      * Enables this item
28828      */
28829     enable : function(){
28830         Roo.fly(this.td).removeClass("x-item-disabled");
28831         this.disabled = false;
28832     }
28833 });
28834 // backwards compat
28835 Roo.ToolbarButton = Roo.Toolbar.Button;
28836
28837 /**
28838  * @class Roo.Toolbar.SplitButton
28839  * @extends Roo.SplitButton
28840  * A menu button that renders into a toolbar.
28841  * @constructor
28842  * Creates a new SplitButton
28843  * @param {Object} config A standard {@link Roo.SplitButton} config object
28844  */
28845 Roo.Toolbar.SplitButton = function(config){
28846     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28847 };
28848 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28849     render : function(td){
28850         this.td = td;
28851         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28852     },
28853     
28854     /**
28855      * Removes and destroys this button
28856      */
28857     destroy : function(){
28858         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28859         this.td.parentNode.removeChild(this.td);
28860     },
28861     
28862     /**
28863      * Shows this button
28864      */
28865     show: function(){
28866         this.hidden = false;
28867         this.td.style.display = "";
28868     },
28869     
28870     /**
28871      * Hides this button
28872      */
28873     hide: function(){
28874         this.hidden = true;
28875         this.td.style.display = "none";
28876     }
28877 });
28878
28879 // backwards compat
28880 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28881  * Based on:
28882  * Ext JS Library 1.1.1
28883  * Copyright(c) 2006-2007, Ext JS, LLC.
28884  *
28885  * Originally Released Under LGPL - original licence link has changed is not relivant.
28886  *
28887  * Fork - LGPL
28888  * <script type="text/javascript">
28889  */
28890  
28891 /**
28892  * @class Roo.PagingToolbar
28893  * @extends Roo.Toolbar
28894  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28895  * @constructor
28896  * Create a new PagingToolbar
28897  * @param {Object} config The config object
28898  */
28899 Roo.PagingToolbar = function(el, ds, config)
28900 {
28901     // old args format still supported... - xtype is prefered..
28902     if (typeof(el) == 'object' && el.xtype) {
28903         // created from xtype...
28904         config = el;
28905         ds = el.dataSource;
28906         el = config.container;
28907     }
28908     var items = [];
28909     if (config.items) {
28910         items = config.items;
28911         config.items = [];
28912     }
28913     
28914     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28915     this.ds = ds;
28916     this.cursor = 0;
28917     this.renderButtons(this.el);
28918     this.bind(ds);
28919     
28920     // supprot items array.
28921    
28922     Roo.each(items, function(e) {
28923         this.add(Roo.factory(e));
28924     },this);
28925     
28926 };
28927
28928 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28929     /**
28930      * @cfg {Roo.data.Store} dataSource
28931      * The underlying data store providing the paged data
28932      */
28933     /**
28934      * @cfg {String/HTMLElement/Element} container
28935      * container The id or element that will contain the toolbar
28936      */
28937     /**
28938      * @cfg {Boolean} displayInfo
28939      * True to display the displayMsg (defaults to false)
28940      */
28941     /**
28942      * @cfg {Number} pageSize
28943      * The number of records to display per page (defaults to 20)
28944      */
28945     pageSize: 20,
28946     /**
28947      * @cfg {String} displayMsg
28948      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28949      */
28950     displayMsg : 'Displaying {0} - {1} of {2}',
28951     /**
28952      * @cfg {String} emptyMsg
28953      * The message to display when no records are found (defaults to "No data to display")
28954      */
28955     emptyMsg : 'No data to display',
28956     /**
28957      * Customizable piece of the default paging text (defaults to "Page")
28958      * @type String
28959      */
28960     beforePageText : "Page",
28961     /**
28962      * Customizable piece of the default paging text (defaults to "of %0")
28963      * @type String
28964      */
28965     afterPageText : "of {0}",
28966     /**
28967      * Customizable piece of the default paging text (defaults to "First Page")
28968      * @type String
28969      */
28970     firstText : "First Page",
28971     /**
28972      * Customizable piece of the default paging text (defaults to "Previous Page")
28973      * @type String
28974      */
28975     prevText : "Previous Page",
28976     /**
28977      * Customizable piece of the default paging text (defaults to "Next Page")
28978      * @type String
28979      */
28980     nextText : "Next Page",
28981     /**
28982      * Customizable piece of the default paging text (defaults to "Last Page")
28983      * @type String
28984      */
28985     lastText : "Last Page",
28986     /**
28987      * Customizable piece of the default paging text (defaults to "Refresh")
28988      * @type String
28989      */
28990     refreshText : "Refresh",
28991
28992     // private
28993     renderButtons : function(el){
28994         Roo.PagingToolbar.superclass.render.call(this, el);
28995         this.first = this.addButton({
28996             tooltip: this.firstText,
28997             cls: "x-btn-icon x-grid-page-first",
28998             disabled: true,
28999             handler: this.onClick.createDelegate(this, ["first"])
29000         });
29001         this.prev = this.addButton({
29002             tooltip: this.prevText,
29003             cls: "x-btn-icon x-grid-page-prev",
29004             disabled: true,
29005             handler: this.onClick.createDelegate(this, ["prev"])
29006         });
29007         //this.addSeparator();
29008         this.add(this.beforePageText);
29009         this.field = Roo.get(this.addDom({
29010            tag: "input",
29011            type: "text",
29012            size: "3",
29013            value: "1",
29014            cls: "x-grid-page-number"
29015         }).el);
29016         this.field.on("keydown", this.onPagingKeydown, this);
29017         this.field.on("focus", function(){this.dom.select();});
29018         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
29019         this.field.setHeight(18);
29020         //this.addSeparator();
29021         this.next = this.addButton({
29022             tooltip: this.nextText,
29023             cls: "x-btn-icon x-grid-page-next",
29024             disabled: true,
29025             handler: this.onClick.createDelegate(this, ["next"])
29026         });
29027         this.last = this.addButton({
29028             tooltip: this.lastText,
29029             cls: "x-btn-icon x-grid-page-last",
29030             disabled: true,
29031             handler: this.onClick.createDelegate(this, ["last"])
29032         });
29033         //this.addSeparator();
29034         this.loading = this.addButton({
29035             tooltip: this.refreshText,
29036             cls: "x-btn-icon x-grid-loading",
29037             handler: this.onClick.createDelegate(this, ["refresh"])
29038         });
29039
29040         if(this.displayInfo){
29041             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
29042         }
29043     },
29044
29045     // private
29046     updateInfo : function(){
29047         if(this.displayEl){
29048             var count = this.ds.getCount();
29049             var msg = count == 0 ?
29050                 this.emptyMsg :
29051                 String.format(
29052                     this.displayMsg,
29053                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
29054                 );
29055             this.displayEl.update(msg);
29056         }
29057     },
29058
29059     // private
29060     onLoad : function(ds, r, o){
29061        this.cursor = o.params ? o.params.start : 0;
29062        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
29063
29064        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
29065        this.field.dom.value = ap;
29066        this.first.setDisabled(ap == 1);
29067        this.prev.setDisabled(ap == 1);
29068        this.next.setDisabled(ap == ps);
29069        this.last.setDisabled(ap == ps);
29070        this.loading.enable();
29071        this.updateInfo();
29072     },
29073
29074     // private
29075     getPageData : function(){
29076         var total = this.ds.getTotalCount();
29077         return {
29078             total : total,
29079             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
29080             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
29081         };
29082     },
29083
29084     // private
29085     onLoadError : function(){
29086         this.loading.enable();
29087     },
29088
29089     // private
29090     onPagingKeydown : function(e){
29091         var k = e.getKey();
29092         var d = this.getPageData();
29093         if(k == e.RETURN){
29094             var v = this.field.dom.value, pageNum;
29095             if(!v || isNaN(pageNum = parseInt(v, 10))){
29096                 this.field.dom.value = d.activePage;
29097                 return;
29098             }
29099             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
29100             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29101             e.stopEvent();
29102         }
29103         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))
29104         {
29105           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
29106           this.field.dom.value = pageNum;
29107           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
29108           e.stopEvent();
29109         }
29110         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29111         {
29112           var v = this.field.dom.value, pageNum; 
29113           var increment = (e.shiftKey) ? 10 : 1;
29114           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
29115             increment *= -1;
29116           }
29117           if(!v || isNaN(pageNum = parseInt(v, 10))) {
29118             this.field.dom.value = d.activePage;
29119             return;
29120           }
29121           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
29122           {
29123             this.field.dom.value = parseInt(v, 10) + increment;
29124             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
29125             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29126           }
29127           e.stopEvent();
29128         }
29129     },
29130
29131     // private
29132     beforeLoad : function(){
29133         if(this.loading){
29134             this.loading.disable();
29135         }
29136     },
29137
29138     // private
29139     onClick : function(which){
29140         var ds = this.ds;
29141         switch(which){
29142             case "first":
29143                 ds.load({params:{start: 0, limit: this.pageSize}});
29144             break;
29145             case "prev":
29146                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
29147             break;
29148             case "next":
29149                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
29150             break;
29151             case "last":
29152                 var total = ds.getTotalCount();
29153                 var extra = total % this.pageSize;
29154                 var lastStart = extra ? (total - extra) : total-this.pageSize;
29155                 ds.load({params:{start: lastStart, limit: this.pageSize}});
29156             break;
29157             case "refresh":
29158                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
29159             break;
29160         }
29161     },
29162
29163     /**
29164      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
29165      * @param {Roo.data.Store} store The data store to unbind
29166      */
29167     unbind : function(ds){
29168         ds.un("beforeload", this.beforeLoad, this);
29169         ds.un("load", this.onLoad, this);
29170         ds.un("loadexception", this.onLoadError, this);
29171         ds.un("remove", this.updateInfo, this);
29172         ds.un("add", this.updateInfo, this);
29173         this.ds = undefined;
29174     },
29175
29176     /**
29177      * Binds the paging toolbar to the specified {@link Roo.data.Store}
29178      * @param {Roo.data.Store} store The data store to bind
29179      */
29180     bind : function(ds){
29181         ds.on("beforeload", this.beforeLoad, this);
29182         ds.on("load", this.onLoad, this);
29183         ds.on("loadexception", this.onLoadError, this);
29184         ds.on("remove", this.updateInfo, this);
29185         ds.on("add", this.updateInfo, this);
29186         this.ds = ds;
29187     }
29188 });/*
29189  * Based on:
29190  * Ext JS Library 1.1.1
29191  * Copyright(c) 2006-2007, Ext JS, LLC.
29192  *
29193  * Originally Released Under LGPL - original licence link has changed is not relivant.
29194  *
29195  * Fork - LGPL
29196  * <script type="text/javascript">
29197  */
29198
29199 /**
29200  * @class Roo.Resizable
29201  * @extends Roo.util.Observable
29202  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
29203  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
29204  * 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
29205  * the element will be wrapped for you automatically.</p>
29206  * <p>Here is the list of valid resize handles:</p>
29207  * <pre>
29208 Value   Description
29209 ------  -------------------
29210  'n'     north
29211  's'     south
29212  'e'     east
29213  'w'     west
29214  'nw'    northwest
29215  'sw'    southwest
29216  'se'    southeast
29217  'ne'    northeast
29218  'hd'    horizontal drag
29219  'all'   all
29220 </pre>
29221  * <p>Here's an example showing the creation of a typical Resizable:</p>
29222  * <pre><code>
29223 var resizer = new Roo.Resizable("element-id", {
29224     handles: 'all',
29225     minWidth: 200,
29226     minHeight: 100,
29227     maxWidth: 500,
29228     maxHeight: 400,
29229     pinned: true
29230 });
29231 resizer.on("resize", myHandler);
29232 </code></pre>
29233  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
29234  * resizer.east.setDisplayed(false);</p>
29235  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
29236  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
29237  * resize operation's new size (defaults to [0, 0])
29238  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
29239  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
29240  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29241  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29242  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29243  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29244  * @cfg {Number} width The width of the element in pixels (defaults to null)
29245  * @cfg {Number} height The height of the element in pixels (defaults to null)
29246  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29247  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29248  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29249  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29250  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29251  * in favor of the handles config option (defaults to false)
29252  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29253  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29254  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29255  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29256  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29257  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29258  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29259  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29260  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29261  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29262  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29263  * @constructor
29264  * Create a new resizable component
29265  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29266  * @param {Object} config configuration options
29267   */
29268 Roo.Resizable = function(el, config)
29269 {
29270     this.el = Roo.get(el);
29271
29272     if(config && config.wrap){
29273         config.resizeChild = this.el;
29274         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29275         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29276         this.el.setStyle("overflow", "hidden");
29277         this.el.setPositioning(config.resizeChild.getPositioning());
29278         config.resizeChild.clearPositioning();
29279         if(!config.width || !config.height){
29280             var csize = config.resizeChild.getSize();
29281             this.el.setSize(csize.width, csize.height);
29282         }
29283         if(config.pinned && !config.adjustments){
29284             config.adjustments = "auto";
29285         }
29286     }
29287
29288     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29289     this.proxy.unselectable();
29290     this.proxy.enableDisplayMode('block');
29291
29292     Roo.apply(this, config);
29293
29294     if(this.pinned){
29295         this.disableTrackOver = true;
29296         this.el.addClass("x-resizable-pinned");
29297     }
29298     // if the element isn't positioned, make it relative
29299     var position = this.el.getStyle("position");
29300     if(position != "absolute" && position != "fixed"){
29301         this.el.setStyle("position", "relative");
29302     }
29303     if(!this.handles){ // no handles passed, must be legacy style
29304         this.handles = 's,e,se';
29305         if(this.multiDirectional){
29306             this.handles += ',n,w';
29307         }
29308     }
29309     if(this.handles == "all"){
29310         this.handles = "n s e w ne nw se sw";
29311     }
29312     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29313     var ps = Roo.Resizable.positions;
29314     for(var i = 0, len = hs.length; i < len; i++){
29315         if(hs[i] && ps[hs[i]]){
29316             var pos = ps[hs[i]];
29317             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29318         }
29319     }
29320     // legacy
29321     this.corner = this.southeast;
29322     
29323     // updateBox = the box can move..
29324     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29325         this.updateBox = true;
29326     }
29327
29328     this.activeHandle = null;
29329
29330     if(this.resizeChild){
29331         if(typeof this.resizeChild == "boolean"){
29332             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29333         }else{
29334             this.resizeChild = Roo.get(this.resizeChild, true);
29335         }
29336     }
29337     
29338     if(this.adjustments == "auto"){
29339         var rc = this.resizeChild;
29340         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29341         if(rc && (hw || hn)){
29342             rc.position("relative");
29343             rc.setLeft(hw ? hw.el.getWidth() : 0);
29344             rc.setTop(hn ? hn.el.getHeight() : 0);
29345         }
29346         this.adjustments = [
29347             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29348             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29349         ];
29350     }
29351
29352     if(this.draggable){
29353         this.dd = this.dynamic ?
29354             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29355         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29356     }
29357
29358     // public events
29359     this.addEvents({
29360         /**
29361          * @event beforeresize
29362          * Fired before resize is allowed. Set enabled to false to cancel resize.
29363          * @param {Roo.Resizable} this
29364          * @param {Roo.EventObject} e The mousedown event
29365          */
29366         "beforeresize" : true,
29367         /**
29368          * @event resizing
29369          * Fired a resizing.
29370          * @param {Roo.Resizable} this
29371          * @param {Number} x The new x position
29372          * @param {Number} y The new y position
29373          * @param {Number} w The new w width
29374          * @param {Number} h The new h hight
29375          * @param {Roo.EventObject} e The mouseup event
29376          */
29377         "resizing" : true,
29378         /**
29379          * @event resize
29380          * Fired after a resize.
29381          * @param {Roo.Resizable} this
29382          * @param {Number} width The new width
29383          * @param {Number} height The new height
29384          * @param {Roo.EventObject} e The mouseup event
29385          */
29386         "resize" : true
29387     });
29388
29389     if(this.width !== null && this.height !== null){
29390         this.resizeTo(this.width, this.height);
29391     }else{
29392         this.updateChildSize();
29393     }
29394     if(Roo.isIE){
29395         this.el.dom.style.zoom = 1;
29396     }
29397     Roo.Resizable.superclass.constructor.call(this);
29398 };
29399
29400 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29401         resizeChild : false,
29402         adjustments : [0, 0],
29403         minWidth : 5,
29404         minHeight : 5,
29405         maxWidth : 10000,
29406         maxHeight : 10000,
29407         enabled : true,
29408         animate : false,
29409         duration : .35,
29410         dynamic : false,
29411         handles : false,
29412         multiDirectional : false,
29413         disableTrackOver : false,
29414         easing : 'easeOutStrong',
29415         widthIncrement : 0,
29416         heightIncrement : 0,
29417         pinned : false,
29418         width : null,
29419         height : null,
29420         preserveRatio : false,
29421         transparent: false,
29422         minX: 0,
29423         minY: 0,
29424         draggable: false,
29425
29426         /**
29427          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29428          */
29429         constrainTo: undefined,
29430         /**
29431          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29432          */
29433         resizeRegion: undefined,
29434
29435
29436     /**
29437      * Perform a manual resize
29438      * @param {Number} width
29439      * @param {Number} height
29440      */
29441     resizeTo : function(width, height){
29442         this.el.setSize(width, height);
29443         this.updateChildSize();
29444         this.fireEvent("resize", this, width, height, null);
29445     },
29446
29447     // private
29448     startSizing : function(e, handle){
29449         this.fireEvent("beforeresize", this, e);
29450         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29451
29452             if(!this.overlay){
29453                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29454                 this.overlay.unselectable();
29455                 this.overlay.enableDisplayMode("block");
29456                 this.overlay.on("mousemove", this.onMouseMove, this);
29457                 this.overlay.on("mouseup", this.onMouseUp, this);
29458             }
29459             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29460
29461             this.resizing = true;
29462             this.startBox = this.el.getBox();
29463             this.startPoint = e.getXY();
29464             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29465                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29466
29467             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29468             this.overlay.show();
29469
29470             if(this.constrainTo) {
29471                 var ct = Roo.get(this.constrainTo);
29472                 this.resizeRegion = ct.getRegion().adjust(
29473                     ct.getFrameWidth('t'),
29474                     ct.getFrameWidth('l'),
29475                     -ct.getFrameWidth('b'),
29476                     -ct.getFrameWidth('r')
29477                 );
29478             }
29479
29480             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29481             this.proxy.show();
29482             this.proxy.setBox(this.startBox);
29483             if(!this.dynamic){
29484                 this.proxy.setStyle('visibility', 'visible');
29485             }
29486         }
29487     },
29488
29489     // private
29490     onMouseDown : function(handle, e){
29491         if(this.enabled){
29492             e.stopEvent();
29493             this.activeHandle = handle;
29494             this.startSizing(e, handle);
29495         }
29496     },
29497
29498     // private
29499     onMouseUp : function(e){
29500         var size = this.resizeElement();
29501         this.resizing = false;
29502         this.handleOut();
29503         this.overlay.hide();
29504         this.proxy.hide();
29505         this.fireEvent("resize", this, size.width, size.height, e);
29506     },
29507
29508     // private
29509     updateChildSize : function(){
29510         
29511         if(this.resizeChild){
29512             var el = this.el;
29513             var child = this.resizeChild;
29514             var adj = this.adjustments;
29515             if(el.dom.offsetWidth){
29516                 var b = el.getSize(true);
29517                 child.setSize(b.width+adj[0], b.height+adj[1]);
29518             }
29519             // Second call here for IE
29520             // The first call enables instant resizing and
29521             // the second call corrects scroll bars if they
29522             // exist
29523             if(Roo.isIE){
29524                 setTimeout(function(){
29525                     if(el.dom.offsetWidth){
29526                         var b = el.getSize(true);
29527                         child.setSize(b.width+adj[0], b.height+adj[1]);
29528                     }
29529                 }, 10);
29530             }
29531         }
29532     },
29533
29534     // private
29535     snap : function(value, inc, min){
29536         if(!inc || !value) {
29537             return value;
29538         }
29539         var newValue = value;
29540         var m = value % inc;
29541         if(m > 0){
29542             if(m > (inc/2)){
29543                 newValue = value + (inc-m);
29544             }else{
29545                 newValue = value - m;
29546             }
29547         }
29548         return Math.max(min, newValue);
29549     },
29550
29551     // private
29552     resizeElement : function(){
29553         var box = this.proxy.getBox();
29554         if(this.updateBox){
29555             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29556         }else{
29557             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29558         }
29559         this.updateChildSize();
29560         if(!this.dynamic){
29561             this.proxy.hide();
29562         }
29563         return box;
29564     },
29565
29566     // private
29567     constrain : function(v, diff, m, mx){
29568         if(v - diff < m){
29569             diff = v - m;
29570         }else if(v - diff > mx){
29571             diff = mx - v;
29572         }
29573         return diff;
29574     },
29575
29576     // private
29577     onMouseMove : function(e){
29578         
29579         if(this.enabled){
29580             try{// try catch so if something goes wrong the user doesn't get hung
29581
29582             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29583                 return;
29584             }
29585
29586             //var curXY = this.startPoint;
29587             var curSize = this.curSize || this.startBox;
29588             var x = this.startBox.x, y = this.startBox.y;
29589             var ox = x, oy = y;
29590             var w = curSize.width, h = curSize.height;
29591             var ow = w, oh = h;
29592             var mw = this.minWidth, mh = this.minHeight;
29593             var mxw = this.maxWidth, mxh = this.maxHeight;
29594             var wi = this.widthIncrement;
29595             var hi = this.heightIncrement;
29596
29597             var eventXY = e.getXY();
29598             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29599             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29600
29601             var pos = this.activeHandle.position;
29602
29603             switch(pos){
29604                 case "east":
29605                     w += diffX;
29606                     w = Math.min(Math.max(mw, w), mxw);
29607                     break;
29608              
29609                 case "south":
29610                     h += diffY;
29611                     h = Math.min(Math.max(mh, h), mxh);
29612                     break;
29613                 case "southeast":
29614                     w += diffX;
29615                     h += diffY;
29616                     w = Math.min(Math.max(mw, w), mxw);
29617                     h = Math.min(Math.max(mh, h), mxh);
29618                     break;
29619                 case "north":
29620                     diffY = this.constrain(h, diffY, mh, mxh);
29621                     y += diffY;
29622                     h -= diffY;
29623                     break;
29624                 case "hdrag":
29625                     
29626                     if (wi) {
29627                         var adiffX = Math.abs(diffX);
29628                         var sub = (adiffX % wi); // how much 
29629                         if (sub > (wi/2)) { // far enough to snap
29630                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29631                         } else {
29632                             // remove difference.. 
29633                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29634                         }
29635                     }
29636                     x += diffX;
29637                     x = Math.max(this.minX, x);
29638                     break;
29639                 case "west":
29640                     diffX = this.constrain(w, diffX, mw, mxw);
29641                     x += diffX;
29642                     w -= diffX;
29643                     break;
29644                 case "northeast":
29645                     w += diffX;
29646                     w = Math.min(Math.max(mw, w), mxw);
29647                     diffY = this.constrain(h, diffY, mh, mxh);
29648                     y += diffY;
29649                     h -= diffY;
29650                     break;
29651                 case "northwest":
29652                     diffX = this.constrain(w, diffX, mw, mxw);
29653                     diffY = this.constrain(h, diffY, mh, mxh);
29654                     y += diffY;
29655                     h -= diffY;
29656                     x += diffX;
29657                     w -= diffX;
29658                     break;
29659                case "southwest":
29660                     diffX = this.constrain(w, diffX, mw, mxw);
29661                     h += diffY;
29662                     h = Math.min(Math.max(mh, h), mxh);
29663                     x += diffX;
29664                     w -= diffX;
29665                     break;
29666             }
29667
29668             var sw = this.snap(w, wi, mw);
29669             var sh = this.snap(h, hi, mh);
29670             if(sw != w || sh != h){
29671                 switch(pos){
29672                     case "northeast":
29673                         y -= sh - h;
29674                     break;
29675                     case "north":
29676                         y -= sh - h;
29677                         break;
29678                     case "southwest":
29679                         x -= sw - w;
29680                     break;
29681                     case "west":
29682                         x -= sw - w;
29683                         break;
29684                     case "northwest":
29685                         x -= sw - w;
29686                         y -= sh - h;
29687                     break;
29688                 }
29689                 w = sw;
29690                 h = sh;
29691             }
29692
29693             if(this.preserveRatio){
29694                 switch(pos){
29695                     case "southeast":
29696                     case "east":
29697                         h = oh * (w/ow);
29698                         h = Math.min(Math.max(mh, h), mxh);
29699                         w = ow * (h/oh);
29700                        break;
29701                     case "south":
29702                         w = ow * (h/oh);
29703                         w = Math.min(Math.max(mw, w), mxw);
29704                         h = oh * (w/ow);
29705                         break;
29706                     case "northeast":
29707                         w = ow * (h/oh);
29708                         w = Math.min(Math.max(mw, w), mxw);
29709                         h = oh * (w/ow);
29710                     break;
29711                     case "north":
29712                         var tw = w;
29713                         w = ow * (h/oh);
29714                         w = Math.min(Math.max(mw, w), mxw);
29715                         h = oh * (w/ow);
29716                         x += (tw - w) / 2;
29717                         break;
29718                     case "southwest":
29719                         h = oh * (w/ow);
29720                         h = Math.min(Math.max(mh, h), mxh);
29721                         var tw = w;
29722                         w = ow * (h/oh);
29723                         x += tw - w;
29724                         break;
29725                     case "west":
29726                         var th = h;
29727                         h = oh * (w/ow);
29728                         h = Math.min(Math.max(mh, h), mxh);
29729                         y += (th - h) / 2;
29730                         var tw = w;
29731                         w = ow * (h/oh);
29732                         x += tw - w;
29733                        break;
29734                     case "northwest":
29735                         var tw = w;
29736                         var th = h;
29737                         h = oh * (w/ow);
29738                         h = Math.min(Math.max(mh, h), mxh);
29739                         w = ow * (h/oh);
29740                         y += th - h;
29741                         x += tw - w;
29742                        break;
29743
29744                 }
29745             }
29746             if (pos == 'hdrag') {
29747                 w = ow;
29748             }
29749             this.proxy.setBounds(x, y, w, h);
29750             if(this.dynamic){
29751                 this.resizeElement();
29752             }
29753             }catch(e){}
29754         }
29755         this.fireEvent("resizing", this, x, y, w, h, e);
29756     },
29757
29758     // private
29759     handleOver : function(){
29760         if(this.enabled){
29761             this.el.addClass("x-resizable-over");
29762         }
29763     },
29764
29765     // private
29766     handleOut : function(){
29767         if(!this.resizing){
29768             this.el.removeClass("x-resizable-over");
29769         }
29770     },
29771
29772     /**
29773      * Returns the element this component is bound to.
29774      * @return {Roo.Element}
29775      */
29776     getEl : function(){
29777         return this.el;
29778     },
29779
29780     /**
29781      * Returns the resizeChild element (or null).
29782      * @return {Roo.Element}
29783      */
29784     getResizeChild : function(){
29785         return this.resizeChild;
29786     },
29787     groupHandler : function()
29788     {
29789         
29790     },
29791     /**
29792      * Destroys this resizable. If the element was wrapped and
29793      * removeEl is not true then the element remains.
29794      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29795      */
29796     destroy : function(removeEl){
29797         this.proxy.remove();
29798         if(this.overlay){
29799             this.overlay.removeAllListeners();
29800             this.overlay.remove();
29801         }
29802         var ps = Roo.Resizable.positions;
29803         for(var k in ps){
29804             if(typeof ps[k] != "function" && this[ps[k]]){
29805                 var h = this[ps[k]];
29806                 h.el.removeAllListeners();
29807                 h.el.remove();
29808             }
29809         }
29810         if(removeEl){
29811             this.el.update("");
29812             this.el.remove();
29813         }
29814     }
29815 });
29816
29817 // private
29818 // hash to map config positions to true positions
29819 Roo.Resizable.positions = {
29820     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29821     hd: "hdrag"
29822 };
29823
29824 // private
29825 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29826     if(!this.tpl){
29827         // only initialize the template if resizable is used
29828         var tpl = Roo.DomHelper.createTemplate(
29829             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29830         );
29831         tpl.compile();
29832         Roo.Resizable.Handle.prototype.tpl = tpl;
29833     }
29834     this.position = pos;
29835     this.rz = rz;
29836     // show north drag fro topdra
29837     var handlepos = pos == 'hdrag' ? 'north' : pos;
29838     
29839     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29840     if (pos == 'hdrag') {
29841         this.el.setStyle('cursor', 'pointer');
29842     }
29843     this.el.unselectable();
29844     if(transparent){
29845         this.el.setOpacity(0);
29846     }
29847     this.el.on("mousedown", this.onMouseDown, this);
29848     if(!disableTrackOver){
29849         this.el.on("mouseover", this.onMouseOver, this);
29850         this.el.on("mouseout", this.onMouseOut, this);
29851     }
29852 };
29853
29854 // private
29855 Roo.Resizable.Handle.prototype = {
29856     afterResize : function(rz){
29857         Roo.log('after?');
29858         // do nothing
29859     },
29860     // private
29861     onMouseDown : function(e){
29862         this.rz.onMouseDown(this, e);
29863     },
29864     // private
29865     onMouseOver : function(e){
29866         this.rz.handleOver(this, e);
29867     },
29868     // private
29869     onMouseOut : function(e){
29870         this.rz.handleOut(this, e);
29871     }
29872 };/*
29873  * Based on:
29874  * Ext JS Library 1.1.1
29875  * Copyright(c) 2006-2007, Ext JS, LLC.
29876  *
29877  * Originally Released Under LGPL - original licence link has changed is not relivant.
29878  *
29879  * Fork - LGPL
29880  * <script type="text/javascript">
29881  */
29882
29883 /**
29884  * @class Roo.Editor
29885  * @extends Roo.Component
29886  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29887  * @constructor
29888  * Create a new Editor
29889  * @param {Roo.form.Field} field The Field object (or descendant)
29890  * @param {Object} config The config object
29891  */
29892 Roo.Editor = function(field, config){
29893     Roo.Editor.superclass.constructor.call(this, config);
29894     this.field = field;
29895     this.addEvents({
29896         /**
29897              * @event beforestartedit
29898              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29899              * false from the handler of this event.
29900              * @param {Editor} this
29901              * @param {Roo.Element} boundEl The underlying element bound to this editor
29902              * @param {Mixed} value The field value being set
29903              */
29904         "beforestartedit" : true,
29905         /**
29906              * @event startedit
29907              * Fires when this editor is displayed
29908              * @param {Roo.Element} boundEl The underlying element bound to this editor
29909              * @param {Mixed} value The starting field value
29910              */
29911         "startedit" : true,
29912         /**
29913              * @event beforecomplete
29914              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29915              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29916              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29917              * event will not fire since no edit actually occurred.
29918              * @param {Editor} this
29919              * @param {Mixed} value The current field value
29920              * @param {Mixed} startValue The original field value
29921              */
29922         "beforecomplete" : true,
29923         /**
29924              * @event complete
29925              * Fires after editing is complete and any changed value has been written to the underlying field.
29926              * @param {Editor} this
29927              * @param {Mixed} value The current field value
29928              * @param {Mixed} startValue The original field value
29929              */
29930         "complete" : true,
29931         /**
29932          * @event specialkey
29933          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29934          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29935          * @param {Roo.form.Field} this
29936          * @param {Roo.EventObject} e The event object
29937          */
29938         "specialkey" : true
29939     });
29940 };
29941
29942 Roo.extend(Roo.Editor, Roo.Component, {
29943     /**
29944      * @cfg {Boolean/String} autosize
29945      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29946      * or "height" to adopt the height only (defaults to false)
29947      */
29948     /**
29949      * @cfg {Boolean} revertInvalid
29950      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29951      * validation fails (defaults to true)
29952      */
29953     /**
29954      * @cfg {Boolean} ignoreNoChange
29955      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29956      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29957      * will never be ignored.
29958      */
29959     /**
29960      * @cfg {Boolean} hideEl
29961      * False to keep the bound element visible while the editor is displayed (defaults to true)
29962      */
29963     /**
29964      * @cfg {Mixed} value
29965      * The data value of the underlying field (defaults to "")
29966      */
29967     value : "",
29968     /**
29969      * @cfg {String} alignment
29970      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29971      */
29972     alignment: "c-c?",
29973     /**
29974      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29975      * for bottom-right shadow (defaults to "frame")
29976      */
29977     shadow : "frame",
29978     /**
29979      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29980      */
29981     constrain : false,
29982     /**
29983      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29984      */
29985     completeOnEnter : false,
29986     /**
29987      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29988      */
29989     cancelOnEsc : false,
29990     /**
29991      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29992      */
29993     updateEl : false,
29994
29995     // private
29996     onRender : function(ct, position){
29997         this.el = new Roo.Layer({
29998             shadow: this.shadow,
29999             cls: "x-editor",
30000             parentEl : ct,
30001             shim : this.shim,
30002             shadowOffset:4,
30003             id: this.id,
30004             constrain: this.constrain
30005         });
30006         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
30007         if(this.field.msgTarget != 'title'){
30008             this.field.msgTarget = 'qtip';
30009         }
30010         this.field.render(this.el);
30011         if(Roo.isGecko){
30012             this.field.el.dom.setAttribute('autocomplete', 'off');
30013         }
30014         this.field.on("specialkey", this.onSpecialKey, this);
30015         if(this.swallowKeys){
30016             this.field.el.swallowEvent(['keydown','keypress']);
30017         }
30018         this.field.show();
30019         this.field.on("blur", this.onBlur, this);
30020         if(this.field.grow){
30021             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
30022         }
30023     },
30024
30025     onSpecialKey : function(field, e)
30026     {
30027         //Roo.log('editor onSpecialKey');
30028         if(this.completeOnEnter && e.getKey() == e.ENTER){
30029             e.stopEvent();
30030             this.completeEdit();
30031             return;
30032         }
30033         // do not fire special key otherwise it might hide close the editor...
30034         if(e.getKey() == e.ENTER){    
30035             return;
30036         }
30037         if(this.cancelOnEsc && e.getKey() == e.ESC){
30038             this.cancelEdit();
30039             return;
30040         } 
30041         this.fireEvent('specialkey', field, e);
30042     
30043     },
30044
30045     /**
30046      * Starts the editing process and shows the editor.
30047      * @param {String/HTMLElement/Element} el The element to edit
30048      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
30049       * to the innerHTML of el.
30050      */
30051     startEdit : function(el, value){
30052         if(this.editing){
30053             this.completeEdit();
30054         }
30055         this.boundEl = Roo.get(el);
30056         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
30057         if(!this.rendered){
30058             this.render(this.parentEl || document.body);
30059         }
30060         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
30061             return;
30062         }
30063         this.startValue = v;
30064         this.field.setValue(v);
30065         if(this.autoSize){
30066             var sz = this.boundEl.getSize();
30067             switch(this.autoSize){
30068                 case "width":
30069                 this.setSize(sz.width,  "");
30070                 break;
30071                 case "height":
30072                 this.setSize("",  sz.height);
30073                 break;
30074                 default:
30075                 this.setSize(sz.width,  sz.height);
30076             }
30077         }
30078         this.el.alignTo(this.boundEl, this.alignment);
30079         this.editing = true;
30080         if(Roo.QuickTips){
30081             Roo.QuickTips.disable();
30082         }
30083         this.show();
30084     },
30085
30086     /**
30087      * Sets the height and width of this editor.
30088      * @param {Number} width The new width
30089      * @param {Number} height The new height
30090      */
30091     setSize : function(w, h){
30092         this.field.setSize(w, h);
30093         if(this.el){
30094             this.el.sync();
30095         }
30096     },
30097
30098     /**
30099      * Realigns the editor to the bound field based on the current alignment config value.
30100      */
30101     realign : function(){
30102         this.el.alignTo(this.boundEl, this.alignment);
30103     },
30104
30105     /**
30106      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
30107      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
30108      */
30109     completeEdit : function(remainVisible){
30110         if(!this.editing){
30111             return;
30112         }
30113         var v = this.getValue();
30114         if(this.revertInvalid !== false && !this.field.isValid()){
30115             v = this.startValue;
30116             this.cancelEdit(true);
30117         }
30118         if(String(v) === String(this.startValue) && this.ignoreNoChange){
30119             this.editing = false;
30120             this.hide();
30121             return;
30122         }
30123         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
30124             this.editing = false;
30125             if(this.updateEl && this.boundEl){
30126                 this.boundEl.update(v);
30127             }
30128             if(remainVisible !== true){
30129                 this.hide();
30130             }
30131             this.fireEvent("complete", this, v, this.startValue);
30132         }
30133     },
30134
30135     // private
30136     onShow : function(){
30137         this.el.show();
30138         if(this.hideEl !== false){
30139             this.boundEl.hide();
30140         }
30141         this.field.show();
30142         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
30143             this.fixIEFocus = true;
30144             this.deferredFocus.defer(50, this);
30145         }else{
30146             this.field.focus();
30147         }
30148         this.fireEvent("startedit", this.boundEl, this.startValue);
30149     },
30150
30151     deferredFocus : function(){
30152         if(this.editing){
30153             this.field.focus();
30154         }
30155     },
30156
30157     /**
30158      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
30159      * reverted to the original starting value.
30160      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
30161      * cancel (defaults to false)
30162      */
30163     cancelEdit : function(remainVisible){
30164         if(this.editing){
30165             this.setValue(this.startValue);
30166             if(remainVisible !== true){
30167                 this.hide();
30168             }
30169         }
30170     },
30171
30172     // private
30173     onBlur : function(){
30174         if(this.allowBlur !== true && this.editing){
30175             this.completeEdit();
30176         }
30177     },
30178
30179     // private
30180     onHide : function(){
30181         if(this.editing){
30182             this.completeEdit();
30183             return;
30184         }
30185         this.field.blur();
30186         if(this.field.collapse){
30187             this.field.collapse();
30188         }
30189         this.el.hide();
30190         if(this.hideEl !== false){
30191             this.boundEl.show();
30192         }
30193         if(Roo.QuickTips){
30194             Roo.QuickTips.enable();
30195         }
30196     },
30197
30198     /**
30199      * Sets the data value of the editor
30200      * @param {Mixed} value Any valid value supported by the underlying field
30201      */
30202     setValue : function(v){
30203         this.field.setValue(v);
30204     },
30205
30206     /**
30207      * Gets the data value of the editor
30208      * @return {Mixed} The data value
30209      */
30210     getValue : function(){
30211         return this.field.getValue();
30212     }
30213 });/*
30214  * Based on:
30215  * Ext JS Library 1.1.1
30216  * Copyright(c) 2006-2007, Ext JS, LLC.
30217  *
30218  * Originally Released Under LGPL - original licence link has changed is not relivant.
30219  *
30220  * Fork - LGPL
30221  * <script type="text/javascript">
30222  */
30223  
30224 /**
30225  * @class Roo.BasicDialog
30226  * @extends Roo.util.Observable
30227  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
30228  * <pre><code>
30229 var dlg = new Roo.BasicDialog("my-dlg", {
30230     height: 200,
30231     width: 300,
30232     minHeight: 100,
30233     minWidth: 150,
30234     modal: true,
30235     proxyDrag: true,
30236     shadow: true
30237 });
30238 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
30239 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
30240 dlg.addButton('Cancel', dlg.hide, dlg);
30241 dlg.show();
30242 </code></pre>
30243   <b>A Dialog should always be a direct child of the body element.</b>
30244  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30245  * @cfg {String} title Default text to display in the title bar (defaults to null)
30246  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30247  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30248  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30249  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30250  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30251  * (defaults to null with no animation)
30252  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30253  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30254  * property for valid values (defaults to 'all')
30255  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30256  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30257  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30258  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30259  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30260  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30261  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30262  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30263  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30264  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30265  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30266  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30267  * draggable = true (defaults to false)
30268  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30269  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30270  * shadow (defaults to false)
30271  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30272  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30273  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30274  * @cfg {Array} buttons Array of buttons
30275  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30276  * @constructor
30277  * Create a new BasicDialog.
30278  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30279  * @param {Object} config Configuration options
30280  */
30281 Roo.BasicDialog = function(el, config){
30282     this.el = Roo.get(el);
30283     var dh = Roo.DomHelper;
30284     if(!this.el && config && config.autoCreate){
30285         if(typeof config.autoCreate == "object"){
30286             if(!config.autoCreate.id){
30287                 config.autoCreate.id = el;
30288             }
30289             this.el = dh.append(document.body,
30290                         config.autoCreate, true);
30291         }else{
30292             this.el = dh.append(document.body,
30293                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30294         }
30295     }
30296     el = this.el;
30297     el.setDisplayed(true);
30298     el.hide = this.hideAction;
30299     this.id = el.id;
30300     el.addClass("x-dlg");
30301
30302     Roo.apply(this, config);
30303
30304     this.proxy = el.createProxy("x-dlg-proxy");
30305     this.proxy.hide = this.hideAction;
30306     this.proxy.setOpacity(.5);
30307     this.proxy.hide();
30308
30309     if(config.width){
30310         el.setWidth(config.width);
30311     }
30312     if(config.height){
30313         el.setHeight(config.height);
30314     }
30315     this.size = el.getSize();
30316     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30317         this.xy = [config.x,config.y];
30318     }else{
30319         this.xy = el.getCenterXY(true);
30320     }
30321     /** The header element @type Roo.Element */
30322     this.header = el.child("> .x-dlg-hd");
30323     /** The body element @type Roo.Element */
30324     this.body = el.child("> .x-dlg-bd");
30325     /** The footer element @type Roo.Element */
30326     this.footer = el.child("> .x-dlg-ft");
30327
30328     if(!this.header){
30329         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30330     }
30331     if(!this.body){
30332         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30333     }
30334
30335     this.header.unselectable();
30336     if(this.title){
30337         this.header.update(this.title);
30338     }
30339     // this element allows the dialog to be focused for keyboard event
30340     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30341     this.focusEl.swallowEvent("click", true);
30342
30343     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30344
30345     // wrap the body and footer for special rendering
30346     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30347     if(this.footer){
30348         this.bwrap.dom.appendChild(this.footer.dom);
30349     }
30350
30351     this.bg = this.el.createChild({
30352         tag: "div", cls:"x-dlg-bg",
30353         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30354     });
30355     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30356
30357
30358     if(this.autoScroll !== false && !this.autoTabs){
30359         this.body.setStyle("overflow", "auto");
30360     }
30361
30362     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30363
30364     if(this.closable !== false){
30365         this.el.addClass("x-dlg-closable");
30366         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30367         this.close.on("click", this.closeClick, this);
30368         this.close.addClassOnOver("x-dlg-close-over");
30369     }
30370     if(this.collapsible !== false){
30371         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30372         this.collapseBtn.on("click", this.collapseClick, this);
30373         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30374         this.header.on("dblclick", this.collapseClick, this);
30375     }
30376     if(this.resizable !== false){
30377         this.el.addClass("x-dlg-resizable");
30378         this.resizer = new Roo.Resizable(el, {
30379             minWidth: this.minWidth || 80,
30380             minHeight:this.minHeight || 80,
30381             handles: this.resizeHandles || "all",
30382             pinned: true
30383         });
30384         this.resizer.on("beforeresize", this.beforeResize, this);
30385         this.resizer.on("resize", this.onResize, this);
30386     }
30387     if(this.draggable !== false){
30388         el.addClass("x-dlg-draggable");
30389         if (!this.proxyDrag) {
30390             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30391         }
30392         else {
30393             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30394         }
30395         dd.setHandleElId(this.header.id);
30396         dd.endDrag = this.endMove.createDelegate(this);
30397         dd.startDrag = this.startMove.createDelegate(this);
30398         dd.onDrag = this.onDrag.createDelegate(this);
30399         dd.scroll = false;
30400         this.dd = dd;
30401     }
30402     if(this.modal){
30403         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30404         this.mask.enableDisplayMode("block");
30405         this.mask.hide();
30406         this.el.addClass("x-dlg-modal");
30407     }
30408     if(this.shadow){
30409         this.shadow = new Roo.Shadow({
30410             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30411             offset : this.shadowOffset
30412         });
30413     }else{
30414         this.shadowOffset = 0;
30415     }
30416     if(Roo.useShims && this.shim !== false){
30417         this.shim = this.el.createShim();
30418         this.shim.hide = this.hideAction;
30419         this.shim.hide();
30420     }else{
30421         this.shim = false;
30422     }
30423     if(this.autoTabs){
30424         this.initTabs();
30425     }
30426     if (this.buttons) { 
30427         var bts= this.buttons;
30428         this.buttons = [];
30429         Roo.each(bts, function(b) {
30430             this.addButton(b);
30431         }, this);
30432     }
30433     
30434     
30435     this.addEvents({
30436         /**
30437          * @event keydown
30438          * Fires when a key is pressed
30439          * @param {Roo.BasicDialog} this
30440          * @param {Roo.EventObject} e
30441          */
30442         "keydown" : true,
30443         /**
30444          * @event move
30445          * Fires when this dialog is moved by the user.
30446          * @param {Roo.BasicDialog} this
30447          * @param {Number} x The new page X
30448          * @param {Number} y The new page Y
30449          */
30450         "move" : true,
30451         /**
30452          * @event resize
30453          * Fires when this dialog is resized by the user.
30454          * @param {Roo.BasicDialog} this
30455          * @param {Number} width The new width
30456          * @param {Number} height The new height
30457          */
30458         "resize" : true,
30459         /**
30460          * @event beforehide
30461          * Fires before this dialog is hidden.
30462          * @param {Roo.BasicDialog} this
30463          */
30464         "beforehide" : true,
30465         /**
30466          * @event hide
30467          * Fires when this dialog is hidden.
30468          * @param {Roo.BasicDialog} this
30469          */
30470         "hide" : true,
30471         /**
30472          * @event beforeshow
30473          * Fires before this dialog is shown.
30474          * @param {Roo.BasicDialog} this
30475          */
30476         "beforeshow" : true,
30477         /**
30478          * @event show
30479          * Fires when this dialog is shown.
30480          * @param {Roo.BasicDialog} this
30481          */
30482         "show" : true
30483     });
30484     el.on("keydown", this.onKeyDown, this);
30485     el.on("mousedown", this.toFront, this);
30486     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30487     this.el.hide();
30488     Roo.DialogManager.register(this);
30489     Roo.BasicDialog.superclass.constructor.call(this);
30490 };
30491
30492 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30493     shadowOffset: Roo.isIE ? 6 : 5,
30494     minHeight: 80,
30495     minWidth: 200,
30496     minButtonWidth: 75,
30497     defaultButton: null,
30498     buttonAlign: "right",
30499     tabTag: 'div',
30500     firstShow: true,
30501
30502     /**
30503      * Sets the dialog title text
30504      * @param {String} text The title text to display
30505      * @return {Roo.BasicDialog} this
30506      */
30507     setTitle : function(text){
30508         this.header.update(text);
30509         return this;
30510     },
30511
30512     // private
30513     closeClick : function(){
30514         this.hide();
30515     },
30516
30517     // private
30518     collapseClick : function(){
30519         this[this.collapsed ? "expand" : "collapse"]();
30520     },
30521
30522     /**
30523      * Collapses the dialog to its minimized state (only the title bar is visible).
30524      * Equivalent to the user clicking the collapse dialog button.
30525      */
30526     collapse : function(){
30527         if(!this.collapsed){
30528             this.collapsed = true;
30529             this.el.addClass("x-dlg-collapsed");
30530             this.restoreHeight = this.el.getHeight();
30531             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30532         }
30533     },
30534
30535     /**
30536      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30537      * clicking the expand dialog button.
30538      */
30539     expand : function(){
30540         if(this.collapsed){
30541             this.collapsed = false;
30542             this.el.removeClass("x-dlg-collapsed");
30543             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30544         }
30545     },
30546
30547     /**
30548      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30549      * @return {Roo.TabPanel} The tabs component
30550      */
30551     initTabs : function(){
30552         var tabs = this.getTabs();
30553         while(tabs.getTab(0)){
30554             tabs.removeTab(0);
30555         }
30556         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30557             var dom = el.dom;
30558             tabs.addTab(Roo.id(dom), dom.title);
30559             dom.title = "";
30560         });
30561         tabs.activate(0);
30562         return tabs;
30563     },
30564
30565     // private
30566     beforeResize : function(){
30567         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30568     },
30569
30570     // private
30571     onResize : function(){
30572         this.refreshSize();
30573         this.syncBodyHeight();
30574         this.adjustAssets();
30575         this.focus();
30576         this.fireEvent("resize", this, this.size.width, this.size.height);
30577     },
30578
30579     // private
30580     onKeyDown : function(e){
30581         if(this.isVisible()){
30582             this.fireEvent("keydown", this, e);
30583         }
30584     },
30585
30586     /**
30587      * Resizes the dialog.
30588      * @param {Number} width
30589      * @param {Number} height
30590      * @return {Roo.BasicDialog} this
30591      */
30592     resizeTo : function(width, height){
30593         this.el.setSize(width, height);
30594         this.size = {width: width, height: height};
30595         this.syncBodyHeight();
30596         if(this.fixedcenter){
30597             this.center();
30598         }
30599         if(this.isVisible()){
30600             this.constrainXY();
30601             this.adjustAssets();
30602         }
30603         this.fireEvent("resize", this, width, height);
30604         return this;
30605     },
30606
30607
30608     /**
30609      * Resizes the dialog to fit the specified content size.
30610      * @param {Number} width
30611      * @param {Number} height
30612      * @return {Roo.BasicDialog} this
30613      */
30614     setContentSize : function(w, h){
30615         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30616         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30617         //if(!this.el.isBorderBox()){
30618             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30619             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30620         //}
30621         if(this.tabs){
30622             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30623             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30624         }
30625         this.resizeTo(w, h);
30626         return this;
30627     },
30628
30629     /**
30630      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30631      * executed in response to a particular key being pressed while the dialog is active.
30632      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30633      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30634      * @param {Function} fn The function to call
30635      * @param {Object} scope (optional) The scope of the function
30636      * @return {Roo.BasicDialog} this
30637      */
30638     addKeyListener : function(key, fn, scope){
30639         var keyCode, shift, ctrl, alt;
30640         if(typeof key == "object" && !(key instanceof Array)){
30641             keyCode = key["key"];
30642             shift = key["shift"];
30643             ctrl = key["ctrl"];
30644             alt = key["alt"];
30645         }else{
30646             keyCode = key;
30647         }
30648         var handler = function(dlg, e){
30649             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30650                 var k = e.getKey();
30651                 if(keyCode instanceof Array){
30652                     for(var i = 0, len = keyCode.length; i < len; i++){
30653                         if(keyCode[i] == k){
30654                           fn.call(scope || window, dlg, k, e);
30655                           return;
30656                         }
30657                     }
30658                 }else{
30659                     if(k == keyCode){
30660                         fn.call(scope || window, dlg, k, e);
30661                     }
30662                 }
30663             }
30664         };
30665         this.on("keydown", handler);
30666         return this;
30667     },
30668
30669     /**
30670      * Returns the TabPanel component (creates it if it doesn't exist).
30671      * Note: If you wish to simply check for the existence of tabs without creating them,
30672      * check for a null 'tabs' property.
30673      * @return {Roo.TabPanel} The tabs component
30674      */
30675     getTabs : function(){
30676         if(!this.tabs){
30677             this.el.addClass("x-dlg-auto-tabs");
30678             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30679             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30680         }
30681         return this.tabs;
30682     },
30683
30684     /**
30685      * Adds a button to the footer section of the dialog.
30686      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30687      * object or a valid Roo.DomHelper element config
30688      * @param {Function} handler The function called when the button is clicked
30689      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30690      * @return {Roo.Button} The new button
30691      */
30692     addButton : function(config, handler, scope){
30693         var dh = Roo.DomHelper;
30694         if(!this.footer){
30695             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30696         }
30697         if(!this.btnContainer){
30698             var tb = this.footer.createChild({
30699
30700                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30701                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30702             }, null, true);
30703             this.btnContainer = tb.firstChild.firstChild.firstChild;
30704         }
30705         var bconfig = {
30706             handler: handler,
30707             scope: scope,
30708             minWidth: this.minButtonWidth,
30709             hideParent:true
30710         };
30711         if(typeof config == "string"){
30712             bconfig.text = config;
30713         }else{
30714             if(config.tag){
30715                 bconfig.dhconfig = config;
30716             }else{
30717                 Roo.apply(bconfig, config);
30718             }
30719         }
30720         var fc = false;
30721         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30722             bconfig.position = Math.max(0, bconfig.position);
30723             fc = this.btnContainer.childNodes[bconfig.position];
30724         }
30725          
30726         var btn = new Roo.Button(
30727             fc ? 
30728                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30729                 : this.btnContainer.appendChild(document.createElement("td")),
30730             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30731             bconfig
30732         );
30733         this.syncBodyHeight();
30734         if(!this.buttons){
30735             /**
30736              * Array of all the buttons that have been added to this dialog via addButton
30737              * @type Array
30738              */
30739             this.buttons = [];
30740         }
30741         this.buttons.push(btn);
30742         return btn;
30743     },
30744
30745     /**
30746      * Sets the default button to be focused when the dialog is displayed.
30747      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30748      * @return {Roo.BasicDialog} this
30749      */
30750     setDefaultButton : function(btn){
30751         this.defaultButton = btn;
30752         return this;
30753     },
30754
30755     // private
30756     getHeaderFooterHeight : function(safe){
30757         var height = 0;
30758         if(this.header){
30759            height += this.header.getHeight();
30760         }
30761         if(this.footer){
30762            var fm = this.footer.getMargins();
30763             height += (this.footer.getHeight()+fm.top+fm.bottom);
30764         }
30765         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30766         height += this.centerBg.getPadding("tb");
30767         return height;
30768     },
30769
30770     // private
30771     syncBodyHeight : function()
30772     {
30773         var bd = this.body, // the text
30774             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30775             bw = this.bwrap;
30776         var height = this.size.height - this.getHeaderFooterHeight(false);
30777         bd.setHeight(height-bd.getMargins("tb"));
30778         var hh = this.header.getHeight();
30779         var h = this.size.height-hh;
30780         cb.setHeight(h);
30781         
30782         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30783         bw.setHeight(h-cb.getPadding("tb"));
30784         
30785         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30786         bd.setWidth(bw.getWidth(true));
30787         if(this.tabs){
30788             this.tabs.syncHeight();
30789             if(Roo.isIE){
30790                 this.tabs.el.repaint();
30791             }
30792         }
30793     },
30794
30795     /**
30796      * Restores the previous state of the dialog if Roo.state is configured.
30797      * @return {Roo.BasicDialog} this
30798      */
30799     restoreState : function(){
30800         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30801         if(box && box.width){
30802             this.xy = [box.x, box.y];
30803             this.resizeTo(box.width, box.height);
30804         }
30805         return this;
30806     },
30807
30808     // private
30809     beforeShow : function(){
30810         this.expand();
30811         if(this.fixedcenter){
30812             this.xy = this.el.getCenterXY(true);
30813         }
30814         if(this.modal){
30815             Roo.get(document.body).addClass("x-body-masked");
30816             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30817             this.mask.show();
30818         }
30819         this.constrainXY();
30820     },
30821
30822     // private
30823     animShow : function(){
30824         var b = Roo.get(this.animateTarget).getBox();
30825         this.proxy.setSize(b.width, b.height);
30826         this.proxy.setLocation(b.x, b.y);
30827         this.proxy.show();
30828         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30829                     true, .35, this.showEl.createDelegate(this));
30830     },
30831
30832     /**
30833      * Shows the dialog.
30834      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30835      * @return {Roo.BasicDialog} this
30836      */
30837     show : function(animateTarget){
30838         if (this.fireEvent("beforeshow", this) === false){
30839             return;
30840         }
30841         if(this.syncHeightBeforeShow){
30842             this.syncBodyHeight();
30843         }else if(this.firstShow){
30844             this.firstShow = false;
30845             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30846         }
30847         this.animateTarget = animateTarget || this.animateTarget;
30848         if(!this.el.isVisible()){
30849             this.beforeShow();
30850             if(this.animateTarget && Roo.get(this.animateTarget)){
30851                 this.animShow();
30852             }else{
30853                 this.showEl();
30854             }
30855         }
30856         return this;
30857     },
30858
30859     // private
30860     showEl : function(){
30861         this.proxy.hide();
30862         this.el.setXY(this.xy);
30863         this.el.show();
30864         this.adjustAssets(true);
30865         this.toFront();
30866         this.focus();
30867         // IE peekaboo bug - fix found by Dave Fenwick
30868         if(Roo.isIE){
30869             this.el.repaint();
30870         }
30871         this.fireEvent("show", this);
30872     },
30873
30874     /**
30875      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30876      * dialog itself will receive focus.
30877      */
30878     focus : function(){
30879         if(this.defaultButton){
30880             this.defaultButton.focus();
30881         }else{
30882             this.focusEl.focus();
30883         }
30884     },
30885
30886     // private
30887     constrainXY : function(){
30888         if(this.constraintoviewport !== false){
30889             if(!this.viewSize){
30890                 if(this.container){
30891                     var s = this.container.getSize();
30892                     this.viewSize = [s.width, s.height];
30893                 }else{
30894                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30895                 }
30896             }
30897             var s = Roo.get(this.container||document).getScroll();
30898
30899             var x = this.xy[0], y = this.xy[1];
30900             var w = this.size.width, h = this.size.height;
30901             var vw = this.viewSize[0], vh = this.viewSize[1];
30902             // only move it if it needs it
30903             var moved = false;
30904             // first validate right/bottom
30905             if(x + w > vw+s.left){
30906                 x = vw - w;
30907                 moved = true;
30908             }
30909             if(y + h > vh+s.top){
30910                 y = vh - h;
30911                 moved = true;
30912             }
30913             // then make sure top/left isn't negative
30914             if(x < s.left){
30915                 x = s.left;
30916                 moved = true;
30917             }
30918             if(y < s.top){
30919                 y = s.top;
30920                 moved = true;
30921             }
30922             if(moved){
30923                 // cache xy
30924                 this.xy = [x, y];
30925                 if(this.isVisible()){
30926                     this.el.setLocation(x, y);
30927                     this.adjustAssets();
30928                 }
30929             }
30930         }
30931     },
30932
30933     // private
30934     onDrag : function(){
30935         if(!this.proxyDrag){
30936             this.xy = this.el.getXY();
30937             this.adjustAssets();
30938         }
30939     },
30940
30941     // private
30942     adjustAssets : function(doShow){
30943         var x = this.xy[0], y = this.xy[1];
30944         var w = this.size.width, h = this.size.height;
30945         if(doShow === true){
30946             if(this.shadow){
30947                 this.shadow.show(this.el);
30948             }
30949             if(this.shim){
30950                 this.shim.show();
30951             }
30952         }
30953         if(this.shadow && this.shadow.isVisible()){
30954             this.shadow.show(this.el);
30955         }
30956         if(this.shim && this.shim.isVisible()){
30957             this.shim.setBounds(x, y, w, h);
30958         }
30959     },
30960
30961     // private
30962     adjustViewport : function(w, h){
30963         if(!w || !h){
30964             w = Roo.lib.Dom.getViewWidth();
30965             h = Roo.lib.Dom.getViewHeight();
30966         }
30967         // cache the size
30968         this.viewSize = [w, h];
30969         if(this.modal && this.mask.isVisible()){
30970             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30971             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30972         }
30973         if(this.isVisible()){
30974             this.constrainXY();
30975         }
30976     },
30977
30978     /**
30979      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30980      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30981      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30982      */
30983     destroy : function(removeEl){
30984         if(this.isVisible()){
30985             this.animateTarget = null;
30986             this.hide();
30987         }
30988         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30989         if(this.tabs){
30990             this.tabs.destroy(removeEl);
30991         }
30992         Roo.destroy(
30993              this.shim,
30994              this.proxy,
30995              this.resizer,
30996              this.close,
30997              this.mask
30998         );
30999         if(this.dd){
31000             this.dd.unreg();
31001         }
31002         if(this.buttons){
31003            for(var i = 0, len = this.buttons.length; i < len; i++){
31004                this.buttons[i].destroy();
31005            }
31006         }
31007         this.el.removeAllListeners();
31008         if(removeEl === true){
31009             this.el.update("");
31010             this.el.remove();
31011         }
31012         Roo.DialogManager.unregister(this);
31013     },
31014
31015     // private
31016     startMove : function(){
31017         if(this.proxyDrag){
31018             this.proxy.show();
31019         }
31020         if(this.constraintoviewport !== false){
31021             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
31022         }
31023     },
31024
31025     // private
31026     endMove : function(){
31027         if(!this.proxyDrag){
31028             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
31029         }else{
31030             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
31031             this.proxy.hide();
31032         }
31033         this.refreshSize();
31034         this.adjustAssets();
31035         this.focus();
31036         this.fireEvent("move", this, this.xy[0], this.xy[1]);
31037     },
31038
31039     /**
31040      * Brings this dialog to the front of any other visible dialogs
31041      * @return {Roo.BasicDialog} this
31042      */
31043     toFront : function(){
31044         Roo.DialogManager.bringToFront(this);
31045         return this;
31046     },
31047
31048     /**
31049      * Sends this dialog to the back (under) of any other visible dialogs
31050      * @return {Roo.BasicDialog} this
31051      */
31052     toBack : function(){
31053         Roo.DialogManager.sendToBack(this);
31054         return this;
31055     },
31056
31057     /**
31058      * Centers this dialog in the viewport
31059      * @return {Roo.BasicDialog} this
31060      */
31061     center : function(){
31062         var xy = this.el.getCenterXY(true);
31063         this.moveTo(xy[0], xy[1]);
31064         return this;
31065     },
31066
31067     /**
31068      * Moves the dialog's top-left corner to the specified point
31069      * @param {Number} x
31070      * @param {Number} y
31071      * @return {Roo.BasicDialog} this
31072      */
31073     moveTo : function(x, y){
31074         this.xy = [x,y];
31075         if(this.isVisible()){
31076             this.el.setXY(this.xy);
31077             this.adjustAssets();
31078         }
31079         return this;
31080     },
31081
31082     /**
31083      * Aligns the dialog to the specified element
31084      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31085      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
31086      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31087      * @return {Roo.BasicDialog} this
31088      */
31089     alignTo : function(element, position, offsets){
31090         this.xy = this.el.getAlignToXY(element, position, offsets);
31091         if(this.isVisible()){
31092             this.el.setXY(this.xy);
31093             this.adjustAssets();
31094         }
31095         return this;
31096     },
31097
31098     /**
31099      * Anchors an element to another element and realigns it when the window is resized.
31100      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31101      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
31102      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31103      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
31104      * is a number, it is used as the buffer delay (defaults to 50ms).
31105      * @return {Roo.BasicDialog} this
31106      */
31107     anchorTo : function(el, alignment, offsets, monitorScroll){
31108         var action = function(){
31109             this.alignTo(el, alignment, offsets);
31110         };
31111         Roo.EventManager.onWindowResize(action, this);
31112         var tm = typeof monitorScroll;
31113         if(tm != 'undefined'){
31114             Roo.EventManager.on(window, 'scroll', action, this,
31115                 {buffer: tm == 'number' ? monitorScroll : 50});
31116         }
31117         action.call(this);
31118         return this;
31119     },
31120
31121     /**
31122      * Returns true if the dialog is visible
31123      * @return {Boolean}
31124      */
31125     isVisible : function(){
31126         return this.el.isVisible();
31127     },
31128
31129     // private
31130     animHide : function(callback){
31131         var b = Roo.get(this.animateTarget).getBox();
31132         this.proxy.show();
31133         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
31134         this.el.hide();
31135         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
31136                     this.hideEl.createDelegate(this, [callback]));
31137     },
31138
31139     /**
31140      * Hides the dialog.
31141      * @param {Function} callback (optional) Function to call when the dialog is hidden
31142      * @return {Roo.BasicDialog} this
31143      */
31144     hide : function(callback){
31145         if (this.fireEvent("beforehide", this) === false){
31146             return;
31147         }
31148         if(this.shadow){
31149             this.shadow.hide();
31150         }
31151         if(this.shim) {
31152           this.shim.hide();
31153         }
31154         // sometimes animateTarget seems to get set.. causing problems...
31155         // this just double checks..
31156         if(this.animateTarget && Roo.get(this.animateTarget)) {
31157            this.animHide(callback);
31158         }else{
31159             this.el.hide();
31160             this.hideEl(callback);
31161         }
31162         return this;
31163     },
31164
31165     // private
31166     hideEl : function(callback){
31167         this.proxy.hide();
31168         if(this.modal){
31169             this.mask.hide();
31170             Roo.get(document.body).removeClass("x-body-masked");
31171         }
31172         this.fireEvent("hide", this);
31173         if(typeof callback == "function"){
31174             callback();
31175         }
31176     },
31177
31178     // private
31179     hideAction : function(){
31180         this.setLeft("-10000px");
31181         this.setTop("-10000px");
31182         this.setStyle("visibility", "hidden");
31183     },
31184
31185     // private
31186     refreshSize : function(){
31187         this.size = this.el.getSize();
31188         this.xy = this.el.getXY();
31189         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
31190     },
31191
31192     // private
31193     // z-index is managed by the DialogManager and may be overwritten at any time
31194     setZIndex : function(index){
31195         if(this.modal){
31196             this.mask.setStyle("z-index", index);
31197         }
31198         if(this.shim){
31199             this.shim.setStyle("z-index", ++index);
31200         }
31201         if(this.shadow){
31202             this.shadow.setZIndex(++index);
31203         }
31204         this.el.setStyle("z-index", ++index);
31205         if(this.proxy){
31206             this.proxy.setStyle("z-index", ++index);
31207         }
31208         if(this.resizer){
31209             this.resizer.proxy.setStyle("z-index", ++index);
31210         }
31211
31212         this.lastZIndex = index;
31213     },
31214
31215     /**
31216      * Returns the element for this dialog
31217      * @return {Roo.Element} The underlying dialog Element
31218      */
31219     getEl : function(){
31220         return this.el;
31221     }
31222 });
31223
31224 /**
31225  * @class Roo.DialogManager
31226  * Provides global access to BasicDialogs that have been created and
31227  * support for z-indexing (layering) multiple open dialogs.
31228  */
31229 Roo.DialogManager = function(){
31230     var list = {};
31231     var accessList = [];
31232     var front = null;
31233
31234     // private
31235     var sortDialogs = function(d1, d2){
31236         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
31237     };
31238
31239     // private
31240     var orderDialogs = function(){
31241         accessList.sort(sortDialogs);
31242         var seed = Roo.DialogManager.zseed;
31243         for(var i = 0, len = accessList.length; i < len; i++){
31244             var dlg = accessList[i];
31245             if(dlg){
31246                 dlg.setZIndex(seed + (i*10));
31247             }
31248         }
31249     };
31250
31251     return {
31252         /**
31253          * The starting z-index for BasicDialogs (defaults to 9000)
31254          * @type Number The z-index value
31255          */
31256         zseed : 9000,
31257
31258         // private
31259         register : function(dlg){
31260             list[dlg.id] = dlg;
31261             accessList.push(dlg);
31262         },
31263
31264         // private
31265         unregister : function(dlg){
31266             delete list[dlg.id];
31267             var i=0;
31268             var len=0;
31269             if(!accessList.indexOf){
31270                 for(  i = 0, len = accessList.length; i < len; i++){
31271                     if(accessList[i] == dlg){
31272                         accessList.splice(i, 1);
31273                         return;
31274                     }
31275                 }
31276             }else{
31277                  i = accessList.indexOf(dlg);
31278                 if(i != -1){
31279                     accessList.splice(i, 1);
31280                 }
31281             }
31282         },
31283
31284         /**
31285          * Gets a registered dialog by id
31286          * @param {String/Object} id The id of the dialog or a dialog
31287          * @return {Roo.BasicDialog} this
31288          */
31289         get : function(id){
31290             return typeof id == "object" ? id : list[id];
31291         },
31292
31293         /**
31294          * Brings the specified dialog to the front
31295          * @param {String/Object} dlg The id of the dialog or a dialog
31296          * @return {Roo.BasicDialog} this
31297          */
31298         bringToFront : function(dlg){
31299             dlg = this.get(dlg);
31300             if(dlg != front){
31301                 front = dlg;
31302                 dlg._lastAccess = new Date().getTime();
31303                 orderDialogs();
31304             }
31305             return dlg;
31306         },
31307
31308         /**
31309          * Sends the specified dialog to the back
31310          * @param {String/Object} dlg The id of the dialog or a dialog
31311          * @return {Roo.BasicDialog} this
31312          */
31313         sendToBack : function(dlg){
31314             dlg = this.get(dlg);
31315             dlg._lastAccess = -(new Date().getTime());
31316             orderDialogs();
31317             return dlg;
31318         },
31319
31320         /**
31321          * Hides all dialogs
31322          */
31323         hideAll : function(){
31324             for(var id in list){
31325                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31326                     list[id].hide();
31327                 }
31328             }
31329         }
31330     };
31331 }();
31332
31333 /**
31334  * @class Roo.LayoutDialog
31335  * @extends Roo.BasicDialog
31336  * Dialog which provides adjustments for working with a layout in a Dialog.
31337  * Add your necessary layout config options to the dialog's config.<br>
31338  * Example usage (including a nested layout):
31339  * <pre><code>
31340 if(!dialog){
31341     dialog = new Roo.LayoutDialog("download-dlg", {
31342         modal: true,
31343         width:600,
31344         height:450,
31345         shadow:true,
31346         minWidth:500,
31347         minHeight:350,
31348         autoTabs:true,
31349         proxyDrag:true,
31350         // layout config merges with the dialog config
31351         center:{
31352             tabPosition: "top",
31353             alwaysShowTabs: true
31354         }
31355     });
31356     dialog.addKeyListener(27, dialog.hide, dialog);
31357     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31358     dialog.addButton("Build It!", this.getDownload, this);
31359
31360     // we can even add nested layouts
31361     var innerLayout = new Roo.BorderLayout("dl-inner", {
31362         east: {
31363             initialSize: 200,
31364             autoScroll:true,
31365             split:true
31366         },
31367         center: {
31368             autoScroll:true
31369         }
31370     });
31371     innerLayout.beginUpdate();
31372     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31373     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31374     innerLayout.endUpdate(true);
31375
31376     var layout = dialog.getLayout();
31377     layout.beginUpdate();
31378     layout.add("center", new Roo.ContentPanel("standard-panel",
31379                         {title: "Download the Source", fitToFrame:true}));
31380     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31381                {title: "Build your own roo.js"}));
31382     layout.getRegion("center").showPanel(sp);
31383     layout.endUpdate();
31384 }
31385 </code></pre>
31386     * @constructor
31387     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31388     * @param {Object} config configuration options
31389   */
31390 Roo.LayoutDialog = function(el, cfg){
31391     
31392     var config=  cfg;
31393     if (typeof(cfg) == 'undefined') {
31394         config = Roo.apply({}, el);
31395         // not sure why we use documentElement here.. - it should always be body.
31396         // IE7 borks horribly if we use documentElement.
31397         // webkit also does not like documentElement - it creates a body element...
31398         el = Roo.get( document.body || document.documentElement ).createChild();
31399         //config.autoCreate = true;
31400     }
31401     
31402     
31403     config.autoTabs = false;
31404     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31405     this.body.setStyle({overflow:"hidden", position:"relative"});
31406     this.layout = new Roo.BorderLayout(this.body.dom, config);
31407     this.layout.monitorWindowResize = false;
31408     this.el.addClass("x-dlg-auto-layout");
31409     // fix case when center region overwrites center function
31410     this.center = Roo.BasicDialog.prototype.center;
31411     this.on("show", this.layout.layout, this.layout, true);
31412     if (config.items) {
31413         var xitems = config.items;
31414         delete config.items;
31415         Roo.each(xitems, this.addxtype, this);
31416     }
31417     
31418     
31419 };
31420 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31421     /**
31422      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31423      * @deprecated
31424      */
31425     endUpdate : function(){
31426         this.layout.endUpdate();
31427     },
31428
31429     /**
31430      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31431      *  @deprecated
31432      */
31433     beginUpdate : function(){
31434         this.layout.beginUpdate();
31435     },
31436
31437     /**
31438      * Get the BorderLayout for this dialog
31439      * @return {Roo.BorderLayout}
31440      */
31441     getLayout : function(){
31442         return this.layout;
31443     },
31444
31445     showEl : function(){
31446         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31447         if(Roo.isIE7){
31448             this.layout.layout();
31449         }
31450     },
31451
31452     // private
31453     // Use the syncHeightBeforeShow config option to control this automatically
31454     syncBodyHeight : function(){
31455         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31456         if(this.layout){this.layout.layout();}
31457     },
31458     
31459       /**
31460      * Add an xtype element (actually adds to the layout.)
31461      * @return {Object} xdata xtype object data.
31462      */
31463     
31464     addxtype : function(c) {
31465         return this.layout.addxtype(c);
31466     }
31467 });/*
31468  * Based on:
31469  * Ext JS Library 1.1.1
31470  * Copyright(c) 2006-2007, Ext JS, LLC.
31471  *
31472  * Originally Released Under LGPL - original licence link has changed is not relivant.
31473  *
31474  * Fork - LGPL
31475  * <script type="text/javascript">
31476  */
31477  
31478 /**
31479  * @class Roo.MessageBox
31480  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31481  * Example usage:
31482  *<pre><code>
31483 // Basic alert:
31484 Roo.Msg.alert('Status', 'Changes saved successfully.');
31485
31486 // Prompt for user data:
31487 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31488     if (btn == 'ok'){
31489         // process text value...
31490     }
31491 });
31492
31493 // Show a dialog using config options:
31494 Roo.Msg.show({
31495    title:'Save Changes?',
31496    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31497    buttons: Roo.Msg.YESNOCANCEL,
31498    fn: processResult,
31499    animEl: 'elId'
31500 });
31501 </code></pre>
31502  * @singleton
31503  */
31504 Roo.MessageBox = function(){
31505     var dlg, opt, mask, waitTimer;
31506     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31507     var buttons, activeTextEl, bwidth;
31508
31509     // private
31510     var handleButton = function(button){
31511         dlg.hide();
31512         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31513     };
31514
31515     // private
31516     var handleHide = function(){
31517         if(opt && opt.cls){
31518             dlg.el.removeClass(opt.cls);
31519         }
31520         if(waitTimer){
31521             Roo.TaskMgr.stop(waitTimer);
31522             waitTimer = null;
31523         }
31524     };
31525
31526     // private
31527     var updateButtons = function(b){
31528         var width = 0;
31529         if(!b){
31530             buttons["ok"].hide();
31531             buttons["cancel"].hide();
31532             buttons["yes"].hide();
31533             buttons["no"].hide();
31534             dlg.footer.dom.style.display = 'none';
31535             return width;
31536         }
31537         dlg.footer.dom.style.display = '';
31538         for(var k in buttons){
31539             if(typeof buttons[k] != "function"){
31540                 if(b[k]){
31541                     buttons[k].show();
31542                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31543                     width += buttons[k].el.getWidth()+15;
31544                 }else{
31545                     buttons[k].hide();
31546                 }
31547             }
31548         }
31549         return width;
31550     };
31551
31552     // private
31553     var handleEsc = function(d, k, e){
31554         if(opt && opt.closable !== false){
31555             dlg.hide();
31556         }
31557         if(e){
31558             e.stopEvent();
31559         }
31560     };
31561
31562     return {
31563         /**
31564          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31565          * @return {Roo.BasicDialog} The BasicDialog element
31566          */
31567         getDialog : function(){
31568            if(!dlg){
31569                 dlg = new Roo.BasicDialog("x-msg-box", {
31570                     autoCreate : true,
31571                     shadow: true,
31572                     draggable: true,
31573                     resizable:false,
31574                     constraintoviewport:false,
31575                     fixedcenter:true,
31576                     collapsible : false,
31577                     shim:true,
31578                     modal: true,
31579                     width:400, height:100,
31580                     buttonAlign:"center",
31581                     closeClick : function(){
31582                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31583                             handleButton("no");
31584                         }else{
31585                             handleButton("cancel");
31586                         }
31587                     }
31588                 });
31589                 dlg.on("hide", handleHide);
31590                 mask = dlg.mask;
31591                 dlg.addKeyListener(27, handleEsc);
31592                 buttons = {};
31593                 var bt = this.buttonText;
31594                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31595                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31596                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31597                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31598                 bodyEl = dlg.body.createChild({
31599
31600                     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>'
31601                 });
31602                 msgEl = bodyEl.dom.firstChild;
31603                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31604                 textboxEl.enableDisplayMode();
31605                 textboxEl.addKeyListener([10,13], function(){
31606                     if(dlg.isVisible() && opt && opt.buttons){
31607                         if(opt.buttons.ok){
31608                             handleButton("ok");
31609                         }else if(opt.buttons.yes){
31610                             handleButton("yes");
31611                         }
31612                     }
31613                 });
31614                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31615                 textareaEl.enableDisplayMode();
31616                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31617                 progressEl.enableDisplayMode();
31618                 var pf = progressEl.dom.firstChild;
31619                 if (pf) {
31620                     pp = Roo.get(pf.firstChild);
31621                     pp.setHeight(pf.offsetHeight);
31622                 }
31623                 
31624             }
31625             return dlg;
31626         },
31627
31628         /**
31629          * Updates the message box body text
31630          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31631          * the XHTML-compliant non-breaking space character '&amp;#160;')
31632          * @return {Roo.MessageBox} This message box
31633          */
31634         updateText : function(text){
31635             if(!dlg.isVisible() && !opt.width){
31636                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31637             }
31638             msgEl.innerHTML = text || '&#160;';
31639       
31640             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31641             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31642             var w = Math.max(
31643                     Math.min(opt.width || cw , this.maxWidth), 
31644                     Math.max(opt.minWidth || this.minWidth, bwidth)
31645             );
31646             if(opt.prompt){
31647                 activeTextEl.setWidth(w);
31648             }
31649             if(dlg.isVisible()){
31650                 dlg.fixedcenter = false;
31651             }
31652             // to big, make it scroll. = But as usual stupid IE does not support
31653             // !important..
31654             
31655             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31656                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31657                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31658             } else {
31659                 bodyEl.dom.style.height = '';
31660                 bodyEl.dom.style.overflowY = '';
31661             }
31662             if (cw > w) {
31663                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31664             } else {
31665                 bodyEl.dom.style.overflowX = '';
31666             }
31667             
31668             dlg.setContentSize(w, bodyEl.getHeight());
31669             if(dlg.isVisible()){
31670                 dlg.fixedcenter = true;
31671             }
31672             return this;
31673         },
31674
31675         /**
31676          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31677          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31678          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31679          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31680          * @return {Roo.MessageBox} This message box
31681          */
31682         updateProgress : function(value, text){
31683             if(text){
31684                 this.updateText(text);
31685             }
31686             if (pp) { // weird bug on my firefox - for some reason this is not defined
31687                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31688             }
31689             return this;
31690         },        
31691
31692         /**
31693          * Returns true if the message box is currently displayed
31694          * @return {Boolean} True if the message box is visible, else false
31695          */
31696         isVisible : function(){
31697             return dlg && dlg.isVisible();  
31698         },
31699
31700         /**
31701          * Hides the message box if it is displayed
31702          */
31703         hide : function(){
31704             if(this.isVisible()){
31705                 dlg.hide();
31706             }  
31707         },
31708
31709         /**
31710          * Displays a new message box, or reinitializes an existing message box, based on the config options
31711          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31712          * The following config object properties are supported:
31713          * <pre>
31714 Property    Type             Description
31715 ----------  ---------------  ------------------------------------------------------------------------------------
31716 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31717                                    closes (defaults to undefined)
31718 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31719                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31720 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31721                                    progress and wait dialogs will ignore this property and always hide the
31722                                    close button as they can only be closed programmatically.
31723 cls               String           A custom CSS class to apply to the message box element
31724 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31725                                    displayed (defaults to 75)
31726 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31727                                    function will be btn (the name of the button that was clicked, if applicable,
31728                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31729                                    Progress and wait dialogs will ignore this option since they do not respond to
31730                                    user actions and can only be closed programmatically, so any required function
31731                                    should be called by the same code after it closes the dialog.
31732 icon              String           A CSS class that provides a background image to be used as an icon for
31733                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31734 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31735 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31736 modal             Boolean          False to allow user interaction with the page while the message box is
31737                                    displayed (defaults to true)
31738 msg               String           A string that will replace the existing message box body text (defaults
31739                                    to the XHTML-compliant non-breaking space character '&#160;')
31740 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31741 progress          Boolean          True to display a progress bar (defaults to false)
31742 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31743 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31744 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31745 title             String           The title text
31746 value             String           The string value to set into the active textbox element if displayed
31747 wait              Boolean          True to display a progress bar (defaults to false)
31748 width             Number           The width of the dialog in pixels
31749 </pre>
31750          *
31751          * Example usage:
31752          * <pre><code>
31753 Roo.Msg.show({
31754    title: 'Address',
31755    msg: 'Please enter your address:',
31756    width: 300,
31757    buttons: Roo.MessageBox.OKCANCEL,
31758    multiline: true,
31759    fn: saveAddress,
31760    animEl: 'addAddressBtn'
31761 });
31762 </code></pre>
31763          * @param {Object} config Configuration options
31764          * @return {Roo.MessageBox} This message box
31765          */
31766         show : function(options)
31767         {
31768             
31769             // this causes nightmares if you show one dialog after another
31770             // especially on callbacks..
31771              
31772             if(this.isVisible()){
31773                 
31774                 this.hide();
31775                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31776                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31777                 Roo.log("New Dialog Message:" +  options.msg )
31778                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31779                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31780                 
31781             }
31782             var d = this.getDialog();
31783             opt = options;
31784             d.setTitle(opt.title || "&#160;");
31785             d.close.setDisplayed(opt.closable !== false);
31786             activeTextEl = textboxEl;
31787             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31788             if(opt.prompt){
31789                 if(opt.multiline){
31790                     textboxEl.hide();
31791                     textareaEl.show();
31792                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31793                         opt.multiline : this.defaultTextHeight);
31794                     activeTextEl = textareaEl;
31795                 }else{
31796                     textboxEl.show();
31797                     textareaEl.hide();
31798                 }
31799             }else{
31800                 textboxEl.hide();
31801                 textareaEl.hide();
31802             }
31803             progressEl.setDisplayed(opt.progress === true);
31804             this.updateProgress(0);
31805             activeTextEl.dom.value = opt.value || "";
31806             if(opt.prompt){
31807                 dlg.setDefaultButton(activeTextEl);
31808             }else{
31809                 var bs = opt.buttons;
31810                 var db = null;
31811                 if(bs && bs.ok){
31812                     db = buttons["ok"];
31813                 }else if(bs && bs.yes){
31814                     db = buttons["yes"];
31815                 }
31816                 dlg.setDefaultButton(db);
31817             }
31818             bwidth = updateButtons(opt.buttons);
31819             this.updateText(opt.msg);
31820             if(opt.cls){
31821                 d.el.addClass(opt.cls);
31822             }
31823             d.proxyDrag = opt.proxyDrag === true;
31824             d.modal = opt.modal !== false;
31825             d.mask = opt.modal !== false ? mask : false;
31826             if(!d.isVisible()){
31827                 // force it to the end of the z-index stack so it gets a cursor in FF
31828                 document.body.appendChild(dlg.el.dom);
31829                 d.animateTarget = null;
31830                 d.show(options.animEl);
31831             }
31832             return this;
31833         },
31834
31835         /**
31836          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31837          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31838          * and closing the message box when the process is complete.
31839          * @param {String} title The title bar text
31840          * @param {String} msg The message box body text
31841          * @return {Roo.MessageBox} This message box
31842          */
31843         progress : function(title, msg){
31844             this.show({
31845                 title : title,
31846                 msg : msg,
31847                 buttons: false,
31848                 progress:true,
31849                 closable:false,
31850                 minWidth: this.minProgressWidth,
31851                 modal : true
31852             });
31853             return this;
31854         },
31855
31856         /**
31857          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31858          * If a callback function is passed it will be called after the user clicks the button, and the
31859          * id of the button that was clicked will be passed as the only parameter to the callback
31860          * (could also be the top-right close button).
31861          * @param {String} title The title bar text
31862          * @param {String} msg The message box body text
31863          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31864          * @param {Object} scope (optional) The scope of the callback function
31865          * @return {Roo.MessageBox} This message box
31866          */
31867         alert : function(title, msg, fn, scope){
31868             this.show({
31869                 title : title,
31870                 msg : msg,
31871                 buttons: this.OK,
31872                 fn: fn,
31873                 scope : scope,
31874                 modal : true
31875             });
31876             return this;
31877         },
31878
31879         /**
31880          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31881          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31882          * You are responsible for closing the message box when the process is complete.
31883          * @param {String} msg The message box body text
31884          * @param {String} title (optional) The title bar text
31885          * @return {Roo.MessageBox} This message box
31886          */
31887         wait : function(msg, title){
31888             this.show({
31889                 title : title,
31890                 msg : msg,
31891                 buttons: false,
31892                 closable:false,
31893                 progress:true,
31894                 modal:true,
31895                 width:300,
31896                 wait:true
31897             });
31898             waitTimer = Roo.TaskMgr.start({
31899                 run: function(i){
31900                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31901                 },
31902                 interval: 1000
31903             });
31904             return this;
31905         },
31906
31907         /**
31908          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31909          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31910          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31911          * @param {String} title The title bar text
31912          * @param {String} msg The message box body text
31913          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31914          * @param {Object} scope (optional) The scope of the callback function
31915          * @return {Roo.MessageBox} This message box
31916          */
31917         confirm : function(title, msg, fn, scope){
31918             this.show({
31919                 title : title,
31920                 msg : msg,
31921                 buttons: this.YESNO,
31922                 fn: fn,
31923                 scope : scope,
31924                 modal : true
31925             });
31926             return this;
31927         },
31928
31929         /**
31930          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31931          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31932          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31933          * (could also be the top-right close button) and the text that was entered will be passed as the two
31934          * parameters to the callback.
31935          * @param {String} title The title bar text
31936          * @param {String} msg The message box body text
31937          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31938          * @param {Object} scope (optional) The scope of the callback function
31939          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31940          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31941          * @return {Roo.MessageBox} This message box
31942          */
31943         prompt : function(title, msg, fn, scope, multiline){
31944             this.show({
31945                 title : title,
31946                 msg : msg,
31947                 buttons: this.OKCANCEL,
31948                 fn: fn,
31949                 minWidth:250,
31950                 scope : scope,
31951                 prompt:true,
31952                 multiline: multiline,
31953                 modal : true
31954             });
31955             return this;
31956         },
31957
31958         /**
31959          * Button config that displays a single OK button
31960          * @type Object
31961          */
31962         OK : {ok:true},
31963         /**
31964          * Button config that displays Yes and No buttons
31965          * @type Object
31966          */
31967         YESNO : {yes:true, no:true},
31968         /**
31969          * Button config that displays OK and Cancel buttons
31970          * @type Object
31971          */
31972         OKCANCEL : {ok:true, cancel:true},
31973         /**
31974          * Button config that displays Yes, No and Cancel buttons
31975          * @type Object
31976          */
31977         YESNOCANCEL : {yes:true, no:true, cancel:true},
31978
31979         /**
31980          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31981          * @type Number
31982          */
31983         defaultTextHeight : 75,
31984         /**
31985          * The maximum width in pixels of the message box (defaults to 600)
31986          * @type Number
31987          */
31988         maxWidth : 600,
31989         /**
31990          * The minimum width in pixels of the message box (defaults to 100)
31991          * @type Number
31992          */
31993         minWidth : 100,
31994         /**
31995          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31996          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31997          * @type Number
31998          */
31999         minProgressWidth : 250,
32000         /**
32001          * An object containing the default button text strings that can be overriden for localized language support.
32002          * Supported properties are: ok, cancel, yes and no.
32003          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
32004          * @type Object
32005          */
32006         buttonText : {
32007             ok : "OK",
32008             cancel : "Cancel",
32009             yes : "Yes",
32010             no : "No"
32011         }
32012     };
32013 }();
32014
32015 /**
32016  * Shorthand for {@link Roo.MessageBox}
32017  */
32018 Roo.Msg = Roo.MessageBox;/*
32019  * Based on:
32020  * Ext JS Library 1.1.1
32021  * Copyright(c) 2006-2007, Ext JS, LLC.
32022  *
32023  * Originally Released Under LGPL - original licence link has changed is not relivant.
32024  *
32025  * Fork - LGPL
32026  * <script type="text/javascript">
32027  */
32028 /**
32029  * @class Roo.QuickTips
32030  * Provides attractive and customizable tooltips for any element.
32031  * @singleton
32032  */
32033 Roo.QuickTips = function(){
32034     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
32035     var ce, bd, xy, dd;
32036     var visible = false, disabled = true, inited = false;
32037     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
32038     
32039     var onOver = function(e){
32040         if(disabled){
32041             return;
32042         }
32043         var t = e.getTarget();
32044         if(!t || t.nodeType !== 1 || t == document || t == document.body){
32045             return;
32046         }
32047         if(ce && t == ce.el){
32048             clearTimeout(hideProc);
32049             return;
32050         }
32051         if(t && tagEls[t.id]){
32052             tagEls[t.id].el = t;
32053             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
32054             return;
32055         }
32056         var ttp, et = Roo.fly(t);
32057         var ns = cfg.namespace;
32058         if(tm.interceptTitles && t.title){
32059             ttp = t.title;
32060             t.qtip = ttp;
32061             t.removeAttribute("title");
32062             e.preventDefault();
32063         }else{
32064             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
32065         }
32066         if(ttp){
32067             showProc = show.defer(tm.showDelay, tm, [{
32068                 el: t, 
32069                 text: ttp, 
32070                 width: et.getAttributeNS(ns, cfg.width),
32071                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
32072                 title: et.getAttributeNS(ns, cfg.title),
32073                     cls: et.getAttributeNS(ns, cfg.cls)
32074             }]);
32075         }
32076     };
32077     
32078     var onOut = function(e){
32079         clearTimeout(showProc);
32080         var t = e.getTarget();
32081         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
32082             hideProc = setTimeout(hide, tm.hideDelay);
32083         }
32084     };
32085     
32086     var onMove = function(e){
32087         if(disabled){
32088             return;
32089         }
32090         xy = e.getXY();
32091         xy[1] += 18;
32092         if(tm.trackMouse && ce){
32093             el.setXY(xy);
32094         }
32095     };
32096     
32097     var onDown = function(e){
32098         clearTimeout(showProc);
32099         clearTimeout(hideProc);
32100         if(!e.within(el)){
32101             if(tm.hideOnClick){
32102                 hide();
32103                 tm.disable();
32104                 tm.enable.defer(100, tm);
32105             }
32106         }
32107     };
32108     
32109     var getPad = function(){
32110         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
32111     };
32112
32113     var show = function(o){
32114         if(disabled){
32115             return;
32116         }
32117         clearTimeout(dismissProc);
32118         ce = o;
32119         if(removeCls){ // in case manually hidden
32120             el.removeClass(removeCls);
32121             removeCls = null;
32122         }
32123         if(ce.cls){
32124             el.addClass(ce.cls);
32125             removeCls = ce.cls;
32126         }
32127         if(ce.title){
32128             tipTitle.update(ce.title);
32129             tipTitle.show();
32130         }else{
32131             tipTitle.update('');
32132             tipTitle.hide();
32133         }
32134         el.dom.style.width  = tm.maxWidth+'px';
32135         //tipBody.dom.style.width = '';
32136         tipBodyText.update(o.text);
32137         var p = getPad(), w = ce.width;
32138         if(!w){
32139             var td = tipBodyText.dom;
32140             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
32141             if(aw > tm.maxWidth){
32142                 w = tm.maxWidth;
32143             }else if(aw < tm.minWidth){
32144                 w = tm.minWidth;
32145             }else{
32146                 w = aw;
32147             }
32148         }
32149         //tipBody.setWidth(w);
32150         el.setWidth(parseInt(w, 10) + p);
32151         if(ce.autoHide === false){
32152             close.setDisplayed(true);
32153             if(dd){
32154                 dd.unlock();
32155             }
32156         }else{
32157             close.setDisplayed(false);
32158             if(dd){
32159                 dd.lock();
32160             }
32161         }
32162         if(xy){
32163             el.avoidY = xy[1]-18;
32164             el.setXY(xy);
32165         }
32166         if(tm.animate){
32167             el.setOpacity(.1);
32168             el.setStyle("visibility", "visible");
32169             el.fadeIn({callback: afterShow});
32170         }else{
32171             afterShow();
32172         }
32173     };
32174     
32175     var afterShow = function(){
32176         if(ce){
32177             el.show();
32178             esc.enable();
32179             if(tm.autoDismiss && ce.autoHide !== false){
32180                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
32181             }
32182         }
32183     };
32184     
32185     var hide = function(noanim){
32186         clearTimeout(dismissProc);
32187         clearTimeout(hideProc);
32188         ce = null;
32189         if(el.isVisible()){
32190             esc.disable();
32191             if(noanim !== true && tm.animate){
32192                 el.fadeOut({callback: afterHide});
32193             }else{
32194                 afterHide();
32195             } 
32196         }
32197     };
32198     
32199     var afterHide = function(){
32200         el.hide();
32201         if(removeCls){
32202             el.removeClass(removeCls);
32203             removeCls = null;
32204         }
32205     };
32206     
32207     return {
32208         /**
32209         * @cfg {Number} minWidth
32210         * The minimum width of the quick tip (defaults to 40)
32211         */
32212        minWidth : 40,
32213         /**
32214         * @cfg {Number} maxWidth
32215         * The maximum width of the quick tip (defaults to 300)
32216         */
32217        maxWidth : 300,
32218         /**
32219         * @cfg {Boolean} interceptTitles
32220         * True to automatically use the element's DOM title value if available (defaults to false)
32221         */
32222        interceptTitles : false,
32223         /**
32224         * @cfg {Boolean} trackMouse
32225         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
32226         */
32227        trackMouse : false,
32228         /**
32229         * @cfg {Boolean} hideOnClick
32230         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
32231         */
32232        hideOnClick : true,
32233         /**
32234         * @cfg {Number} showDelay
32235         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
32236         */
32237        showDelay : 500,
32238         /**
32239         * @cfg {Number} hideDelay
32240         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
32241         */
32242        hideDelay : 200,
32243         /**
32244         * @cfg {Boolean} autoHide
32245         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32246         * Used in conjunction with hideDelay.
32247         */
32248        autoHide : true,
32249         /**
32250         * @cfg {Boolean}
32251         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32252         * (defaults to true).  Used in conjunction with autoDismissDelay.
32253         */
32254        autoDismiss : true,
32255         /**
32256         * @cfg {Number}
32257         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32258         */
32259        autoDismissDelay : 5000,
32260        /**
32261         * @cfg {Boolean} animate
32262         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32263         */
32264        animate : false,
32265
32266        /**
32267         * @cfg {String} title
32268         * Title text to display (defaults to '').  This can be any valid HTML markup.
32269         */
32270         title: '',
32271        /**
32272         * @cfg {String} text
32273         * Body text to display (defaults to '').  This can be any valid HTML markup.
32274         */
32275         text : '',
32276        /**
32277         * @cfg {String} cls
32278         * A CSS class to apply to the base quick tip element (defaults to '').
32279         */
32280         cls : '',
32281        /**
32282         * @cfg {Number} width
32283         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32284         * minWidth or maxWidth.
32285         */
32286         width : null,
32287
32288     /**
32289      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32290      * or display QuickTips in a page.
32291      */
32292        init : function(){
32293           tm = Roo.QuickTips;
32294           cfg = tm.tagConfig;
32295           if(!inited){
32296               if(!Roo.isReady){ // allow calling of init() before onReady
32297                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32298                   return;
32299               }
32300               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32301               el.fxDefaults = {stopFx: true};
32302               // maximum custom styling
32303               //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>');
32304               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>');              
32305               tipTitle = el.child('h3');
32306               tipTitle.enableDisplayMode("block");
32307               tipBody = el.child('div.x-tip-bd');
32308               tipBodyText = el.child('div.x-tip-bd-inner');
32309               //bdLeft = el.child('div.x-tip-bd-left');
32310               //bdRight = el.child('div.x-tip-bd-right');
32311               close = el.child('div.x-tip-close');
32312               close.enableDisplayMode("block");
32313               close.on("click", hide);
32314               var d = Roo.get(document);
32315               d.on("mousedown", onDown);
32316               d.on("mouseover", onOver);
32317               d.on("mouseout", onOut);
32318               d.on("mousemove", onMove);
32319               esc = d.addKeyListener(27, hide);
32320               esc.disable();
32321               if(Roo.dd.DD){
32322                   dd = el.initDD("default", null, {
32323                       onDrag : function(){
32324                           el.sync();  
32325                       }
32326                   });
32327                   dd.setHandleElId(tipTitle.id);
32328                   dd.lock();
32329               }
32330               inited = true;
32331           }
32332           this.enable(); 
32333        },
32334
32335     /**
32336      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32337      * are supported:
32338      * <pre>
32339 Property    Type                   Description
32340 ----------  ---------------------  ------------------------------------------------------------------------
32341 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32342      * </ul>
32343      * @param {Object} config The config object
32344      */
32345        register : function(config){
32346            var cs = config instanceof Array ? config : arguments;
32347            for(var i = 0, len = cs.length; i < len; i++) {
32348                var c = cs[i];
32349                var target = c.target;
32350                if(target){
32351                    if(target instanceof Array){
32352                        for(var j = 0, jlen = target.length; j < jlen; j++){
32353                            tagEls[target[j]] = c;
32354                        }
32355                    }else{
32356                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32357                    }
32358                }
32359            }
32360        },
32361
32362     /**
32363      * Removes this quick tip from its element and destroys it.
32364      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32365      */
32366        unregister : function(el){
32367            delete tagEls[Roo.id(el)];
32368        },
32369
32370     /**
32371      * Enable this quick tip.
32372      */
32373        enable : function(){
32374            if(inited && disabled){
32375                locks.pop();
32376                if(locks.length < 1){
32377                    disabled = false;
32378                }
32379            }
32380        },
32381
32382     /**
32383      * Disable this quick tip.
32384      */
32385        disable : function(){
32386           disabled = true;
32387           clearTimeout(showProc);
32388           clearTimeout(hideProc);
32389           clearTimeout(dismissProc);
32390           if(ce){
32391               hide(true);
32392           }
32393           locks.push(1);
32394        },
32395
32396     /**
32397      * Returns true if the quick tip is enabled, else false.
32398      */
32399        isEnabled : function(){
32400             return !disabled;
32401        },
32402
32403         // private
32404        tagConfig : {
32405            namespace : "roo", // was ext?? this may break..
32406            alt_namespace : "ext",
32407            attribute : "qtip",
32408            width : "width",
32409            target : "target",
32410            title : "qtitle",
32411            hide : "hide",
32412            cls : "qclass"
32413        }
32414    };
32415 }();
32416
32417 // backwards compat
32418 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32419  * Based on:
32420  * Ext JS Library 1.1.1
32421  * Copyright(c) 2006-2007, Ext JS, LLC.
32422  *
32423  * Originally Released Under LGPL - original licence link has changed is not relivant.
32424  *
32425  * Fork - LGPL
32426  * <script type="text/javascript">
32427  */
32428  
32429
32430 /**
32431  * @class Roo.tree.TreePanel
32432  * @extends Roo.data.Tree
32433
32434  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32435  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32436  * @cfg {Boolean} enableDD true to enable drag and drop
32437  * @cfg {Boolean} enableDrag true to enable just drag
32438  * @cfg {Boolean} enableDrop true to enable just drop
32439  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32440  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32441  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32442  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32443  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32444  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32445  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32446  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32447  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32448  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32449  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32450  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32451  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32452  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32453  * @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>
32454  * @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>
32455  * 
32456  * @constructor
32457  * @param {String/HTMLElement/Element} el The container element
32458  * @param {Object} config
32459  */
32460 Roo.tree.TreePanel = function(el, config){
32461     var root = false;
32462     var loader = false;
32463     if (config.root) {
32464         root = config.root;
32465         delete config.root;
32466     }
32467     if (config.loader) {
32468         loader = config.loader;
32469         delete config.loader;
32470     }
32471     
32472     Roo.apply(this, config);
32473     Roo.tree.TreePanel.superclass.constructor.call(this);
32474     this.el = Roo.get(el);
32475     this.el.addClass('x-tree');
32476     //console.log(root);
32477     if (root) {
32478         this.setRootNode( Roo.factory(root, Roo.tree));
32479     }
32480     if (loader) {
32481         this.loader = Roo.factory(loader, Roo.tree);
32482     }
32483    /**
32484     * Read-only. The id of the container element becomes this TreePanel's id.
32485     */
32486     this.id = this.el.id;
32487     this.addEvents({
32488         /**
32489         * @event beforeload
32490         * Fires before a node is loaded, return false to cancel
32491         * @param {Node} node The node being loaded
32492         */
32493         "beforeload" : true,
32494         /**
32495         * @event load
32496         * Fires when a node is loaded
32497         * @param {Node} node The node that was loaded
32498         */
32499         "load" : true,
32500         /**
32501         * @event textchange
32502         * Fires when the text for a node is changed
32503         * @param {Node} node The node
32504         * @param {String} text The new text
32505         * @param {String} oldText The old text
32506         */
32507         "textchange" : true,
32508         /**
32509         * @event beforeexpand
32510         * Fires before a node is expanded, return false to cancel.
32511         * @param {Node} node The node
32512         * @param {Boolean} deep
32513         * @param {Boolean} anim
32514         */
32515         "beforeexpand" : true,
32516         /**
32517         * @event beforecollapse
32518         * Fires before a node is collapsed, return false to cancel.
32519         * @param {Node} node The node
32520         * @param {Boolean} deep
32521         * @param {Boolean} anim
32522         */
32523         "beforecollapse" : true,
32524         /**
32525         * @event expand
32526         * Fires when a node is expanded
32527         * @param {Node} node The node
32528         */
32529         "expand" : true,
32530         /**
32531         * @event disabledchange
32532         * Fires when the disabled status of a node changes
32533         * @param {Node} node The node
32534         * @param {Boolean} disabled
32535         */
32536         "disabledchange" : true,
32537         /**
32538         * @event collapse
32539         * Fires when a node is collapsed
32540         * @param {Node} node The node
32541         */
32542         "collapse" : true,
32543         /**
32544         * @event beforeclick
32545         * Fires before click processing on a node. Return false to cancel the default action.
32546         * @param {Node} node The node
32547         * @param {Roo.EventObject} e The event object
32548         */
32549         "beforeclick":true,
32550         /**
32551         * @event checkchange
32552         * Fires when a node with a checkbox's checked property changes
32553         * @param {Node} this This node
32554         * @param {Boolean} checked
32555         */
32556         "checkchange":true,
32557         /**
32558         * @event click
32559         * Fires when a node is clicked
32560         * @param {Node} node The node
32561         * @param {Roo.EventObject} e The event object
32562         */
32563         "click":true,
32564         /**
32565         * @event dblclick
32566         * Fires when a node is double clicked
32567         * @param {Node} node The node
32568         * @param {Roo.EventObject} e The event object
32569         */
32570         "dblclick":true,
32571         /**
32572         * @event contextmenu
32573         * Fires when a node is right clicked
32574         * @param {Node} node The node
32575         * @param {Roo.EventObject} e The event object
32576         */
32577         "contextmenu":true,
32578         /**
32579         * @event beforechildrenrendered
32580         * Fires right before the child nodes for a node are rendered
32581         * @param {Node} node The node
32582         */
32583         "beforechildrenrendered":true,
32584         /**
32585         * @event startdrag
32586         * Fires when a node starts being dragged
32587         * @param {Roo.tree.TreePanel} this
32588         * @param {Roo.tree.TreeNode} node
32589         * @param {event} e The raw browser event
32590         */ 
32591        "startdrag" : true,
32592        /**
32593         * @event enddrag
32594         * Fires when a drag operation is complete
32595         * @param {Roo.tree.TreePanel} this
32596         * @param {Roo.tree.TreeNode} node
32597         * @param {event} e The raw browser event
32598         */
32599        "enddrag" : true,
32600        /**
32601         * @event dragdrop
32602         * Fires when a dragged node is dropped on a valid DD target
32603         * @param {Roo.tree.TreePanel} this
32604         * @param {Roo.tree.TreeNode} node
32605         * @param {DD} dd The dd it was dropped on
32606         * @param {event} e The raw browser event
32607         */
32608        "dragdrop" : true,
32609        /**
32610         * @event beforenodedrop
32611         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32612         * passed to handlers has the following properties:<br />
32613         * <ul style="padding:5px;padding-left:16px;">
32614         * <li>tree - The TreePanel</li>
32615         * <li>target - The node being targeted for the drop</li>
32616         * <li>data - The drag data from the drag source</li>
32617         * <li>point - The point of the drop - append, above or below</li>
32618         * <li>source - The drag source</li>
32619         * <li>rawEvent - Raw mouse event</li>
32620         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32621         * to be inserted by setting them on this object.</li>
32622         * <li>cancel - Set this to true to cancel the drop.</li>
32623         * </ul>
32624         * @param {Object} dropEvent
32625         */
32626        "beforenodedrop" : true,
32627        /**
32628         * @event nodedrop
32629         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32630         * passed to handlers has the following properties:<br />
32631         * <ul style="padding:5px;padding-left:16px;">
32632         * <li>tree - The TreePanel</li>
32633         * <li>target - The node being targeted for the drop</li>
32634         * <li>data - The drag data from the drag source</li>
32635         * <li>point - The point of the drop - append, above or below</li>
32636         * <li>source - The drag source</li>
32637         * <li>rawEvent - Raw mouse event</li>
32638         * <li>dropNode - Dropped node(s).</li>
32639         * </ul>
32640         * @param {Object} dropEvent
32641         */
32642        "nodedrop" : true,
32643         /**
32644         * @event nodedragover
32645         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32646         * passed to handlers has the following properties:<br />
32647         * <ul style="padding:5px;padding-left:16px;">
32648         * <li>tree - The TreePanel</li>
32649         * <li>target - The node being targeted for the drop</li>
32650         * <li>data - The drag data from the drag source</li>
32651         * <li>point - The point of the drop - append, above or below</li>
32652         * <li>source - The drag source</li>
32653         * <li>rawEvent - Raw mouse event</li>
32654         * <li>dropNode - Drop node(s) provided by the source.</li>
32655         * <li>cancel - Set this to true to signal drop not allowed.</li>
32656         * </ul>
32657         * @param {Object} dragOverEvent
32658         */
32659        "nodedragover" : true
32660         
32661     });
32662     if(this.singleExpand){
32663        this.on("beforeexpand", this.restrictExpand, this);
32664     }
32665     if (this.editor) {
32666         this.editor.tree = this;
32667         this.editor = Roo.factory(this.editor, Roo.tree);
32668     }
32669     
32670     if (this.selModel) {
32671         this.selModel = Roo.factory(this.selModel, Roo.tree);
32672     }
32673    
32674 };
32675 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32676     rootVisible : true,
32677     animate: Roo.enableFx,
32678     lines : true,
32679     enableDD : false,
32680     hlDrop : Roo.enableFx,
32681   
32682     renderer: false,
32683     
32684     rendererTip: false,
32685     // private
32686     restrictExpand : function(node){
32687         var p = node.parentNode;
32688         if(p){
32689             if(p.expandedChild && p.expandedChild.parentNode == p){
32690                 p.expandedChild.collapse();
32691             }
32692             p.expandedChild = node;
32693         }
32694     },
32695
32696     // private override
32697     setRootNode : function(node){
32698         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32699         if(!this.rootVisible){
32700             node.ui = new Roo.tree.RootTreeNodeUI(node);
32701         }
32702         return node;
32703     },
32704
32705     /**
32706      * Returns the container element for this TreePanel
32707      */
32708     getEl : function(){
32709         return this.el;
32710     },
32711
32712     /**
32713      * Returns the default TreeLoader for this TreePanel
32714      */
32715     getLoader : function(){
32716         return this.loader;
32717     },
32718
32719     /**
32720      * Expand all nodes
32721      */
32722     expandAll : function(){
32723         this.root.expand(true);
32724     },
32725
32726     /**
32727      * Collapse all nodes
32728      */
32729     collapseAll : function(){
32730         this.root.collapse(true);
32731     },
32732
32733     /**
32734      * Returns the selection model used by this TreePanel
32735      */
32736     getSelectionModel : function(){
32737         if(!this.selModel){
32738             this.selModel = new Roo.tree.DefaultSelectionModel();
32739         }
32740         return this.selModel;
32741     },
32742
32743     /**
32744      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32745      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32746      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32747      * @return {Array}
32748      */
32749     getChecked : function(a, startNode){
32750         startNode = startNode || this.root;
32751         var r = [];
32752         var f = function(){
32753             if(this.attributes.checked){
32754                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32755             }
32756         }
32757         startNode.cascade(f);
32758         return r;
32759     },
32760
32761     /**
32762      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32763      * @param {String} path
32764      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32765      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32766      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32767      */
32768     expandPath : function(path, attr, callback){
32769         attr = attr || "id";
32770         var keys = path.split(this.pathSeparator);
32771         var curNode = this.root;
32772         if(curNode.attributes[attr] != keys[1]){ // invalid root
32773             if(callback){
32774                 callback(false, null);
32775             }
32776             return;
32777         }
32778         var index = 1;
32779         var f = function(){
32780             if(++index == keys.length){
32781                 if(callback){
32782                     callback(true, curNode);
32783                 }
32784                 return;
32785             }
32786             var c = curNode.findChild(attr, keys[index]);
32787             if(!c){
32788                 if(callback){
32789                     callback(false, curNode);
32790                 }
32791                 return;
32792             }
32793             curNode = c;
32794             c.expand(false, false, f);
32795         };
32796         curNode.expand(false, false, f);
32797     },
32798
32799     /**
32800      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32801      * @param {String} path
32802      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32803      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32804      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32805      */
32806     selectPath : function(path, attr, callback){
32807         attr = attr || "id";
32808         var keys = path.split(this.pathSeparator);
32809         var v = keys.pop();
32810         if(keys.length > 0){
32811             var f = function(success, node){
32812                 if(success && node){
32813                     var n = node.findChild(attr, v);
32814                     if(n){
32815                         n.select();
32816                         if(callback){
32817                             callback(true, n);
32818                         }
32819                     }else if(callback){
32820                         callback(false, n);
32821                     }
32822                 }else{
32823                     if(callback){
32824                         callback(false, n);
32825                     }
32826                 }
32827             };
32828             this.expandPath(keys.join(this.pathSeparator), attr, f);
32829         }else{
32830             this.root.select();
32831             if(callback){
32832                 callback(true, this.root);
32833             }
32834         }
32835     },
32836
32837     getTreeEl : function(){
32838         return this.el;
32839     },
32840
32841     /**
32842      * Trigger rendering of this TreePanel
32843      */
32844     render : function(){
32845         if (this.innerCt) {
32846             return this; // stop it rendering more than once!!
32847         }
32848         
32849         this.innerCt = this.el.createChild({tag:"ul",
32850                cls:"x-tree-root-ct " +
32851                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32852
32853         if(this.containerScroll){
32854             Roo.dd.ScrollManager.register(this.el);
32855         }
32856         if((this.enableDD || this.enableDrop) && !this.dropZone){
32857            /**
32858             * The dropZone used by this tree if drop is enabled
32859             * @type Roo.tree.TreeDropZone
32860             */
32861              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32862                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32863            });
32864         }
32865         if((this.enableDD || this.enableDrag) && !this.dragZone){
32866            /**
32867             * The dragZone used by this tree if drag is enabled
32868             * @type Roo.tree.TreeDragZone
32869             */
32870             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32871                ddGroup: this.ddGroup || "TreeDD",
32872                scroll: this.ddScroll
32873            });
32874         }
32875         this.getSelectionModel().init(this);
32876         if (!this.root) {
32877             Roo.log("ROOT not set in tree");
32878             return this;
32879         }
32880         this.root.render();
32881         if(!this.rootVisible){
32882             this.root.renderChildren();
32883         }
32884         return this;
32885     }
32886 });/*
32887  * Based on:
32888  * Ext JS Library 1.1.1
32889  * Copyright(c) 2006-2007, Ext JS, LLC.
32890  *
32891  * Originally Released Under LGPL - original licence link has changed is not relivant.
32892  *
32893  * Fork - LGPL
32894  * <script type="text/javascript">
32895  */
32896  
32897
32898 /**
32899  * @class Roo.tree.DefaultSelectionModel
32900  * @extends Roo.util.Observable
32901  * The default single selection for a TreePanel.
32902  * @param {Object} cfg Configuration
32903  */
32904 Roo.tree.DefaultSelectionModel = function(cfg){
32905    this.selNode = null;
32906    
32907    
32908    
32909    this.addEvents({
32910        /**
32911         * @event selectionchange
32912         * Fires when the selected node changes
32913         * @param {DefaultSelectionModel} this
32914         * @param {TreeNode} node the new selection
32915         */
32916        "selectionchange" : true,
32917
32918        /**
32919         * @event beforeselect
32920         * Fires before the selected node changes, return false to cancel the change
32921         * @param {DefaultSelectionModel} this
32922         * @param {TreeNode} node the new selection
32923         * @param {TreeNode} node the old selection
32924         */
32925        "beforeselect" : true
32926    });
32927    
32928     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32929 };
32930
32931 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32932     init : function(tree){
32933         this.tree = tree;
32934         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32935         tree.on("click", this.onNodeClick, this);
32936     },
32937     
32938     onNodeClick : function(node, e){
32939         if (e.ctrlKey && this.selNode == node)  {
32940             this.unselect(node);
32941             return;
32942         }
32943         this.select(node);
32944     },
32945     
32946     /**
32947      * Select a node.
32948      * @param {TreeNode} node The node to select
32949      * @return {TreeNode} The selected node
32950      */
32951     select : function(node){
32952         var last = this.selNode;
32953         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32954             if(last){
32955                 last.ui.onSelectedChange(false);
32956             }
32957             this.selNode = node;
32958             node.ui.onSelectedChange(true);
32959             this.fireEvent("selectionchange", this, node, last);
32960         }
32961         return node;
32962     },
32963     
32964     /**
32965      * Deselect a node.
32966      * @param {TreeNode} node The node to unselect
32967      */
32968     unselect : function(node){
32969         if(this.selNode == node){
32970             this.clearSelections();
32971         }    
32972     },
32973     
32974     /**
32975      * Clear all selections
32976      */
32977     clearSelections : function(){
32978         var n = this.selNode;
32979         if(n){
32980             n.ui.onSelectedChange(false);
32981             this.selNode = null;
32982             this.fireEvent("selectionchange", this, null);
32983         }
32984         return n;
32985     },
32986     
32987     /**
32988      * Get the selected node
32989      * @return {TreeNode} The selected node
32990      */
32991     getSelectedNode : function(){
32992         return this.selNode;    
32993     },
32994     
32995     /**
32996      * Returns true if the node is selected
32997      * @param {TreeNode} node The node to check
32998      * @return {Boolean}
32999      */
33000     isSelected : function(node){
33001         return this.selNode == node;  
33002     },
33003
33004     /**
33005      * Selects the node above the selected node in the tree, intelligently walking the nodes
33006      * @return TreeNode The new selection
33007      */
33008     selectPrevious : function(){
33009         var s = this.selNode || this.lastSelNode;
33010         if(!s){
33011             return null;
33012         }
33013         var ps = s.previousSibling;
33014         if(ps){
33015             if(!ps.isExpanded() || ps.childNodes.length < 1){
33016                 return this.select(ps);
33017             } else{
33018                 var lc = ps.lastChild;
33019                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
33020                     lc = lc.lastChild;
33021                 }
33022                 return this.select(lc);
33023             }
33024         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
33025             return this.select(s.parentNode);
33026         }
33027         return null;
33028     },
33029
33030     /**
33031      * Selects the node above the selected node in the tree, intelligently walking the nodes
33032      * @return TreeNode The new selection
33033      */
33034     selectNext : function(){
33035         var s = this.selNode || this.lastSelNode;
33036         if(!s){
33037             return null;
33038         }
33039         if(s.firstChild && s.isExpanded()){
33040              return this.select(s.firstChild);
33041          }else if(s.nextSibling){
33042              return this.select(s.nextSibling);
33043          }else if(s.parentNode){
33044             var newS = null;
33045             s.parentNode.bubble(function(){
33046                 if(this.nextSibling){
33047                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
33048                     return false;
33049                 }
33050             });
33051             return newS;
33052          }
33053         return null;
33054     },
33055
33056     onKeyDown : function(e){
33057         var s = this.selNode || this.lastSelNode;
33058         // undesirable, but required
33059         var sm = this;
33060         if(!s){
33061             return;
33062         }
33063         var k = e.getKey();
33064         switch(k){
33065              case e.DOWN:
33066                  e.stopEvent();
33067                  this.selectNext();
33068              break;
33069              case e.UP:
33070                  e.stopEvent();
33071                  this.selectPrevious();
33072              break;
33073              case e.RIGHT:
33074                  e.preventDefault();
33075                  if(s.hasChildNodes()){
33076                      if(!s.isExpanded()){
33077                          s.expand();
33078                      }else if(s.firstChild){
33079                          this.select(s.firstChild, e);
33080                      }
33081                  }
33082              break;
33083              case e.LEFT:
33084                  e.preventDefault();
33085                  if(s.hasChildNodes() && s.isExpanded()){
33086                      s.collapse();
33087                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
33088                      this.select(s.parentNode, e);
33089                  }
33090              break;
33091         };
33092     }
33093 });
33094
33095 /**
33096  * @class Roo.tree.MultiSelectionModel
33097  * @extends Roo.util.Observable
33098  * Multi selection for a TreePanel.
33099  * @param {Object} cfg Configuration
33100  */
33101 Roo.tree.MultiSelectionModel = function(){
33102    this.selNodes = [];
33103    this.selMap = {};
33104    this.addEvents({
33105        /**
33106         * @event selectionchange
33107         * Fires when the selected nodes change
33108         * @param {MultiSelectionModel} this
33109         * @param {Array} nodes Array of the selected nodes
33110         */
33111        "selectionchange" : true
33112    });
33113    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
33114    
33115 };
33116
33117 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
33118     init : function(tree){
33119         this.tree = tree;
33120         tree.getTreeEl().on("keydown", this.onKeyDown, this);
33121         tree.on("click", this.onNodeClick, this);
33122     },
33123     
33124     onNodeClick : function(node, e){
33125         this.select(node, e, e.ctrlKey);
33126     },
33127     
33128     /**
33129      * Select a node.
33130      * @param {TreeNode} node The node to select
33131      * @param {EventObject} e (optional) An event associated with the selection
33132      * @param {Boolean} keepExisting True to retain existing selections
33133      * @return {TreeNode} The selected node
33134      */
33135     select : function(node, e, keepExisting){
33136         if(keepExisting !== true){
33137             this.clearSelections(true);
33138         }
33139         if(this.isSelected(node)){
33140             this.lastSelNode = node;
33141             return node;
33142         }
33143         this.selNodes.push(node);
33144         this.selMap[node.id] = node;
33145         this.lastSelNode = node;
33146         node.ui.onSelectedChange(true);
33147         this.fireEvent("selectionchange", this, this.selNodes);
33148         return node;
33149     },
33150     
33151     /**
33152      * Deselect a node.
33153      * @param {TreeNode} node The node to unselect
33154      */
33155     unselect : function(node){
33156         if(this.selMap[node.id]){
33157             node.ui.onSelectedChange(false);
33158             var sn = this.selNodes;
33159             var index = -1;
33160             if(sn.indexOf){
33161                 index = sn.indexOf(node);
33162             }else{
33163                 for(var i = 0, len = sn.length; i < len; i++){
33164                     if(sn[i] == node){
33165                         index = i;
33166                         break;
33167                     }
33168                 }
33169             }
33170             if(index != -1){
33171                 this.selNodes.splice(index, 1);
33172             }
33173             delete this.selMap[node.id];
33174             this.fireEvent("selectionchange", this, this.selNodes);
33175         }
33176     },
33177     
33178     /**
33179      * Clear all selections
33180      */
33181     clearSelections : function(suppressEvent){
33182         var sn = this.selNodes;
33183         if(sn.length > 0){
33184             for(var i = 0, len = sn.length; i < len; i++){
33185                 sn[i].ui.onSelectedChange(false);
33186             }
33187             this.selNodes = [];
33188             this.selMap = {};
33189             if(suppressEvent !== true){
33190                 this.fireEvent("selectionchange", this, this.selNodes);
33191             }
33192         }
33193     },
33194     
33195     /**
33196      * Returns true if the node is selected
33197      * @param {TreeNode} node The node to check
33198      * @return {Boolean}
33199      */
33200     isSelected : function(node){
33201         return this.selMap[node.id] ? true : false;  
33202     },
33203     
33204     /**
33205      * Returns an array of the selected nodes
33206      * @return {Array}
33207      */
33208     getSelectedNodes : function(){
33209         return this.selNodes;    
33210     },
33211
33212     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
33213
33214     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
33215
33216     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
33217 });/*
33218  * Based on:
33219  * Ext JS Library 1.1.1
33220  * Copyright(c) 2006-2007, Ext JS, LLC.
33221  *
33222  * Originally Released Under LGPL - original licence link has changed is not relivant.
33223  *
33224  * Fork - LGPL
33225  * <script type="text/javascript">
33226  */
33227  
33228 /**
33229  * @class Roo.tree.TreeNode
33230  * @extends Roo.data.Node
33231  * @cfg {String} text The text for this node
33232  * @cfg {Boolean} expanded true to start the node expanded
33233  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
33234  * @cfg {Boolean} allowDrop false if this node cannot be drop on
33235  * @cfg {Boolean} disabled true to start the node disabled
33236  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
33237  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
33238  * @cfg {String} cls A css class to be added to the node
33239  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
33240  * @cfg {String} href URL of the link used for the node (defaults to #)
33241  * @cfg {String} hrefTarget target frame for the link
33242  * @cfg {String} qtip An Ext QuickTip for the node
33243  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33244  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33245  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33246  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33247  * (defaults to undefined with no checkbox rendered)
33248  * @constructor
33249  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33250  */
33251 Roo.tree.TreeNode = function(attributes){
33252     attributes = attributes || {};
33253     if(typeof attributes == "string"){
33254         attributes = {text: attributes};
33255     }
33256     this.childrenRendered = false;
33257     this.rendered = false;
33258     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33259     this.expanded = attributes.expanded === true;
33260     this.isTarget = attributes.isTarget !== false;
33261     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33262     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33263
33264     /**
33265      * Read-only. The text for this node. To change it use setText().
33266      * @type String
33267      */
33268     this.text = attributes.text;
33269     /**
33270      * True if this node is disabled.
33271      * @type Boolean
33272      */
33273     this.disabled = attributes.disabled === true;
33274
33275     this.addEvents({
33276         /**
33277         * @event textchange
33278         * Fires when the text for this node is changed
33279         * @param {Node} this This node
33280         * @param {String} text The new text
33281         * @param {String} oldText The old text
33282         */
33283         "textchange" : true,
33284         /**
33285         * @event beforeexpand
33286         * Fires before this node is expanded, return false to cancel.
33287         * @param {Node} this This node
33288         * @param {Boolean} deep
33289         * @param {Boolean} anim
33290         */
33291         "beforeexpand" : true,
33292         /**
33293         * @event beforecollapse
33294         * Fires before this node is collapsed, return false to cancel.
33295         * @param {Node} this This node
33296         * @param {Boolean} deep
33297         * @param {Boolean} anim
33298         */
33299         "beforecollapse" : true,
33300         /**
33301         * @event expand
33302         * Fires when this node is expanded
33303         * @param {Node} this This node
33304         */
33305         "expand" : true,
33306         /**
33307         * @event disabledchange
33308         * Fires when the disabled status of this node changes
33309         * @param {Node} this This node
33310         * @param {Boolean} disabled
33311         */
33312         "disabledchange" : true,
33313         /**
33314         * @event collapse
33315         * Fires when this node is collapsed
33316         * @param {Node} this This node
33317         */
33318         "collapse" : true,
33319         /**
33320         * @event beforeclick
33321         * Fires before click processing. Return false to cancel the default action.
33322         * @param {Node} this This node
33323         * @param {Roo.EventObject} e The event object
33324         */
33325         "beforeclick":true,
33326         /**
33327         * @event checkchange
33328         * Fires when a node with a checkbox's checked property changes
33329         * @param {Node} this This node
33330         * @param {Boolean} checked
33331         */
33332         "checkchange":true,
33333         /**
33334         * @event click
33335         * Fires when this node is clicked
33336         * @param {Node} this This node
33337         * @param {Roo.EventObject} e The event object
33338         */
33339         "click":true,
33340         /**
33341         * @event dblclick
33342         * Fires when this node is double clicked
33343         * @param {Node} this This node
33344         * @param {Roo.EventObject} e The event object
33345         */
33346         "dblclick":true,
33347         /**
33348         * @event contextmenu
33349         * Fires when this node is right clicked
33350         * @param {Node} this This node
33351         * @param {Roo.EventObject} e The event object
33352         */
33353         "contextmenu":true,
33354         /**
33355         * @event beforechildrenrendered
33356         * Fires right before the child nodes for this node are rendered
33357         * @param {Node} this This node
33358         */
33359         "beforechildrenrendered":true
33360     });
33361
33362     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33363
33364     /**
33365      * Read-only. The UI for this node
33366      * @type TreeNodeUI
33367      */
33368     this.ui = new uiClass(this);
33369     
33370     // finally support items[]
33371     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33372         return;
33373     }
33374     
33375     
33376     Roo.each(this.attributes.items, function(c) {
33377         this.appendChild(Roo.factory(c,Roo.Tree));
33378     }, this);
33379     delete this.attributes.items;
33380     
33381     
33382     
33383 };
33384 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33385     preventHScroll: true,
33386     /**
33387      * Returns true if this node is expanded
33388      * @return {Boolean}
33389      */
33390     isExpanded : function(){
33391         return this.expanded;
33392     },
33393
33394     /**
33395      * Returns the UI object for this node
33396      * @return {TreeNodeUI}
33397      */
33398     getUI : function(){
33399         return this.ui;
33400     },
33401
33402     // private override
33403     setFirstChild : function(node){
33404         var of = this.firstChild;
33405         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33406         if(this.childrenRendered && of && node != of){
33407             of.renderIndent(true, true);
33408         }
33409         if(this.rendered){
33410             this.renderIndent(true, true);
33411         }
33412     },
33413
33414     // private override
33415     setLastChild : function(node){
33416         var ol = this.lastChild;
33417         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33418         if(this.childrenRendered && ol && node != ol){
33419             ol.renderIndent(true, true);
33420         }
33421         if(this.rendered){
33422             this.renderIndent(true, true);
33423         }
33424     },
33425
33426     // these methods are overridden to provide lazy rendering support
33427     // private override
33428     appendChild : function()
33429     {
33430         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33431         if(node && this.childrenRendered){
33432             node.render();
33433         }
33434         this.ui.updateExpandIcon();
33435         return node;
33436     },
33437
33438     // private override
33439     removeChild : function(node){
33440         this.ownerTree.getSelectionModel().unselect(node);
33441         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33442         // if it's been rendered remove dom node
33443         if(this.childrenRendered){
33444             node.ui.remove();
33445         }
33446         if(this.childNodes.length < 1){
33447             this.collapse(false, false);
33448         }else{
33449             this.ui.updateExpandIcon();
33450         }
33451         if(!this.firstChild) {
33452             this.childrenRendered = false;
33453         }
33454         return node;
33455     },
33456
33457     // private override
33458     insertBefore : function(node, refNode){
33459         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33460         if(newNode && refNode && this.childrenRendered){
33461             node.render();
33462         }
33463         this.ui.updateExpandIcon();
33464         return newNode;
33465     },
33466
33467     /**
33468      * Sets the text for this node
33469      * @param {String} text
33470      */
33471     setText : function(text){
33472         var oldText = this.text;
33473         this.text = text;
33474         this.attributes.text = text;
33475         if(this.rendered){ // event without subscribing
33476             this.ui.onTextChange(this, text, oldText);
33477         }
33478         this.fireEvent("textchange", this, text, oldText);
33479     },
33480
33481     /**
33482      * Triggers selection of this node
33483      */
33484     select : function(){
33485         this.getOwnerTree().getSelectionModel().select(this);
33486     },
33487
33488     /**
33489      * Triggers deselection of this node
33490      */
33491     unselect : function(){
33492         this.getOwnerTree().getSelectionModel().unselect(this);
33493     },
33494
33495     /**
33496      * Returns true if this node is selected
33497      * @return {Boolean}
33498      */
33499     isSelected : function(){
33500         return this.getOwnerTree().getSelectionModel().isSelected(this);
33501     },
33502
33503     /**
33504      * Expand this node.
33505      * @param {Boolean} deep (optional) True to expand all children as well
33506      * @param {Boolean} anim (optional) false to cancel the default animation
33507      * @param {Function} callback (optional) A callback to be called when
33508      * expanding this node completes (does not wait for deep expand to complete).
33509      * Called with 1 parameter, this node.
33510      */
33511     expand : function(deep, anim, callback){
33512         if(!this.expanded){
33513             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33514                 return;
33515             }
33516             if(!this.childrenRendered){
33517                 this.renderChildren();
33518             }
33519             this.expanded = true;
33520             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33521                 this.ui.animExpand(function(){
33522                     this.fireEvent("expand", this);
33523                     if(typeof callback == "function"){
33524                         callback(this);
33525                     }
33526                     if(deep === true){
33527                         this.expandChildNodes(true);
33528                     }
33529                 }.createDelegate(this));
33530                 return;
33531             }else{
33532                 this.ui.expand();
33533                 this.fireEvent("expand", this);
33534                 if(typeof callback == "function"){
33535                     callback(this);
33536                 }
33537             }
33538         }else{
33539            if(typeof callback == "function"){
33540                callback(this);
33541            }
33542         }
33543         if(deep === true){
33544             this.expandChildNodes(true);
33545         }
33546     },
33547
33548     isHiddenRoot : function(){
33549         return this.isRoot && !this.getOwnerTree().rootVisible;
33550     },
33551
33552     /**
33553      * Collapse this node.
33554      * @param {Boolean} deep (optional) True to collapse all children as well
33555      * @param {Boolean} anim (optional) false to cancel the default animation
33556      */
33557     collapse : function(deep, anim){
33558         if(this.expanded && !this.isHiddenRoot()){
33559             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33560                 return;
33561             }
33562             this.expanded = false;
33563             if((this.getOwnerTree().animate && anim !== false) || anim){
33564                 this.ui.animCollapse(function(){
33565                     this.fireEvent("collapse", this);
33566                     if(deep === true){
33567                         this.collapseChildNodes(true);
33568                     }
33569                 }.createDelegate(this));
33570                 return;
33571             }else{
33572                 this.ui.collapse();
33573                 this.fireEvent("collapse", this);
33574             }
33575         }
33576         if(deep === true){
33577             var cs = this.childNodes;
33578             for(var i = 0, len = cs.length; i < len; i++) {
33579                 cs[i].collapse(true, false);
33580             }
33581         }
33582     },
33583
33584     // private
33585     delayedExpand : function(delay){
33586         if(!this.expandProcId){
33587             this.expandProcId = this.expand.defer(delay, this);
33588         }
33589     },
33590
33591     // private
33592     cancelExpand : function(){
33593         if(this.expandProcId){
33594             clearTimeout(this.expandProcId);
33595         }
33596         this.expandProcId = false;
33597     },
33598
33599     /**
33600      * Toggles expanded/collapsed state of the node
33601      */
33602     toggle : function(){
33603         if(this.expanded){
33604             this.collapse();
33605         }else{
33606             this.expand();
33607         }
33608     },
33609
33610     /**
33611      * Ensures all parent nodes are expanded
33612      */
33613     ensureVisible : function(callback){
33614         var tree = this.getOwnerTree();
33615         tree.expandPath(this.parentNode.getPath(), false, function(){
33616             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33617             Roo.callback(callback);
33618         }.createDelegate(this));
33619     },
33620
33621     /**
33622      * Expand all child nodes
33623      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33624      */
33625     expandChildNodes : function(deep){
33626         var cs = this.childNodes;
33627         for(var i = 0, len = cs.length; i < len; i++) {
33628                 cs[i].expand(deep);
33629         }
33630     },
33631
33632     /**
33633      * Collapse all child nodes
33634      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33635      */
33636     collapseChildNodes : function(deep){
33637         var cs = this.childNodes;
33638         for(var i = 0, len = cs.length; i < len; i++) {
33639                 cs[i].collapse(deep);
33640         }
33641     },
33642
33643     /**
33644      * Disables this node
33645      */
33646     disable : function(){
33647         this.disabled = true;
33648         this.unselect();
33649         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33650             this.ui.onDisableChange(this, true);
33651         }
33652         this.fireEvent("disabledchange", this, true);
33653     },
33654
33655     /**
33656      * Enables this node
33657      */
33658     enable : function(){
33659         this.disabled = false;
33660         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33661             this.ui.onDisableChange(this, false);
33662         }
33663         this.fireEvent("disabledchange", this, false);
33664     },
33665
33666     // private
33667     renderChildren : function(suppressEvent){
33668         if(suppressEvent !== false){
33669             this.fireEvent("beforechildrenrendered", this);
33670         }
33671         var cs = this.childNodes;
33672         for(var i = 0, len = cs.length; i < len; i++){
33673             cs[i].render(true);
33674         }
33675         this.childrenRendered = true;
33676     },
33677
33678     // private
33679     sort : function(fn, scope){
33680         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33681         if(this.childrenRendered){
33682             var cs = this.childNodes;
33683             for(var i = 0, len = cs.length; i < len; i++){
33684                 cs[i].render(true);
33685             }
33686         }
33687     },
33688
33689     // private
33690     render : function(bulkRender){
33691         this.ui.render(bulkRender);
33692         if(!this.rendered){
33693             this.rendered = true;
33694             if(this.expanded){
33695                 this.expanded = false;
33696                 this.expand(false, false);
33697             }
33698         }
33699     },
33700
33701     // private
33702     renderIndent : function(deep, refresh){
33703         if(refresh){
33704             this.ui.childIndent = null;
33705         }
33706         this.ui.renderIndent();
33707         if(deep === true && this.childrenRendered){
33708             var cs = this.childNodes;
33709             for(var i = 0, len = cs.length; i < len; i++){
33710                 cs[i].renderIndent(true, refresh);
33711             }
33712         }
33713     }
33714 });/*
33715  * Based on:
33716  * Ext JS Library 1.1.1
33717  * Copyright(c) 2006-2007, Ext JS, LLC.
33718  *
33719  * Originally Released Under LGPL - original licence link has changed is not relivant.
33720  *
33721  * Fork - LGPL
33722  * <script type="text/javascript">
33723  */
33724  
33725 /**
33726  * @class Roo.tree.AsyncTreeNode
33727  * @extends Roo.tree.TreeNode
33728  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33729  * @constructor
33730  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33731  */
33732  Roo.tree.AsyncTreeNode = function(config){
33733     this.loaded = false;
33734     this.loading = false;
33735     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33736     /**
33737     * @event beforeload
33738     * Fires before this node is loaded, return false to cancel
33739     * @param {Node} this This node
33740     */
33741     this.addEvents({'beforeload':true, 'load': true});
33742     /**
33743     * @event load
33744     * Fires when this node is loaded
33745     * @param {Node} this This node
33746     */
33747     /**
33748      * The loader used by this node (defaults to using the tree's defined loader)
33749      * @type TreeLoader
33750      * @property loader
33751      */
33752 };
33753 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33754     expand : function(deep, anim, callback){
33755         if(this.loading){ // if an async load is already running, waiting til it's done
33756             var timer;
33757             var f = function(){
33758                 if(!this.loading){ // done loading
33759                     clearInterval(timer);
33760                     this.expand(deep, anim, callback);
33761                 }
33762             }.createDelegate(this);
33763             timer = setInterval(f, 200);
33764             return;
33765         }
33766         if(!this.loaded){
33767             if(this.fireEvent("beforeload", this) === false){
33768                 return;
33769             }
33770             this.loading = true;
33771             this.ui.beforeLoad(this);
33772             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33773             if(loader){
33774                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33775                 return;
33776             }
33777         }
33778         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33779     },
33780     
33781     /**
33782      * Returns true if this node is currently loading
33783      * @return {Boolean}
33784      */
33785     isLoading : function(){
33786         return this.loading;  
33787     },
33788     
33789     loadComplete : function(deep, anim, callback){
33790         this.loading = false;
33791         this.loaded = true;
33792         this.ui.afterLoad(this);
33793         this.fireEvent("load", this);
33794         this.expand(deep, anim, callback);
33795     },
33796     
33797     /**
33798      * Returns true if this node has been loaded
33799      * @return {Boolean}
33800      */
33801     isLoaded : function(){
33802         return this.loaded;
33803     },
33804     
33805     hasChildNodes : function(){
33806         if(!this.isLeaf() && !this.loaded){
33807             return true;
33808         }else{
33809             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33810         }
33811     },
33812
33813     /**
33814      * Trigger a reload for this node
33815      * @param {Function} callback
33816      */
33817     reload : function(callback){
33818         this.collapse(false, false);
33819         while(this.firstChild){
33820             this.removeChild(this.firstChild);
33821         }
33822         this.childrenRendered = false;
33823         this.loaded = false;
33824         if(this.isHiddenRoot()){
33825             this.expanded = false;
33826         }
33827         this.expand(false, false, callback);
33828     }
33829 });/*
33830  * Based on:
33831  * Ext JS Library 1.1.1
33832  * Copyright(c) 2006-2007, Ext JS, LLC.
33833  *
33834  * Originally Released Under LGPL - original licence link has changed is not relivant.
33835  *
33836  * Fork - LGPL
33837  * <script type="text/javascript">
33838  */
33839  
33840 /**
33841  * @class Roo.tree.TreeNodeUI
33842  * @constructor
33843  * @param {Object} node The node to render
33844  * The TreeNode UI implementation is separate from the
33845  * tree implementation. Unless you are customizing the tree UI,
33846  * you should never have to use this directly.
33847  */
33848 Roo.tree.TreeNodeUI = function(node){
33849     this.node = node;
33850     this.rendered = false;
33851     this.animating = false;
33852     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33853 };
33854
33855 Roo.tree.TreeNodeUI.prototype = {
33856     removeChild : function(node){
33857         if(this.rendered){
33858             this.ctNode.removeChild(node.ui.getEl());
33859         }
33860     },
33861
33862     beforeLoad : function(){
33863          this.addClass("x-tree-node-loading");
33864     },
33865
33866     afterLoad : function(){
33867          this.removeClass("x-tree-node-loading");
33868     },
33869
33870     onTextChange : function(node, text, oldText){
33871         if(this.rendered){
33872             this.textNode.innerHTML = text;
33873         }
33874     },
33875
33876     onDisableChange : function(node, state){
33877         this.disabled = state;
33878         if(state){
33879             this.addClass("x-tree-node-disabled");
33880         }else{
33881             this.removeClass("x-tree-node-disabled");
33882         }
33883     },
33884
33885     onSelectedChange : function(state){
33886         if(state){
33887             this.focus();
33888             this.addClass("x-tree-selected");
33889         }else{
33890             //this.blur();
33891             this.removeClass("x-tree-selected");
33892         }
33893     },
33894
33895     onMove : function(tree, node, oldParent, newParent, index, refNode){
33896         this.childIndent = null;
33897         if(this.rendered){
33898             var targetNode = newParent.ui.getContainer();
33899             if(!targetNode){//target not rendered
33900                 this.holder = document.createElement("div");
33901                 this.holder.appendChild(this.wrap);
33902                 return;
33903             }
33904             var insertBefore = refNode ? refNode.ui.getEl() : null;
33905             if(insertBefore){
33906                 targetNode.insertBefore(this.wrap, insertBefore);
33907             }else{
33908                 targetNode.appendChild(this.wrap);
33909             }
33910             this.node.renderIndent(true);
33911         }
33912     },
33913
33914     addClass : function(cls){
33915         if(this.elNode){
33916             Roo.fly(this.elNode).addClass(cls);
33917         }
33918     },
33919
33920     removeClass : function(cls){
33921         if(this.elNode){
33922             Roo.fly(this.elNode).removeClass(cls);
33923         }
33924     },
33925
33926     remove : function(){
33927         if(this.rendered){
33928             this.holder = document.createElement("div");
33929             this.holder.appendChild(this.wrap);
33930         }
33931     },
33932
33933     fireEvent : function(){
33934         return this.node.fireEvent.apply(this.node, arguments);
33935     },
33936
33937     initEvents : function(){
33938         this.node.on("move", this.onMove, this);
33939         var E = Roo.EventManager;
33940         var a = this.anchor;
33941
33942         var el = Roo.fly(a, '_treeui');
33943
33944         if(Roo.isOpera){ // opera render bug ignores the CSS
33945             el.setStyle("text-decoration", "none");
33946         }
33947
33948         el.on("click", this.onClick, this);
33949         el.on("dblclick", this.onDblClick, this);
33950
33951         if(this.checkbox){
33952             Roo.EventManager.on(this.checkbox,
33953                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33954         }
33955
33956         el.on("contextmenu", this.onContextMenu, this);
33957
33958         var icon = Roo.fly(this.iconNode);
33959         icon.on("click", this.onClick, this);
33960         icon.on("dblclick", this.onDblClick, this);
33961         icon.on("contextmenu", this.onContextMenu, this);
33962         E.on(this.ecNode, "click", this.ecClick, this, true);
33963
33964         if(this.node.disabled){
33965             this.addClass("x-tree-node-disabled");
33966         }
33967         if(this.node.hidden){
33968             this.addClass("x-tree-node-disabled");
33969         }
33970         var ot = this.node.getOwnerTree();
33971         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33972         if(dd && (!this.node.isRoot || ot.rootVisible)){
33973             Roo.dd.Registry.register(this.elNode, {
33974                 node: this.node,
33975                 handles: this.getDDHandles(),
33976                 isHandle: false
33977             });
33978         }
33979     },
33980
33981     getDDHandles : function(){
33982         return [this.iconNode, this.textNode];
33983     },
33984
33985     hide : function(){
33986         if(this.rendered){
33987             this.wrap.style.display = "none";
33988         }
33989     },
33990
33991     show : function(){
33992         if(this.rendered){
33993             this.wrap.style.display = "";
33994         }
33995     },
33996
33997     onContextMenu : function(e){
33998         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33999             e.preventDefault();
34000             this.focus();
34001             this.fireEvent("contextmenu", this.node, e);
34002         }
34003     },
34004
34005     onClick : function(e){
34006         if(this.dropping){
34007             e.stopEvent();
34008             return;
34009         }
34010         if(this.fireEvent("beforeclick", this.node, e) !== false){
34011             if(!this.disabled && this.node.attributes.href){
34012                 this.fireEvent("click", this.node, e);
34013                 return;
34014             }
34015             e.preventDefault();
34016             if(this.disabled){
34017                 return;
34018             }
34019
34020             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
34021                 this.node.toggle();
34022             }
34023
34024             this.fireEvent("click", this.node, e);
34025         }else{
34026             e.stopEvent();
34027         }
34028     },
34029
34030     onDblClick : function(e){
34031         e.preventDefault();
34032         if(this.disabled){
34033             return;
34034         }
34035         if(this.checkbox){
34036             this.toggleCheck();
34037         }
34038         if(!this.animating && this.node.hasChildNodes()){
34039             this.node.toggle();
34040         }
34041         this.fireEvent("dblclick", this.node, e);
34042     },
34043
34044     onCheckChange : function(){
34045         var checked = this.checkbox.checked;
34046         this.node.attributes.checked = checked;
34047         this.fireEvent('checkchange', this.node, checked);
34048     },
34049
34050     ecClick : function(e){
34051         if(!this.animating && this.node.hasChildNodes()){
34052             this.node.toggle();
34053         }
34054     },
34055
34056     startDrop : function(){
34057         this.dropping = true;
34058     },
34059
34060     // delayed drop so the click event doesn't get fired on a drop
34061     endDrop : function(){
34062        setTimeout(function(){
34063            this.dropping = false;
34064        }.createDelegate(this), 50);
34065     },
34066
34067     expand : function(){
34068         this.updateExpandIcon();
34069         this.ctNode.style.display = "";
34070     },
34071
34072     focus : function(){
34073         if(!this.node.preventHScroll){
34074             try{this.anchor.focus();
34075             }catch(e){}
34076         }else if(!Roo.isIE){
34077             try{
34078                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
34079                 var l = noscroll.scrollLeft;
34080                 this.anchor.focus();
34081                 noscroll.scrollLeft = l;
34082             }catch(e){}
34083         }
34084     },
34085
34086     toggleCheck : function(value){
34087         var cb = this.checkbox;
34088         if(cb){
34089             cb.checked = (value === undefined ? !cb.checked : value);
34090         }
34091     },
34092
34093     blur : function(){
34094         try{
34095             this.anchor.blur();
34096         }catch(e){}
34097     },
34098
34099     animExpand : function(callback){
34100         var ct = Roo.get(this.ctNode);
34101         ct.stopFx();
34102         if(!this.node.hasChildNodes()){
34103             this.updateExpandIcon();
34104             this.ctNode.style.display = "";
34105             Roo.callback(callback);
34106             return;
34107         }
34108         this.animating = true;
34109         this.updateExpandIcon();
34110
34111         ct.slideIn('t', {
34112            callback : function(){
34113                this.animating = false;
34114                Roo.callback(callback);
34115             },
34116             scope: this,
34117             duration: this.node.ownerTree.duration || .25
34118         });
34119     },
34120
34121     highlight : function(){
34122         var tree = this.node.getOwnerTree();
34123         Roo.fly(this.wrap).highlight(
34124             tree.hlColor || "C3DAF9",
34125             {endColor: tree.hlBaseColor}
34126         );
34127     },
34128
34129     collapse : function(){
34130         this.updateExpandIcon();
34131         this.ctNode.style.display = "none";
34132     },
34133
34134     animCollapse : function(callback){
34135         var ct = Roo.get(this.ctNode);
34136         ct.enableDisplayMode('block');
34137         ct.stopFx();
34138
34139         this.animating = true;
34140         this.updateExpandIcon();
34141
34142         ct.slideOut('t', {
34143             callback : function(){
34144                this.animating = false;
34145                Roo.callback(callback);
34146             },
34147             scope: this,
34148             duration: this.node.ownerTree.duration || .25
34149         });
34150     },
34151
34152     getContainer : function(){
34153         return this.ctNode;
34154     },
34155
34156     getEl : function(){
34157         return this.wrap;
34158     },
34159
34160     appendDDGhost : function(ghostNode){
34161         ghostNode.appendChild(this.elNode.cloneNode(true));
34162     },
34163
34164     getDDRepairXY : function(){
34165         return Roo.lib.Dom.getXY(this.iconNode);
34166     },
34167
34168     onRender : function(){
34169         this.render();
34170     },
34171
34172     render : function(bulkRender){
34173         var n = this.node, a = n.attributes;
34174         var targetNode = n.parentNode ?
34175               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
34176
34177         if(!this.rendered){
34178             this.rendered = true;
34179
34180             this.renderElements(n, a, targetNode, bulkRender);
34181
34182             if(a.qtip){
34183                if(this.textNode.setAttributeNS){
34184                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
34185                    if(a.qtipTitle){
34186                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
34187                    }
34188                }else{
34189                    this.textNode.setAttribute("ext:qtip", a.qtip);
34190                    if(a.qtipTitle){
34191                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
34192                    }
34193                }
34194             }else if(a.qtipCfg){
34195                 a.qtipCfg.target = Roo.id(this.textNode);
34196                 Roo.QuickTips.register(a.qtipCfg);
34197             }
34198             this.initEvents();
34199             if(!this.node.expanded){
34200                 this.updateExpandIcon();
34201             }
34202         }else{
34203             if(bulkRender === true) {
34204                 targetNode.appendChild(this.wrap);
34205             }
34206         }
34207     },
34208
34209     renderElements : function(n, a, targetNode, bulkRender)
34210     {
34211         // add some indent caching, this helps performance when rendering a large tree
34212         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34213         var t = n.getOwnerTree();
34214         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
34215         if (typeof(n.attributes.html) != 'undefined') {
34216             txt = n.attributes.html;
34217         }
34218         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
34219         var cb = typeof a.checked == 'boolean';
34220         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34221         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
34222             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
34223             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
34224             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
34225             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
34226             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
34227              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
34228                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
34229             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34230             "</li>"];
34231
34232         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34233             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34234                                 n.nextSibling.ui.getEl(), buf.join(""));
34235         }else{
34236             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34237         }
34238
34239         this.elNode = this.wrap.childNodes[0];
34240         this.ctNode = this.wrap.childNodes[1];
34241         var cs = this.elNode.childNodes;
34242         this.indentNode = cs[0];
34243         this.ecNode = cs[1];
34244         this.iconNode = cs[2];
34245         var index = 3;
34246         if(cb){
34247             this.checkbox = cs[3];
34248             index++;
34249         }
34250         this.anchor = cs[index];
34251         this.textNode = cs[index].firstChild;
34252     },
34253
34254     getAnchor : function(){
34255         return this.anchor;
34256     },
34257
34258     getTextEl : function(){
34259         return this.textNode;
34260     },
34261
34262     getIconEl : function(){
34263         return this.iconNode;
34264     },
34265
34266     isChecked : function(){
34267         return this.checkbox ? this.checkbox.checked : false;
34268     },
34269
34270     updateExpandIcon : function(){
34271         if(this.rendered){
34272             var n = this.node, c1, c2;
34273             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34274             var hasChild = n.hasChildNodes();
34275             if(hasChild){
34276                 if(n.expanded){
34277                     cls += "-minus";
34278                     c1 = "x-tree-node-collapsed";
34279                     c2 = "x-tree-node-expanded";
34280                 }else{
34281                     cls += "-plus";
34282                     c1 = "x-tree-node-expanded";
34283                     c2 = "x-tree-node-collapsed";
34284                 }
34285                 if(this.wasLeaf){
34286                     this.removeClass("x-tree-node-leaf");
34287                     this.wasLeaf = false;
34288                 }
34289                 if(this.c1 != c1 || this.c2 != c2){
34290                     Roo.fly(this.elNode).replaceClass(c1, c2);
34291                     this.c1 = c1; this.c2 = c2;
34292                 }
34293             }else{
34294                 // this changes non-leafs into leafs if they have no children.
34295                 // it's not very rational behaviour..
34296                 
34297                 if(!this.wasLeaf && this.node.leaf){
34298                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34299                     delete this.c1;
34300                     delete this.c2;
34301                     this.wasLeaf = true;
34302                 }
34303             }
34304             var ecc = "x-tree-ec-icon "+cls;
34305             if(this.ecc != ecc){
34306                 this.ecNode.className = ecc;
34307                 this.ecc = ecc;
34308             }
34309         }
34310     },
34311
34312     getChildIndent : function(){
34313         if(!this.childIndent){
34314             var buf = [];
34315             var p = this.node;
34316             while(p){
34317                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34318                     if(!p.isLast()) {
34319                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34320                     } else {
34321                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34322                     }
34323                 }
34324                 p = p.parentNode;
34325             }
34326             this.childIndent = buf.join("");
34327         }
34328         return this.childIndent;
34329     },
34330
34331     renderIndent : function(){
34332         if(this.rendered){
34333             var indent = "";
34334             var p = this.node.parentNode;
34335             if(p){
34336                 indent = p.ui.getChildIndent();
34337             }
34338             if(this.indentMarkup != indent){ // don't rerender if not required
34339                 this.indentNode.innerHTML = indent;
34340                 this.indentMarkup = indent;
34341             }
34342             this.updateExpandIcon();
34343         }
34344     }
34345 };
34346
34347 Roo.tree.RootTreeNodeUI = function(){
34348     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34349 };
34350 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34351     render : function(){
34352         if(!this.rendered){
34353             var targetNode = this.node.ownerTree.innerCt.dom;
34354             this.node.expanded = true;
34355             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34356             this.wrap = this.ctNode = targetNode.firstChild;
34357         }
34358     },
34359     collapse : function(){
34360     },
34361     expand : function(){
34362     }
34363 });/*
34364  * Based on:
34365  * Ext JS Library 1.1.1
34366  * Copyright(c) 2006-2007, Ext JS, LLC.
34367  *
34368  * Originally Released Under LGPL - original licence link has changed is not relivant.
34369  *
34370  * Fork - LGPL
34371  * <script type="text/javascript">
34372  */
34373 /**
34374  * @class Roo.tree.TreeLoader
34375  * @extends Roo.util.Observable
34376  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34377  * nodes from a specified URL. The response must be a javascript Array definition
34378  * who's elements are node definition objects. eg:
34379  * <pre><code>
34380 {  success : true,
34381    data :      [
34382    
34383     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34384     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34385     ]
34386 }
34387
34388
34389 </code></pre>
34390  * <br><br>
34391  * The old style respose with just an array is still supported, but not recommended.
34392  * <br><br>
34393  *
34394  * A server request is sent, and child nodes are loaded only when a node is expanded.
34395  * The loading node's id is passed to the server under the parameter name "node" to
34396  * enable the server to produce the correct child nodes.
34397  * <br><br>
34398  * To pass extra parameters, an event handler may be attached to the "beforeload"
34399  * event, and the parameters specified in the TreeLoader's baseParams property:
34400  * <pre><code>
34401     myTreeLoader.on("beforeload", function(treeLoader, node) {
34402         this.baseParams.category = node.attributes.category;
34403     }, this);
34404 </code></pre><
34405  * This would pass an HTTP parameter called "category" to the server containing
34406  * the value of the Node's "category" attribute.
34407  * @constructor
34408  * Creates a new Treeloader.
34409  * @param {Object} config A config object containing config properties.
34410  */
34411 Roo.tree.TreeLoader = function(config){
34412     this.baseParams = {};
34413     this.requestMethod = "POST";
34414     Roo.apply(this, config);
34415
34416     this.addEvents({
34417     
34418         /**
34419          * @event beforeload
34420          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34421          * @param {Object} This TreeLoader object.
34422          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34423          * @param {Object} callback The callback function specified in the {@link #load} call.
34424          */
34425         beforeload : true,
34426         /**
34427          * @event load
34428          * Fires when the node has been successfuly loaded.
34429          * @param {Object} This TreeLoader object.
34430          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34431          * @param {Object} response The response object containing the data from the server.
34432          */
34433         load : true,
34434         /**
34435          * @event loadexception
34436          * Fires if the network request failed.
34437          * @param {Object} This TreeLoader object.
34438          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34439          * @param {Object} response The response object containing the data from the server.
34440          */
34441         loadexception : true,
34442         /**
34443          * @event create
34444          * Fires before a node is created, enabling you to return custom Node types 
34445          * @param {Object} This TreeLoader object.
34446          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34447          */
34448         create : true
34449     });
34450
34451     Roo.tree.TreeLoader.superclass.constructor.call(this);
34452 };
34453
34454 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34455     /**
34456     * @cfg {String} dataUrl The URL from which to request a Json string which
34457     * specifies an array of node definition object representing the child nodes
34458     * to be loaded.
34459     */
34460     /**
34461     * @cfg {String} requestMethod either GET or POST
34462     * defaults to POST (due to BC)
34463     * to be loaded.
34464     */
34465     /**
34466     * @cfg {Object} baseParams (optional) An object containing properties which
34467     * specify HTTP parameters to be passed to each request for child nodes.
34468     */
34469     /**
34470     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34471     * created by this loader. If the attributes sent by the server have an attribute in this object,
34472     * they take priority.
34473     */
34474     /**
34475     * @cfg {Object} uiProviders (optional) An object containing properties which
34476     * 
34477     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34478     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34479     * <i>uiProvider</i> attribute of a returned child node is a string rather
34480     * than a reference to a TreeNodeUI implementation, this that string value
34481     * is used as a property name in the uiProviders object. You can define the provider named
34482     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34483     */
34484     uiProviders : {},
34485
34486     /**
34487     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34488     * child nodes before loading.
34489     */
34490     clearOnLoad : true,
34491
34492     /**
34493     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34494     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34495     * Grid query { data : [ .....] }
34496     */
34497     
34498     root : false,
34499      /**
34500     * @cfg {String} queryParam (optional) 
34501     * Name of the query as it will be passed on the querystring (defaults to 'node')
34502     * eg. the request will be ?node=[id]
34503     */
34504     
34505     
34506     queryParam: false,
34507     
34508     /**
34509      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34510      * This is called automatically when a node is expanded, but may be used to reload
34511      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34512      * @param {Roo.tree.TreeNode} node
34513      * @param {Function} callback
34514      */
34515     load : function(node, callback){
34516         if(this.clearOnLoad){
34517             while(node.firstChild){
34518                 node.removeChild(node.firstChild);
34519             }
34520         }
34521         if(node.attributes.children){ // preloaded json children
34522             var cs = node.attributes.children;
34523             for(var i = 0, len = cs.length; i < len; i++){
34524                 node.appendChild(this.createNode(cs[i]));
34525             }
34526             if(typeof callback == "function"){
34527                 callback();
34528             }
34529         }else if(this.dataUrl){
34530             this.requestData(node, callback);
34531         }
34532     },
34533
34534     getParams: function(node){
34535         var buf = [], bp = this.baseParams;
34536         for(var key in bp){
34537             if(typeof bp[key] != "function"){
34538                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34539             }
34540         }
34541         var n = this.queryParam === false ? 'node' : this.queryParam;
34542         buf.push(n + "=", encodeURIComponent(node.id));
34543         return buf.join("");
34544     },
34545
34546     requestData : function(node, callback){
34547         if(this.fireEvent("beforeload", this, node, callback) !== false){
34548             this.transId = Roo.Ajax.request({
34549                 method:this.requestMethod,
34550                 url: this.dataUrl||this.url,
34551                 success: this.handleResponse,
34552                 failure: this.handleFailure,
34553                 scope: this,
34554                 argument: {callback: callback, node: node},
34555                 params: this.getParams(node)
34556             });
34557         }else{
34558             // if the load is cancelled, make sure we notify
34559             // the node that we are done
34560             if(typeof callback == "function"){
34561                 callback();
34562             }
34563         }
34564     },
34565
34566     isLoading : function(){
34567         return this.transId ? true : false;
34568     },
34569
34570     abort : function(){
34571         if(this.isLoading()){
34572             Roo.Ajax.abort(this.transId);
34573         }
34574     },
34575
34576     // private
34577     createNode : function(attr)
34578     {
34579         // apply baseAttrs, nice idea Corey!
34580         if(this.baseAttrs){
34581             Roo.applyIf(attr, this.baseAttrs);
34582         }
34583         if(this.applyLoader !== false){
34584             attr.loader = this;
34585         }
34586         // uiProvider = depreciated..
34587         
34588         if(typeof(attr.uiProvider) == 'string'){
34589            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34590                 /**  eval:var:attr */ eval(attr.uiProvider);
34591         }
34592         if(typeof(this.uiProviders['default']) != 'undefined') {
34593             attr.uiProvider = this.uiProviders['default'];
34594         }
34595         
34596         this.fireEvent('create', this, attr);
34597         
34598         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34599         return(attr.leaf ?
34600                         new Roo.tree.TreeNode(attr) :
34601                         new Roo.tree.AsyncTreeNode(attr));
34602     },
34603
34604     processResponse : function(response, node, callback)
34605     {
34606         var json = response.responseText;
34607         try {
34608             
34609             var o = Roo.decode(json);
34610             
34611             if (this.root === false && typeof(o.success) != undefined) {
34612                 this.root = 'data'; // the default behaviour for list like data..
34613                 }
34614                 
34615             if (this.root !== false &&  !o.success) {
34616                 // it's a failure condition.
34617                 var a = response.argument;
34618                 this.fireEvent("loadexception", this, a.node, response);
34619                 Roo.log("Load failed - should have a handler really");
34620                 return;
34621             }
34622             
34623             
34624             
34625             if (this.root !== false) {
34626                  o = o[this.root];
34627             }
34628             
34629             for(var i = 0, len = o.length; i < len; i++){
34630                 var n = this.createNode(o[i]);
34631                 if(n){
34632                     node.appendChild(n);
34633                 }
34634             }
34635             if(typeof callback == "function"){
34636                 callback(this, node);
34637             }
34638         }catch(e){
34639             this.handleFailure(response);
34640         }
34641     },
34642
34643     handleResponse : function(response){
34644         this.transId = false;
34645         var a = response.argument;
34646         this.processResponse(response, a.node, a.callback);
34647         this.fireEvent("load", this, a.node, response);
34648     },
34649
34650     handleFailure : function(response)
34651     {
34652         // should handle failure better..
34653         this.transId = false;
34654         var a = response.argument;
34655         this.fireEvent("loadexception", this, a.node, response);
34656         if(typeof a.callback == "function"){
34657             a.callback(this, a.node);
34658         }
34659     }
34660 });/*
34661  * Based on:
34662  * Ext JS Library 1.1.1
34663  * Copyright(c) 2006-2007, Ext JS, LLC.
34664  *
34665  * Originally Released Under LGPL - original licence link has changed is not relivant.
34666  *
34667  * Fork - LGPL
34668  * <script type="text/javascript">
34669  */
34670
34671 /**
34672 * @class Roo.tree.TreeFilter
34673 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34674 * @param {TreePanel} tree
34675 * @param {Object} config (optional)
34676  */
34677 Roo.tree.TreeFilter = function(tree, config){
34678     this.tree = tree;
34679     this.filtered = {};
34680     Roo.apply(this, config);
34681 };
34682
34683 Roo.tree.TreeFilter.prototype = {
34684     clearBlank:false,
34685     reverse:false,
34686     autoClear:false,
34687     remove:false,
34688
34689      /**
34690      * Filter the data by a specific attribute.
34691      * @param {String/RegExp} value Either string that the attribute value
34692      * should start with or a RegExp to test against the attribute
34693      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34694      * @param {TreeNode} startNode (optional) The node to start the filter at.
34695      */
34696     filter : function(value, attr, startNode){
34697         attr = attr || "text";
34698         var f;
34699         if(typeof value == "string"){
34700             var vlen = value.length;
34701             // auto clear empty filter
34702             if(vlen == 0 && this.clearBlank){
34703                 this.clear();
34704                 return;
34705             }
34706             value = value.toLowerCase();
34707             f = function(n){
34708                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34709             };
34710         }else if(value.exec){ // regex?
34711             f = function(n){
34712                 return value.test(n.attributes[attr]);
34713             };
34714         }else{
34715             throw 'Illegal filter type, must be string or regex';
34716         }
34717         this.filterBy(f, null, startNode);
34718         },
34719
34720     /**
34721      * Filter by a function. The passed function will be called with each
34722      * node in the tree (or from the startNode). If the function returns true, the node is kept
34723      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34724      * @param {Function} fn The filter function
34725      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34726      */
34727     filterBy : function(fn, scope, startNode){
34728         startNode = startNode || this.tree.root;
34729         if(this.autoClear){
34730             this.clear();
34731         }
34732         var af = this.filtered, rv = this.reverse;
34733         var f = function(n){
34734             if(n == startNode){
34735                 return true;
34736             }
34737             if(af[n.id]){
34738                 return false;
34739             }
34740             var m = fn.call(scope || n, n);
34741             if(!m || rv){
34742                 af[n.id] = n;
34743                 n.ui.hide();
34744                 return false;
34745             }
34746             return true;
34747         };
34748         startNode.cascade(f);
34749         if(this.remove){
34750            for(var id in af){
34751                if(typeof id != "function"){
34752                    var n = af[id];
34753                    if(n && n.parentNode){
34754                        n.parentNode.removeChild(n);
34755                    }
34756                }
34757            }
34758         }
34759     },
34760
34761     /**
34762      * Clears the current filter. Note: with the "remove" option
34763      * set a filter cannot be cleared.
34764      */
34765     clear : function(){
34766         var t = this.tree;
34767         var af = this.filtered;
34768         for(var id in af){
34769             if(typeof id != "function"){
34770                 var n = af[id];
34771                 if(n){
34772                     n.ui.show();
34773                 }
34774             }
34775         }
34776         this.filtered = {};
34777     }
34778 };
34779 /*
34780  * Based on:
34781  * Ext JS Library 1.1.1
34782  * Copyright(c) 2006-2007, Ext JS, LLC.
34783  *
34784  * Originally Released Under LGPL - original licence link has changed is not relivant.
34785  *
34786  * Fork - LGPL
34787  * <script type="text/javascript">
34788  */
34789  
34790
34791 /**
34792  * @class Roo.tree.TreeSorter
34793  * Provides sorting of nodes in a TreePanel
34794  * 
34795  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34796  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34797  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34798  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34799  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34800  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34801  * @constructor
34802  * @param {TreePanel} tree
34803  * @param {Object} config
34804  */
34805 Roo.tree.TreeSorter = function(tree, config){
34806     Roo.apply(this, config);
34807     tree.on("beforechildrenrendered", this.doSort, this);
34808     tree.on("append", this.updateSort, this);
34809     tree.on("insert", this.updateSort, this);
34810     
34811     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34812     var p = this.property || "text";
34813     var sortType = this.sortType;
34814     var fs = this.folderSort;
34815     var cs = this.caseSensitive === true;
34816     var leafAttr = this.leafAttr || 'leaf';
34817
34818     this.sortFn = function(n1, n2){
34819         if(fs){
34820             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34821                 return 1;
34822             }
34823             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34824                 return -1;
34825             }
34826         }
34827         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34828         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34829         if(v1 < v2){
34830                         return dsc ? +1 : -1;
34831                 }else if(v1 > v2){
34832                         return dsc ? -1 : +1;
34833         }else{
34834                 return 0;
34835         }
34836     };
34837 };
34838
34839 Roo.tree.TreeSorter.prototype = {
34840     doSort : function(node){
34841         node.sort(this.sortFn);
34842     },
34843     
34844     compareNodes : function(n1, n2){
34845         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34846     },
34847     
34848     updateSort : function(tree, node){
34849         if(node.childrenRendered){
34850             this.doSort.defer(1, this, [node]);
34851         }
34852     }
34853 };/*
34854  * Based on:
34855  * Ext JS Library 1.1.1
34856  * Copyright(c) 2006-2007, Ext JS, LLC.
34857  *
34858  * Originally Released Under LGPL - original licence link has changed is not relivant.
34859  *
34860  * Fork - LGPL
34861  * <script type="text/javascript">
34862  */
34863
34864 if(Roo.dd.DropZone){
34865     
34866 Roo.tree.TreeDropZone = function(tree, config){
34867     this.allowParentInsert = false;
34868     this.allowContainerDrop = false;
34869     this.appendOnly = false;
34870     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34871     this.tree = tree;
34872     this.lastInsertClass = "x-tree-no-status";
34873     this.dragOverData = {};
34874 };
34875
34876 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34877     ddGroup : "TreeDD",
34878     scroll:  true,
34879     
34880     expandDelay : 1000,
34881     
34882     expandNode : function(node){
34883         if(node.hasChildNodes() && !node.isExpanded()){
34884             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34885         }
34886     },
34887     
34888     queueExpand : function(node){
34889         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34890     },
34891     
34892     cancelExpand : function(){
34893         if(this.expandProcId){
34894             clearTimeout(this.expandProcId);
34895             this.expandProcId = false;
34896         }
34897     },
34898     
34899     isValidDropPoint : function(n, pt, dd, e, data){
34900         if(!n || !data){ return false; }
34901         var targetNode = n.node;
34902         var dropNode = data.node;
34903         // default drop rules
34904         if(!(targetNode && targetNode.isTarget && pt)){
34905             return false;
34906         }
34907         if(pt == "append" && targetNode.allowChildren === false){
34908             return false;
34909         }
34910         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34911             return false;
34912         }
34913         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34914             return false;
34915         }
34916         // reuse the object
34917         var overEvent = this.dragOverData;
34918         overEvent.tree = this.tree;
34919         overEvent.target = targetNode;
34920         overEvent.data = data;
34921         overEvent.point = pt;
34922         overEvent.source = dd;
34923         overEvent.rawEvent = e;
34924         overEvent.dropNode = dropNode;
34925         overEvent.cancel = false;  
34926         var result = this.tree.fireEvent("nodedragover", overEvent);
34927         return overEvent.cancel === false && result !== false;
34928     },
34929     
34930     getDropPoint : function(e, n, dd)
34931     {
34932         var tn = n.node;
34933         if(tn.isRoot){
34934             return tn.allowChildren !== false ? "append" : false; // always append for root
34935         }
34936         var dragEl = n.ddel;
34937         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34938         var y = Roo.lib.Event.getPageY(e);
34939         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34940         
34941         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34942         var noAppend = tn.allowChildren === false;
34943         if(this.appendOnly || tn.parentNode.allowChildren === false){
34944             return noAppend ? false : "append";
34945         }
34946         var noBelow = false;
34947         if(!this.allowParentInsert){
34948             noBelow = tn.hasChildNodes() && tn.isExpanded();
34949         }
34950         var q = (b - t) / (noAppend ? 2 : 3);
34951         if(y >= t && y < (t + q)){
34952             return "above";
34953         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34954             return "below";
34955         }else{
34956             return "append";
34957         }
34958     },
34959     
34960     onNodeEnter : function(n, dd, e, data)
34961     {
34962         this.cancelExpand();
34963     },
34964     
34965     onNodeOver : function(n, dd, e, data)
34966     {
34967        
34968         var pt = this.getDropPoint(e, n, dd);
34969         var node = n.node;
34970         
34971         // auto node expand check
34972         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34973             this.queueExpand(node);
34974         }else if(pt != "append"){
34975             this.cancelExpand();
34976         }
34977         
34978         // set the insert point style on the target node
34979         var returnCls = this.dropNotAllowed;
34980         if(this.isValidDropPoint(n, pt, dd, e, data)){
34981            if(pt){
34982                var el = n.ddel;
34983                var cls;
34984                if(pt == "above"){
34985                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34986                    cls = "x-tree-drag-insert-above";
34987                }else if(pt == "below"){
34988                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34989                    cls = "x-tree-drag-insert-below";
34990                }else{
34991                    returnCls = "x-tree-drop-ok-append";
34992                    cls = "x-tree-drag-append";
34993                }
34994                if(this.lastInsertClass != cls){
34995                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34996                    this.lastInsertClass = cls;
34997                }
34998            }
34999        }
35000        return returnCls;
35001     },
35002     
35003     onNodeOut : function(n, dd, e, data){
35004         
35005         this.cancelExpand();
35006         this.removeDropIndicators(n);
35007     },
35008     
35009     onNodeDrop : function(n, dd, e, data){
35010         var point = this.getDropPoint(e, n, dd);
35011         var targetNode = n.node;
35012         targetNode.ui.startDrop();
35013         if(!this.isValidDropPoint(n, point, dd, e, data)){
35014             targetNode.ui.endDrop();
35015             return false;
35016         }
35017         // first try to find the drop node
35018         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
35019         var dropEvent = {
35020             tree : this.tree,
35021             target: targetNode,
35022             data: data,
35023             point: point,
35024             source: dd,
35025             rawEvent: e,
35026             dropNode: dropNode,
35027             cancel: !dropNode   
35028         };
35029         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
35030         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
35031             targetNode.ui.endDrop();
35032             return false;
35033         }
35034         // allow target changing
35035         targetNode = dropEvent.target;
35036         if(point == "append" && !targetNode.isExpanded()){
35037             targetNode.expand(false, null, function(){
35038                 this.completeDrop(dropEvent);
35039             }.createDelegate(this));
35040         }else{
35041             this.completeDrop(dropEvent);
35042         }
35043         return true;
35044     },
35045     
35046     completeDrop : function(de){
35047         var ns = de.dropNode, p = de.point, t = de.target;
35048         if(!(ns instanceof Array)){
35049             ns = [ns];
35050         }
35051         var n;
35052         for(var i = 0, len = ns.length; i < len; i++){
35053             n = ns[i];
35054             if(p == "above"){
35055                 t.parentNode.insertBefore(n, t);
35056             }else if(p == "below"){
35057                 t.parentNode.insertBefore(n, t.nextSibling);
35058             }else{
35059                 t.appendChild(n);
35060             }
35061         }
35062         n.ui.focus();
35063         if(this.tree.hlDrop){
35064             n.ui.highlight();
35065         }
35066         t.ui.endDrop();
35067         this.tree.fireEvent("nodedrop", de);
35068     },
35069     
35070     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
35071         if(this.tree.hlDrop){
35072             dropNode.ui.focus();
35073             dropNode.ui.highlight();
35074         }
35075         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
35076     },
35077     
35078     getTree : function(){
35079         return this.tree;
35080     },
35081     
35082     removeDropIndicators : function(n){
35083         if(n && n.ddel){
35084             var el = n.ddel;
35085             Roo.fly(el).removeClass([
35086                     "x-tree-drag-insert-above",
35087                     "x-tree-drag-insert-below",
35088                     "x-tree-drag-append"]);
35089             this.lastInsertClass = "_noclass";
35090         }
35091     },
35092     
35093     beforeDragDrop : function(target, e, id){
35094         this.cancelExpand();
35095         return true;
35096     },
35097     
35098     afterRepair : function(data){
35099         if(data && Roo.enableFx){
35100             data.node.ui.highlight();
35101         }
35102         this.hideProxy();
35103     } 
35104     
35105 });
35106
35107 }
35108 /*
35109  * Based on:
35110  * Ext JS Library 1.1.1
35111  * Copyright(c) 2006-2007, Ext JS, LLC.
35112  *
35113  * Originally Released Under LGPL - original licence link has changed is not relivant.
35114  *
35115  * Fork - LGPL
35116  * <script type="text/javascript">
35117  */
35118  
35119
35120 if(Roo.dd.DragZone){
35121 Roo.tree.TreeDragZone = function(tree, config){
35122     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
35123     this.tree = tree;
35124 };
35125
35126 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
35127     ddGroup : "TreeDD",
35128    
35129     onBeforeDrag : function(data, e){
35130         var n = data.node;
35131         return n && n.draggable && !n.disabled;
35132     },
35133      
35134     
35135     onInitDrag : function(e){
35136         var data = this.dragData;
35137         this.tree.getSelectionModel().select(data.node);
35138         this.proxy.update("");
35139         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
35140         this.tree.fireEvent("startdrag", this.tree, data.node, e);
35141     },
35142     
35143     getRepairXY : function(e, data){
35144         return data.node.ui.getDDRepairXY();
35145     },
35146     
35147     onEndDrag : function(data, e){
35148         this.tree.fireEvent("enddrag", this.tree, data.node, e);
35149         
35150         
35151     },
35152     
35153     onValidDrop : function(dd, e, id){
35154         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
35155         this.hideProxy();
35156     },
35157     
35158     beforeInvalidDrop : function(e, id){
35159         // this scrolls the original position back into view
35160         var sm = this.tree.getSelectionModel();
35161         sm.clearSelections();
35162         sm.select(this.dragData.node);
35163     }
35164 });
35165 }/*
35166  * Based on:
35167  * Ext JS Library 1.1.1
35168  * Copyright(c) 2006-2007, Ext JS, LLC.
35169  *
35170  * Originally Released Under LGPL - original licence link has changed is not relivant.
35171  *
35172  * Fork - LGPL
35173  * <script type="text/javascript">
35174  */
35175 /**
35176  * @class Roo.tree.TreeEditor
35177  * @extends Roo.Editor
35178  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
35179  * as the editor field.
35180  * @constructor
35181  * @param {Object} config (used to be the tree panel.)
35182  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
35183  * 
35184  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
35185  * @cfg {Roo.form.TextField|Object} field The field configuration
35186  *
35187  * 
35188  */
35189 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
35190     var tree = config;
35191     var field;
35192     if (oldconfig) { // old style..
35193         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
35194     } else {
35195         // new style..
35196         tree = config.tree;
35197         config.field = config.field  || {};
35198         config.field.xtype = 'TextField';
35199         field = Roo.factory(config.field, Roo.form);
35200     }
35201     config = config || {};
35202     
35203     
35204     this.addEvents({
35205         /**
35206          * @event beforenodeedit
35207          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35208          * false from the handler of this event.
35209          * @param {Editor} this
35210          * @param {Roo.tree.Node} node 
35211          */
35212         "beforenodeedit" : true
35213     });
35214     
35215     //Roo.log(config);
35216     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
35217
35218     this.tree = tree;
35219
35220     tree.on('beforeclick', this.beforeNodeClick, this);
35221     tree.getTreeEl().on('mousedown', this.hide, this);
35222     this.on('complete', this.updateNode, this);
35223     this.on('beforestartedit', this.fitToTree, this);
35224     this.on('startedit', this.bindScroll, this, {delay:10});
35225     this.on('specialkey', this.onSpecialKey, this);
35226 };
35227
35228 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
35229     /**
35230      * @cfg {String} alignment
35231      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
35232      */
35233     alignment: "l-l",
35234     // inherit
35235     autoSize: false,
35236     /**
35237      * @cfg {Boolean} hideEl
35238      * True to hide the bound element while the editor is displayed (defaults to false)
35239      */
35240     hideEl : false,
35241     /**
35242      * @cfg {String} cls
35243      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35244      */
35245     cls: "x-small-editor x-tree-editor",
35246     /**
35247      * @cfg {Boolean} shim
35248      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35249      */
35250     shim:false,
35251     // inherit
35252     shadow:"frame",
35253     /**
35254      * @cfg {Number} maxWidth
35255      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35256      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35257      * scroll and client offsets into account prior to each edit.
35258      */
35259     maxWidth: 250,
35260
35261     editDelay : 350,
35262
35263     // private
35264     fitToTree : function(ed, el){
35265         var td = this.tree.getTreeEl().dom, nd = el.dom;
35266         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35267             td.scrollLeft = nd.offsetLeft;
35268         }
35269         var w = Math.min(
35270                 this.maxWidth,
35271                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35272         this.setSize(w, '');
35273         
35274         return this.fireEvent('beforenodeedit', this, this.editNode);
35275         
35276     },
35277
35278     // private
35279     triggerEdit : function(node){
35280         this.completeEdit();
35281         this.editNode = node;
35282         this.startEdit(node.ui.textNode, node.text);
35283     },
35284
35285     // private
35286     bindScroll : function(){
35287         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35288     },
35289
35290     // private
35291     beforeNodeClick : function(node, e){
35292         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35293         this.lastClick = new Date();
35294         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35295             e.stopEvent();
35296             this.triggerEdit(node);
35297             return false;
35298         }
35299         return true;
35300     },
35301
35302     // private
35303     updateNode : function(ed, value){
35304         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35305         this.editNode.setText(value);
35306     },
35307
35308     // private
35309     onHide : function(){
35310         Roo.tree.TreeEditor.superclass.onHide.call(this);
35311         if(this.editNode){
35312             this.editNode.ui.focus();
35313         }
35314     },
35315
35316     // private
35317     onSpecialKey : function(field, e){
35318         var k = e.getKey();
35319         if(k == e.ESC){
35320             e.stopEvent();
35321             this.cancelEdit();
35322         }else if(k == e.ENTER && !e.hasModifier()){
35323             e.stopEvent();
35324             this.completeEdit();
35325         }
35326     }
35327 });//<Script type="text/javascript">
35328 /*
35329  * Based on:
35330  * Ext JS Library 1.1.1
35331  * Copyright(c) 2006-2007, Ext JS, LLC.
35332  *
35333  * Originally Released Under LGPL - original licence link has changed is not relivant.
35334  *
35335  * Fork - LGPL
35336  * <script type="text/javascript">
35337  */
35338  
35339 /**
35340  * Not documented??? - probably should be...
35341  */
35342
35343 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35344     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35345     
35346     renderElements : function(n, a, targetNode, bulkRender){
35347         //consel.log("renderElements?");
35348         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35349
35350         var t = n.getOwnerTree();
35351         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35352         
35353         var cols = t.columns;
35354         var bw = t.borderWidth;
35355         var c = cols[0];
35356         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35357          var cb = typeof a.checked == "boolean";
35358         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35359         var colcls = 'x-t-' + tid + '-c0';
35360         var buf = [
35361             '<li class="x-tree-node">',
35362             
35363                 
35364                 '<div class="x-tree-node-el ', a.cls,'">',
35365                     // extran...
35366                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35367                 
35368                 
35369                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35370                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35371                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35372                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35373                            (a.iconCls ? ' '+a.iconCls : ''),
35374                            '" unselectable="on" />',
35375                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35376                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35377                              
35378                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35379                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35380                             '<span unselectable="on" qtip="' + tx + '">',
35381                              tx,
35382                              '</span></a>' ,
35383                     '</div>',
35384                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35385                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35386                  ];
35387         for(var i = 1, len = cols.length; i < len; i++){
35388             c = cols[i];
35389             colcls = 'x-t-' + tid + '-c' +i;
35390             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35391             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35392                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35393                       "</div>");
35394          }
35395          
35396          buf.push(
35397             '</a>',
35398             '<div class="x-clear"></div></div>',
35399             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35400             "</li>");
35401         
35402         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35403             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35404                                 n.nextSibling.ui.getEl(), buf.join(""));
35405         }else{
35406             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35407         }
35408         var el = this.wrap.firstChild;
35409         this.elRow = el;
35410         this.elNode = el.firstChild;
35411         this.ranchor = el.childNodes[1];
35412         this.ctNode = this.wrap.childNodes[1];
35413         var cs = el.firstChild.childNodes;
35414         this.indentNode = cs[0];
35415         this.ecNode = cs[1];
35416         this.iconNode = cs[2];
35417         var index = 3;
35418         if(cb){
35419             this.checkbox = cs[3];
35420             index++;
35421         }
35422         this.anchor = cs[index];
35423         
35424         this.textNode = cs[index].firstChild;
35425         
35426         //el.on("click", this.onClick, this);
35427         //el.on("dblclick", this.onDblClick, this);
35428         
35429         
35430        // console.log(this);
35431     },
35432     initEvents : function(){
35433         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35434         
35435             
35436         var a = this.ranchor;
35437
35438         var el = Roo.get(a);
35439
35440         if(Roo.isOpera){ // opera render bug ignores the CSS
35441             el.setStyle("text-decoration", "none");
35442         }
35443
35444         el.on("click", this.onClick, this);
35445         el.on("dblclick", this.onDblClick, this);
35446         el.on("contextmenu", this.onContextMenu, this);
35447         
35448     },
35449     
35450     /*onSelectedChange : function(state){
35451         if(state){
35452             this.focus();
35453             this.addClass("x-tree-selected");
35454         }else{
35455             //this.blur();
35456             this.removeClass("x-tree-selected");
35457         }
35458     },*/
35459     addClass : function(cls){
35460         if(this.elRow){
35461             Roo.fly(this.elRow).addClass(cls);
35462         }
35463         
35464     },
35465     
35466     
35467     removeClass : function(cls){
35468         if(this.elRow){
35469             Roo.fly(this.elRow).removeClass(cls);
35470         }
35471     }
35472
35473     
35474     
35475 });//<Script type="text/javascript">
35476
35477 /*
35478  * Based on:
35479  * Ext JS Library 1.1.1
35480  * Copyright(c) 2006-2007, Ext JS, LLC.
35481  *
35482  * Originally Released Under LGPL - original licence link has changed is not relivant.
35483  *
35484  * Fork - LGPL
35485  * <script type="text/javascript">
35486  */
35487  
35488
35489 /**
35490  * @class Roo.tree.ColumnTree
35491  * @extends Roo.data.TreePanel
35492  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35493  * @cfg {int} borderWidth  compined right/left border allowance
35494  * @constructor
35495  * @param {String/HTMLElement/Element} el The container element
35496  * @param {Object} config
35497  */
35498 Roo.tree.ColumnTree =  function(el, config)
35499 {
35500    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35501    this.addEvents({
35502         /**
35503         * @event resize
35504         * Fire this event on a container when it resizes
35505         * @param {int} w Width
35506         * @param {int} h Height
35507         */
35508        "resize" : true
35509     });
35510     this.on('resize', this.onResize, this);
35511 };
35512
35513 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35514     //lines:false,
35515     
35516     
35517     borderWidth: Roo.isBorderBox ? 0 : 2, 
35518     headEls : false,
35519     
35520     render : function(){
35521         // add the header.....
35522        
35523         Roo.tree.ColumnTree.superclass.render.apply(this);
35524         
35525         this.el.addClass('x-column-tree');
35526         
35527         this.headers = this.el.createChild(
35528             {cls:'x-tree-headers'},this.innerCt.dom);
35529    
35530         var cols = this.columns, c;
35531         var totalWidth = 0;
35532         this.headEls = [];
35533         var  len = cols.length;
35534         for(var i = 0; i < len; i++){
35535              c = cols[i];
35536              totalWidth += c.width;
35537             this.headEls.push(this.headers.createChild({
35538                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35539                  cn: {
35540                      cls:'x-tree-hd-text',
35541                      html: c.header
35542                  },
35543                  style:'width:'+(c.width-this.borderWidth)+'px;'
35544              }));
35545         }
35546         this.headers.createChild({cls:'x-clear'});
35547         // prevent floats from wrapping when clipped
35548         this.headers.setWidth(totalWidth);
35549         //this.innerCt.setWidth(totalWidth);
35550         this.innerCt.setStyle({ overflow: 'auto' });
35551         this.onResize(this.width, this.height);
35552              
35553         
35554     },
35555     onResize : function(w,h)
35556     {
35557         this.height = h;
35558         this.width = w;
35559         // resize cols..
35560         this.innerCt.setWidth(this.width);
35561         this.innerCt.setHeight(this.height-20);
35562         
35563         // headers...
35564         var cols = this.columns, c;
35565         var totalWidth = 0;
35566         var expEl = false;
35567         var len = cols.length;
35568         for(var i = 0; i < len; i++){
35569             c = cols[i];
35570             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35571                 // it's the expander..
35572                 expEl  = this.headEls[i];
35573                 continue;
35574             }
35575             totalWidth += c.width;
35576             
35577         }
35578         if (expEl) {
35579             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35580         }
35581         this.headers.setWidth(w-20);
35582
35583         
35584         
35585         
35586     }
35587 });
35588 /*
35589  * Based on:
35590  * Ext JS Library 1.1.1
35591  * Copyright(c) 2006-2007, Ext JS, LLC.
35592  *
35593  * Originally Released Under LGPL - original licence link has changed is not relivant.
35594  *
35595  * Fork - LGPL
35596  * <script type="text/javascript">
35597  */
35598  
35599 /**
35600  * @class Roo.menu.Menu
35601  * @extends Roo.util.Observable
35602  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35603  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35604  * @constructor
35605  * Creates a new Menu
35606  * @param {Object} config Configuration options
35607  */
35608 Roo.menu.Menu = function(config){
35609     Roo.apply(this, config);
35610     this.id = this.id || Roo.id();
35611     this.addEvents({
35612         /**
35613          * @event beforeshow
35614          * Fires before this menu is displayed
35615          * @param {Roo.menu.Menu} this
35616          */
35617         beforeshow : true,
35618         /**
35619          * @event beforehide
35620          * Fires before this menu is hidden
35621          * @param {Roo.menu.Menu} this
35622          */
35623         beforehide : true,
35624         /**
35625          * @event show
35626          * Fires after this menu is displayed
35627          * @param {Roo.menu.Menu} this
35628          */
35629         show : true,
35630         /**
35631          * @event hide
35632          * Fires after this menu is hidden
35633          * @param {Roo.menu.Menu} this
35634          */
35635         hide : true,
35636         /**
35637          * @event click
35638          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35639          * @param {Roo.menu.Menu} this
35640          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35641          * @param {Roo.EventObject} e
35642          */
35643         click : true,
35644         /**
35645          * @event mouseover
35646          * Fires when the mouse is hovering over this menu
35647          * @param {Roo.menu.Menu} this
35648          * @param {Roo.EventObject} e
35649          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35650          */
35651         mouseover : true,
35652         /**
35653          * @event mouseout
35654          * Fires when the mouse exits this menu
35655          * @param {Roo.menu.Menu} this
35656          * @param {Roo.EventObject} e
35657          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35658          */
35659         mouseout : true,
35660         /**
35661          * @event itemclick
35662          * Fires when a menu item contained in this menu is clicked
35663          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35664          * @param {Roo.EventObject} e
35665          */
35666         itemclick: true
35667     });
35668     if (this.registerMenu) {
35669         Roo.menu.MenuMgr.register(this);
35670     }
35671     
35672     var mis = this.items;
35673     this.items = new Roo.util.MixedCollection();
35674     if(mis){
35675         this.add.apply(this, mis);
35676     }
35677 };
35678
35679 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35680     /**
35681      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35682      */
35683     minWidth : 120,
35684     /**
35685      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35686      * for bottom-right shadow (defaults to "sides")
35687      */
35688     shadow : "sides",
35689     /**
35690      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35691      * this menu (defaults to "tl-tr?")
35692      */
35693     subMenuAlign : "tl-tr?",
35694     /**
35695      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35696      * relative to its element of origin (defaults to "tl-bl?")
35697      */
35698     defaultAlign : "tl-bl?",
35699     /**
35700      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35701      */
35702     allowOtherMenus : false,
35703     /**
35704      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35705      */
35706     registerMenu : true,
35707
35708     hidden:true,
35709
35710     // private
35711     render : function(){
35712         if(this.el){
35713             return;
35714         }
35715         var el = this.el = new Roo.Layer({
35716             cls: "x-menu",
35717             shadow:this.shadow,
35718             constrain: false,
35719             parentEl: this.parentEl || document.body,
35720             zindex:15000
35721         });
35722
35723         this.keyNav = new Roo.menu.MenuNav(this);
35724
35725         if(this.plain){
35726             el.addClass("x-menu-plain");
35727         }
35728         if(this.cls){
35729             el.addClass(this.cls);
35730         }
35731         // generic focus element
35732         this.focusEl = el.createChild({
35733             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35734         });
35735         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35736         //disabling touch- as it's causing issues ..
35737         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35738         ul.on('click'   , this.onClick, this);
35739         
35740         
35741         ul.on("mouseover", this.onMouseOver, this);
35742         ul.on("mouseout", this.onMouseOut, this);
35743         this.items.each(function(item){
35744             if (item.hidden) {
35745                 return;
35746             }
35747             
35748             var li = document.createElement("li");
35749             li.className = "x-menu-list-item";
35750             ul.dom.appendChild(li);
35751             item.render(li, this);
35752         }, this);
35753         this.ul = ul;
35754         this.autoWidth();
35755     },
35756
35757     // private
35758     autoWidth : function(){
35759         var el = this.el, ul = this.ul;
35760         if(!el){
35761             return;
35762         }
35763         var w = this.width;
35764         if(w){
35765             el.setWidth(w);
35766         }else if(Roo.isIE){
35767             el.setWidth(this.minWidth);
35768             var t = el.dom.offsetWidth; // force recalc
35769             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35770         }
35771     },
35772
35773     // private
35774     delayAutoWidth : function(){
35775         if(this.rendered){
35776             if(!this.awTask){
35777                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35778             }
35779             this.awTask.delay(20);
35780         }
35781     },
35782
35783     // private
35784     findTargetItem : function(e){
35785         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35786         if(t && t.menuItemId){
35787             return this.items.get(t.menuItemId);
35788         }
35789     },
35790
35791     // private
35792     onClick : function(e){
35793         Roo.log("menu.onClick");
35794         var t = this.findTargetItem(e);
35795         if(!t){
35796             return;
35797         }
35798         Roo.log(e);
35799         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35800             if(t == this.activeItem && t.shouldDeactivate(e)){
35801                 this.activeItem.deactivate();
35802                 delete this.activeItem;
35803                 return;
35804             }
35805             if(t.canActivate){
35806                 this.setActiveItem(t, true);
35807             }
35808             return;
35809             
35810             
35811         }
35812         
35813         t.onClick(e);
35814         this.fireEvent("click", this, t, e);
35815     },
35816
35817     // private
35818     setActiveItem : function(item, autoExpand){
35819         if(item != this.activeItem){
35820             if(this.activeItem){
35821                 this.activeItem.deactivate();
35822             }
35823             this.activeItem = item;
35824             item.activate(autoExpand);
35825         }else if(autoExpand){
35826             item.expandMenu();
35827         }
35828     },
35829
35830     // private
35831     tryActivate : function(start, step){
35832         var items = this.items;
35833         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35834             var item = items.get(i);
35835             if(!item.disabled && item.canActivate){
35836                 this.setActiveItem(item, false);
35837                 return item;
35838             }
35839         }
35840         return false;
35841     },
35842
35843     // private
35844     onMouseOver : function(e){
35845         var t;
35846         if(t = this.findTargetItem(e)){
35847             if(t.canActivate && !t.disabled){
35848                 this.setActiveItem(t, true);
35849             }
35850         }
35851         this.fireEvent("mouseover", this, e, t);
35852     },
35853
35854     // private
35855     onMouseOut : function(e){
35856         var t;
35857         if(t = this.findTargetItem(e)){
35858             if(t == this.activeItem && t.shouldDeactivate(e)){
35859                 this.activeItem.deactivate();
35860                 delete this.activeItem;
35861             }
35862         }
35863         this.fireEvent("mouseout", this, e, t);
35864     },
35865
35866     /**
35867      * Read-only.  Returns true if the menu is currently displayed, else false.
35868      * @type Boolean
35869      */
35870     isVisible : function(){
35871         return this.el && !this.hidden;
35872     },
35873
35874     /**
35875      * Displays this menu relative to another element
35876      * @param {String/HTMLElement/Roo.Element} element The element to align to
35877      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35878      * the element (defaults to this.defaultAlign)
35879      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35880      */
35881     show : function(el, pos, parentMenu){
35882         this.parentMenu = parentMenu;
35883         if(!this.el){
35884             this.render();
35885         }
35886         this.fireEvent("beforeshow", this);
35887         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35888     },
35889
35890     /**
35891      * Displays this menu at a specific xy position
35892      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35893      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35894      */
35895     showAt : function(xy, parentMenu, /* private: */_e){
35896         this.parentMenu = parentMenu;
35897         if(!this.el){
35898             this.render();
35899         }
35900         if(_e !== false){
35901             this.fireEvent("beforeshow", this);
35902             xy = this.el.adjustForConstraints(xy);
35903         }
35904         this.el.setXY(xy);
35905         this.el.show();
35906         this.hidden = false;
35907         this.focus();
35908         this.fireEvent("show", this);
35909     },
35910
35911     focus : function(){
35912         if(!this.hidden){
35913             this.doFocus.defer(50, this);
35914         }
35915     },
35916
35917     doFocus : function(){
35918         if(!this.hidden){
35919             this.focusEl.focus();
35920         }
35921     },
35922
35923     /**
35924      * Hides this menu and optionally all parent menus
35925      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35926      */
35927     hide : function(deep){
35928         if(this.el && this.isVisible()){
35929             this.fireEvent("beforehide", this);
35930             if(this.activeItem){
35931                 this.activeItem.deactivate();
35932                 this.activeItem = null;
35933             }
35934             this.el.hide();
35935             this.hidden = true;
35936             this.fireEvent("hide", this);
35937         }
35938         if(deep === true && this.parentMenu){
35939             this.parentMenu.hide(true);
35940         }
35941     },
35942
35943     /**
35944      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35945      * Any of the following are valid:
35946      * <ul>
35947      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35948      * <li>An HTMLElement object which will be converted to a menu item</li>
35949      * <li>A menu item config object that will be created as a new menu item</li>
35950      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35951      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35952      * </ul>
35953      * Usage:
35954      * <pre><code>
35955 // Create the menu
35956 var menu = new Roo.menu.Menu();
35957
35958 // Create a menu item to add by reference
35959 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35960
35961 // Add a bunch of items at once using different methods.
35962 // Only the last item added will be returned.
35963 var item = menu.add(
35964     menuItem,                // add existing item by ref
35965     'Dynamic Item',          // new TextItem
35966     '-',                     // new separator
35967     { text: 'Config Item' }  // new item by config
35968 );
35969 </code></pre>
35970      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35971      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35972      */
35973     add : function(){
35974         var a = arguments, l = a.length, item;
35975         for(var i = 0; i < l; i++){
35976             var el = a[i];
35977             if ((typeof(el) == "object") && el.xtype && el.xns) {
35978                 el = Roo.factory(el, Roo.menu);
35979             }
35980             
35981             if(el.render){ // some kind of Item
35982                 item = this.addItem(el);
35983             }else if(typeof el == "string"){ // string
35984                 if(el == "separator" || el == "-"){
35985                     item = this.addSeparator();
35986                 }else{
35987                     item = this.addText(el);
35988                 }
35989             }else if(el.tagName || el.el){ // element
35990                 item = this.addElement(el);
35991             }else if(typeof el == "object"){ // must be menu item config?
35992                 item = this.addMenuItem(el);
35993             }
35994         }
35995         return item;
35996     },
35997
35998     /**
35999      * Returns this menu's underlying {@link Roo.Element} object
36000      * @return {Roo.Element} The element
36001      */
36002     getEl : function(){
36003         if(!this.el){
36004             this.render();
36005         }
36006         return this.el;
36007     },
36008
36009     /**
36010      * Adds a separator bar to the menu
36011      * @return {Roo.menu.Item} The menu item that was added
36012      */
36013     addSeparator : function(){
36014         return this.addItem(new Roo.menu.Separator());
36015     },
36016
36017     /**
36018      * Adds an {@link Roo.Element} object to the menu
36019      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
36020      * @return {Roo.menu.Item} The menu item that was added
36021      */
36022     addElement : function(el){
36023         return this.addItem(new Roo.menu.BaseItem(el));
36024     },
36025
36026     /**
36027      * Adds an existing object based on {@link Roo.menu.Item} to the menu
36028      * @param {Roo.menu.Item} item The menu item to add
36029      * @return {Roo.menu.Item} The menu item that was added
36030      */
36031     addItem : function(item){
36032         this.items.add(item);
36033         if(this.ul){
36034             var li = document.createElement("li");
36035             li.className = "x-menu-list-item";
36036             this.ul.dom.appendChild(li);
36037             item.render(li, this);
36038             this.delayAutoWidth();
36039         }
36040         return item;
36041     },
36042
36043     /**
36044      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
36045      * @param {Object} config A MenuItem config object
36046      * @return {Roo.menu.Item} The menu item that was added
36047      */
36048     addMenuItem : function(config){
36049         if(!(config instanceof Roo.menu.Item)){
36050             if(typeof config.checked == "boolean"){ // must be check menu item config?
36051                 config = new Roo.menu.CheckItem(config);
36052             }else{
36053                 config = new Roo.menu.Item(config);
36054             }
36055         }
36056         return this.addItem(config);
36057     },
36058
36059     /**
36060      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
36061      * @param {String} text The text to display in the menu item
36062      * @return {Roo.menu.Item} The menu item that was added
36063      */
36064     addText : function(text){
36065         return this.addItem(new Roo.menu.TextItem({ text : text }));
36066     },
36067
36068     /**
36069      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
36070      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
36071      * @param {Roo.menu.Item} item The menu item to add
36072      * @return {Roo.menu.Item} The menu item that was added
36073      */
36074     insert : function(index, item){
36075         this.items.insert(index, item);
36076         if(this.ul){
36077             var li = document.createElement("li");
36078             li.className = "x-menu-list-item";
36079             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
36080             item.render(li, this);
36081             this.delayAutoWidth();
36082         }
36083         return item;
36084     },
36085
36086     /**
36087      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
36088      * @param {Roo.menu.Item} item The menu item to remove
36089      */
36090     remove : function(item){
36091         this.items.removeKey(item.id);
36092         item.destroy();
36093     },
36094
36095     /**
36096      * Removes and destroys all items in the menu
36097      */
36098     removeAll : function(){
36099         var f;
36100         while(f = this.items.first()){
36101             this.remove(f);
36102         }
36103     }
36104 });
36105
36106 // MenuNav is a private utility class used internally by the Menu
36107 Roo.menu.MenuNav = function(menu){
36108     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
36109     this.scope = this.menu = menu;
36110 };
36111
36112 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
36113     doRelay : function(e, h){
36114         var k = e.getKey();
36115         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
36116             this.menu.tryActivate(0, 1);
36117             return false;
36118         }
36119         return h.call(this.scope || this, e, this.menu);
36120     },
36121
36122     up : function(e, m){
36123         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
36124             m.tryActivate(m.items.length-1, -1);
36125         }
36126     },
36127
36128     down : function(e, m){
36129         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
36130             m.tryActivate(0, 1);
36131         }
36132     },
36133
36134     right : function(e, m){
36135         if(m.activeItem){
36136             m.activeItem.expandMenu(true);
36137         }
36138     },
36139
36140     left : function(e, m){
36141         m.hide();
36142         if(m.parentMenu && m.parentMenu.activeItem){
36143             m.parentMenu.activeItem.activate();
36144         }
36145     },
36146
36147     enter : function(e, m){
36148         if(m.activeItem){
36149             e.stopPropagation();
36150             m.activeItem.onClick(e);
36151             m.fireEvent("click", this, m.activeItem);
36152             return true;
36153         }
36154     }
36155 });/*
36156  * Based on:
36157  * Ext JS Library 1.1.1
36158  * Copyright(c) 2006-2007, Ext JS, LLC.
36159  *
36160  * Originally Released Under LGPL - original licence link has changed is not relivant.
36161  *
36162  * Fork - LGPL
36163  * <script type="text/javascript">
36164  */
36165  
36166 /**
36167  * @class Roo.menu.MenuMgr
36168  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
36169  * @singleton
36170  */
36171 Roo.menu.MenuMgr = function(){
36172    var menus, active, groups = {}, attached = false, lastShow = new Date();
36173
36174    // private - called when first menu is created
36175    function init(){
36176        menus = {};
36177        active = new Roo.util.MixedCollection();
36178        Roo.get(document).addKeyListener(27, function(){
36179            if(active.length > 0){
36180                hideAll();
36181            }
36182        });
36183    }
36184
36185    // private
36186    function hideAll(){
36187        if(active && active.length > 0){
36188            var c = active.clone();
36189            c.each(function(m){
36190                m.hide();
36191            });
36192        }
36193    }
36194
36195    // private
36196    function onHide(m){
36197        active.remove(m);
36198        if(active.length < 1){
36199            Roo.get(document).un("mousedown", onMouseDown);
36200            attached = false;
36201        }
36202    }
36203
36204    // private
36205    function onShow(m){
36206        var last = active.last();
36207        lastShow = new Date();
36208        active.add(m);
36209        if(!attached){
36210            Roo.get(document).on("mousedown", onMouseDown);
36211            attached = true;
36212        }
36213        if(m.parentMenu){
36214           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
36215           m.parentMenu.activeChild = m;
36216        }else if(last && last.isVisible()){
36217           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
36218        }
36219    }
36220
36221    // private
36222    function onBeforeHide(m){
36223        if(m.activeChild){
36224            m.activeChild.hide();
36225        }
36226        if(m.autoHideTimer){
36227            clearTimeout(m.autoHideTimer);
36228            delete m.autoHideTimer;
36229        }
36230    }
36231
36232    // private
36233    function onBeforeShow(m){
36234        var pm = m.parentMenu;
36235        if(!pm && !m.allowOtherMenus){
36236            hideAll();
36237        }else if(pm && pm.activeChild && active != m){
36238            pm.activeChild.hide();
36239        }
36240    }
36241
36242    // private
36243    function onMouseDown(e){
36244        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36245            hideAll();
36246        }
36247    }
36248
36249    // private
36250    function onBeforeCheck(mi, state){
36251        if(state){
36252            var g = groups[mi.group];
36253            for(var i = 0, l = g.length; i < l; i++){
36254                if(g[i] != mi){
36255                    g[i].setChecked(false);
36256                }
36257            }
36258        }
36259    }
36260
36261    return {
36262
36263        /**
36264         * Hides all menus that are currently visible
36265         */
36266        hideAll : function(){
36267             hideAll();  
36268        },
36269
36270        // private
36271        register : function(menu){
36272            if(!menus){
36273                init();
36274            }
36275            menus[menu.id] = menu;
36276            menu.on("beforehide", onBeforeHide);
36277            menu.on("hide", onHide);
36278            menu.on("beforeshow", onBeforeShow);
36279            menu.on("show", onShow);
36280            var g = menu.group;
36281            if(g && menu.events["checkchange"]){
36282                if(!groups[g]){
36283                    groups[g] = [];
36284                }
36285                groups[g].push(menu);
36286                menu.on("checkchange", onCheck);
36287            }
36288        },
36289
36290         /**
36291          * Returns a {@link Roo.menu.Menu} object
36292          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36293          * be used to generate and return a new Menu instance.
36294          */
36295        get : function(menu){
36296            if(typeof menu == "string"){ // menu id
36297                return menus[menu];
36298            }else if(menu.events){  // menu instance
36299                return menu;
36300            }else if(typeof menu.length == 'number'){ // array of menu items?
36301                return new Roo.menu.Menu({items:menu});
36302            }else{ // otherwise, must be a config
36303                return new Roo.menu.Menu(menu);
36304            }
36305        },
36306
36307        // private
36308        unregister : function(menu){
36309            delete menus[menu.id];
36310            menu.un("beforehide", onBeforeHide);
36311            menu.un("hide", onHide);
36312            menu.un("beforeshow", onBeforeShow);
36313            menu.un("show", onShow);
36314            var g = menu.group;
36315            if(g && menu.events["checkchange"]){
36316                groups[g].remove(menu);
36317                menu.un("checkchange", onCheck);
36318            }
36319        },
36320
36321        // private
36322        registerCheckable : function(menuItem){
36323            var g = menuItem.group;
36324            if(g){
36325                if(!groups[g]){
36326                    groups[g] = [];
36327                }
36328                groups[g].push(menuItem);
36329                menuItem.on("beforecheckchange", onBeforeCheck);
36330            }
36331        },
36332
36333        // private
36334        unregisterCheckable : function(menuItem){
36335            var g = menuItem.group;
36336            if(g){
36337                groups[g].remove(menuItem);
36338                menuItem.un("beforecheckchange", onBeforeCheck);
36339            }
36340        }
36341    };
36342 }();/*
36343  * Based on:
36344  * Ext JS Library 1.1.1
36345  * Copyright(c) 2006-2007, Ext JS, LLC.
36346  *
36347  * Originally Released Under LGPL - original licence link has changed is not relivant.
36348  *
36349  * Fork - LGPL
36350  * <script type="text/javascript">
36351  */
36352  
36353
36354 /**
36355  * @class Roo.menu.BaseItem
36356  * @extends Roo.Component
36357  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36358  * management and base configuration options shared by all menu components.
36359  * @constructor
36360  * Creates a new BaseItem
36361  * @param {Object} config Configuration options
36362  */
36363 Roo.menu.BaseItem = function(config){
36364     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36365
36366     this.addEvents({
36367         /**
36368          * @event click
36369          * Fires when this item is clicked
36370          * @param {Roo.menu.BaseItem} this
36371          * @param {Roo.EventObject} e
36372          */
36373         click: true,
36374         /**
36375          * @event activate
36376          * Fires when this item is activated
36377          * @param {Roo.menu.BaseItem} this
36378          */
36379         activate : true,
36380         /**
36381          * @event deactivate
36382          * Fires when this item is deactivated
36383          * @param {Roo.menu.BaseItem} this
36384          */
36385         deactivate : true
36386     });
36387
36388     if(this.handler){
36389         this.on("click", this.handler, this.scope, true);
36390     }
36391 };
36392
36393 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36394     /**
36395      * @cfg {Function} handler
36396      * A function that will handle the click event of this menu item (defaults to undefined)
36397      */
36398     /**
36399      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36400      */
36401     canActivate : false,
36402     
36403      /**
36404      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36405      */
36406     hidden: false,
36407     
36408     /**
36409      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36410      */
36411     activeClass : "x-menu-item-active",
36412     /**
36413      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36414      */
36415     hideOnClick : true,
36416     /**
36417      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36418      */
36419     hideDelay : 100,
36420
36421     // private
36422     ctype: "Roo.menu.BaseItem",
36423
36424     // private
36425     actionMode : "container",
36426
36427     // private
36428     render : function(container, parentMenu){
36429         this.parentMenu = parentMenu;
36430         Roo.menu.BaseItem.superclass.render.call(this, container);
36431         this.container.menuItemId = this.id;
36432     },
36433
36434     // private
36435     onRender : function(container, position){
36436         this.el = Roo.get(this.el);
36437         container.dom.appendChild(this.el.dom);
36438     },
36439
36440     // private
36441     onClick : function(e){
36442         if(!this.disabled && this.fireEvent("click", this, e) !== false
36443                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36444             this.handleClick(e);
36445         }else{
36446             e.stopEvent();
36447         }
36448     },
36449
36450     // private
36451     activate : function(){
36452         if(this.disabled){
36453             return false;
36454         }
36455         var li = this.container;
36456         li.addClass(this.activeClass);
36457         this.region = li.getRegion().adjust(2, 2, -2, -2);
36458         this.fireEvent("activate", this);
36459         return true;
36460     },
36461
36462     // private
36463     deactivate : function(){
36464         this.container.removeClass(this.activeClass);
36465         this.fireEvent("deactivate", this);
36466     },
36467
36468     // private
36469     shouldDeactivate : function(e){
36470         return !this.region || !this.region.contains(e.getPoint());
36471     },
36472
36473     // private
36474     handleClick : function(e){
36475         if(this.hideOnClick){
36476             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36477         }
36478     },
36479
36480     // private
36481     expandMenu : function(autoActivate){
36482         // do nothing
36483     },
36484
36485     // private
36486     hideMenu : function(){
36487         // do nothing
36488     }
36489 });/*
36490  * Based on:
36491  * Ext JS Library 1.1.1
36492  * Copyright(c) 2006-2007, Ext JS, LLC.
36493  *
36494  * Originally Released Under LGPL - original licence link has changed is not relivant.
36495  *
36496  * Fork - LGPL
36497  * <script type="text/javascript">
36498  */
36499  
36500 /**
36501  * @class Roo.menu.Adapter
36502  * @extends Roo.menu.BaseItem
36503  * 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.
36504  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36505  * @constructor
36506  * Creates a new Adapter
36507  * @param {Object} config Configuration options
36508  */
36509 Roo.menu.Adapter = function(component, config){
36510     Roo.menu.Adapter.superclass.constructor.call(this, config);
36511     this.component = component;
36512 };
36513 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36514     // private
36515     canActivate : true,
36516
36517     // private
36518     onRender : function(container, position){
36519         this.component.render(container);
36520         this.el = this.component.getEl();
36521     },
36522
36523     // private
36524     activate : function(){
36525         if(this.disabled){
36526             return false;
36527         }
36528         this.component.focus();
36529         this.fireEvent("activate", this);
36530         return true;
36531     },
36532
36533     // private
36534     deactivate : function(){
36535         this.fireEvent("deactivate", this);
36536     },
36537
36538     // private
36539     disable : function(){
36540         this.component.disable();
36541         Roo.menu.Adapter.superclass.disable.call(this);
36542     },
36543
36544     // private
36545     enable : function(){
36546         this.component.enable();
36547         Roo.menu.Adapter.superclass.enable.call(this);
36548     }
36549 });/*
36550  * Based on:
36551  * Ext JS Library 1.1.1
36552  * Copyright(c) 2006-2007, Ext JS, LLC.
36553  *
36554  * Originally Released Under LGPL - original licence link has changed is not relivant.
36555  *
36556  * Fork - LGPL
36557  * <script type="text/javascript">
36558  */
36559
36560 /**
36561  * @class Roo.menu.TextItem
36562  * @extends Roo.menu.BaseItem
36563  * Adds a static text string to a menu, usually used as either a heading or group separator.
36564  * Note: old style constructor with text is still supported.
36565  * 
36566  * @constructor
36567  * Creates a new TextItem
36568  * @param {Object} cfg Configuration
36569  */
36570 Roo.menu.TextItem = function(cfg){
36571     if (typeof(cfg) == 'string') {
36572         this.text = cfg;
36573     } else {
36574         Roo.apply(this,cfg);
36575     }
36576     
36577     Roo.menu.TextItem.superclass.constructor.call(this);
36578 };
36579
36580 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36581     /**
36582      * @cfg {Boolean} text Text to show on item.
36583      */
36584     text : '',
36585     
36586     /**
36587      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36588      */
36589     hideOnClick : false,
36590     /**
36591      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36592      */
36593     itemCls : "x-menu-text",
36594
36595     // private
36596     onRender : function(){
36597         var s = document.createElement("span");
36598         s.className = this.itemCls;
36599         s.innerHTML = this.text;
36600         this.el = s;
36601         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36602     }
36603 });/*
36604  * Based on:
36605  * Ext JS Library 1.1.1
36606  * Copyright(c) 2006-2007, Ext JS, LLC.
36607  *
36608  * Originally Released Under LGPL - original licence link has changed is not relivant.
36609  *
36610  * Fork - LGPL
36611  * <script type="text/javascript">
36612  */
36613
36614 /**
36615  * @class Roo.menu.Separator
36616  * @extends Roo.menu.BaseItem
36617  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36618  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36619  * @constructor
36620  * @param {Object} config Configuration options
36621  */
36622 Roo.menu.Separator = function(config){
36623     Roo.menu.Separator.superclass.constructor.call(this, config);
36624 };
36625
36626 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36627     /**
36628      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36629      */
36630     itemCls : "x-menu-sep",
36631     /**
36632      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36633      */
36634     hideOnClick : false,
36635
36636     // private
36637     onRender : function(li){
36638         var s = document.createElement("span");
36639         s.className = this.itemCls;
36640         s.innerHTML = "&#160;";
36641         this.el = s;
36642         li.addClass("x-menu-sep-li");
36643         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36644     }
36645 });/*
36646  * Based on:
36647  * Ext JS Library 1.1.1
36648  * Copyright(c) 2006-2007, Ext JS, LLC.
36649  *
36650  * Originally Released Under LGPL - original licence link has changed is not relivant.
36651  *
36652  * Fork - LGPL
36653  * <script type="text/javascript">
36654  */
36655 /**
36656  * @class Roo.menu.Item
36657  * @extends Roo.menu.BaseItem
36658  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36659  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36660  * activation and click handling.
36661  * @constructor
36662  * Creates a new Item
36663  * @param {Object} config Configuration options
36664  */
36665 Roo.menu.Item = function(config){
36666     Roo.menu.Item.superclass.constructor.call(this, config);
36667     if(this.menu){
36668         this.menu = Roo.menu.MenuMgr.get(this.menu);
36669     }
36670 };
36671 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36672     
36673     /**
36674      * @cfg {String} text
36675      * The text to show on the menu item.
36676      */
36677     text: '',
36678      /**
36679      * @cfg {String} HTML to render in menu
36680      * The text to show on the menu item (HTML version).
36681      */
36682     html: '',
36683     /**
36684      * @cfg {String} icon
36685      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36686      */
36687     icon: undefined,
36688     /**
36689      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36690      */
36691     itemCls : "x-menu-item",
36692     /**
36693      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36694      */
36695     canActivate : true,
36696     /**
36697      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36698      */
36699     showDelay: 200,
36700     // doc'd in BaseItem
36701     hideDelay: 200,
36702
36703     // private
36704     ctype: "Roo.menu.Item",
36705     
36706     // private
36707     onRender : function(container, position){
36708         var el = document.createElement("a");
36709         el.hideFocus = true;
36710         el.unselectable = "on";
36711         el.href = this.href || "#";
36712         if(this.hrefTarget){
36713             el.target = this.hrefTarget;
36714         }
36715         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36716         
36717         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36718         
36719         el.innerHTML = String.format(
36720                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36721                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36722         this.el = el;
36723         Roo.menu.Item.superclass.onRender.call(this, container, position);
36724     },
36725
36726     /**
36727      * Sets the text to display in this menu item
36728      * @param {String} text The text to display
36729      * @param {Boolean} isHTML true to indicate text is pure html.
36730      */
36731     setText : function(text, isHTML){
36732         if (isHTML) {
36733             this.html = text;
36734         } else {
36735             this.text = text;
36736             this.html = '';
36737         }
36738         if(this.rendered){
36739             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36740      
36741             this.el.update(String.format(
36742                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36743                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36744             this.parentMenu.autoWidth();
36745         }
36746     },
36747
36748     // private
36749     handleClick : function(e){
36750         if(!this.href){ // if no link defined, stop the event automatically
36751             e.stopEvent();
36752         }
36753         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36754     },
36755
36756     // private
36757     activate : function(autoExpand){
36758         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36759             this.focus();
36760             if(autoExpand){
36761                 this.expandMenu();
36762             }
36763         }
36764         return true;
36765     },
36766
36767     // private
36768     shouldDeactivate : function(e){
36769         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36770             if(this.menu && this.menu.isVisible()){
36771                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36772             }
36773             return true;
36774         }
36775         return false;
36776     },
36777
36778     // private
36779     deactivate : function(){
36780         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36781         this.hideMenu();
36782     },
36783
36784     // private
36785     expandMenu : function(autoActivate){
36786         if(!this.disabled && this.menu){
36787             clearTimeout(this.hideTimer);
36788             delete this.hideTimer;
36789             if(!this.menu.isVisible() && !this.showTimer){
36790                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36791             }else if (this.menu.isVisible() && autoActivate){
36792                 this.menu.tryActivate(0, 1);
36793             }
36794         }
36795     },
36796
36797     // private
36798     deferExpand : function(autoActivate){
36799         delete this.showTimer;
36800         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36801         if(autoActivate){
36802             this.menu.tryActivate(0, 1);
36803         }
36804     },
36805
36806     // private
36807     hideMenu : function(){
36808         clearTimeout(this.showTimer);
36809         delete this.showTimer;
36810         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36811             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36812         }
36813     },
36814
36815     // private
36816     deferHide : function(){
36817         delete this.hideTimer;
36818         this.menu.hide();
36819     }
36820 });/*
36821  * Based on:
36822  * Ext JS Library 1.1.1
36823  * Copyright(c) 2006-2007, Ext JS, LLC.
36824  *
36825  * Originally Released Under LGPL - original licence link has changed is not relivant.
36826  *
36827  * Fork - LGPL
36828  * <script type="text/javascript">
36829  */
36830  
36831 /**
36832  * @class Roo.menu.CheckItem
36833  * @extends Roo.menu.Item
36834  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36835  * @constructor
36836  * Creates a new CheckItem
36837  * @param {Object} config Configuration options
36838  */
36839 Roo.menu.CheckItem = function(config){
36840     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36841     this.addEvents({
36842         /**
36843          * @event beforecheckchange
36844          * Fires before the checked value is set, providing an opportunity to cancel if needed
36845          * @param {Roo.menu.CheckItem} this
36846          * @param {Boolean} checked The new checked value that will be set
36847          */
36848         "beforecheckchange" : true,
36849         /**
36850          * @event checkchange
36851          * Fires after the checked value has been set
36852          * @param {Roo.menu.CheckItem} this
36853          * @param {Boolean} checked The checked value that was set
36854          */
36855         "checkchange" : true
36856     });
36857     if(this.checkHandler){
36858         this.on('checkchange', this.checkHandler, this.scope);
36859     }
36860 };
36861 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36862     /**
36863      * @cfg {String} group
36864      * All check items with the same group name will automatically be grouped into a single-select
36865      * radio button group (defaults to '')
36866      */
36867     /**
36868      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36869      */
36870     itemCls : "x-menu-item x-menu-check-item",
36871     /**
36872      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36873      */
36874     groupClass : "x-menu-group-item",
36875
36876     /**
36877      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36878      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36879      * initialized with checked = true will be rendered as checked.
36880      */
36881     checked: false,
36882
36883     // private
36884     ctype: "Roo.menu.CheckItem",
36885
36886     // private
36887     onRender : function(c){
36888         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36889         if(this.group){
36890             this.el.addClass(this.groupClass);
36891         }
36892         Roo.menu.MenuMgr.registerCheckable(this);
36893         if(this.checked){
36894             this.checked = false;
36895             this.setChecked(true, true);
36896         }
36897     },
36898
36899     // private
36900     destroy : function(){
36901         if(this.rendered){
36902             Roo.menu.MenuMgr.unregisterCheckable(this);
36903         }
36904         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36905     },
36906
36907     /**
36908      * Set the checked state of this item
36909      * @param {Boolean} checked The new checked value
36910      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36911      */
36912     setChecked : function(state, suppressEvent){
36913         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36914             if(this.container){
36915                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36916             }
36917             this.checked = state;
36918             if(suppressEvent !== true){
36919                 this.fireEvent("checkchange", this, state);
36920             }
36921         }
36922     },
36923
36924     // private
36925     handleClick : function(e){
36926        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36927            this.setChecked(!this.checked);
36928        }
36929        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36930     }
36931 });/*
36932  * Based on:
36933  * Ext JS Library 1.1.1
36934  * Copyright(c) 2006-2007, Ext JS, LLC.
36935  *
36936  * Originally Released Under LGPL - original licence link has changed is not relivant.
36937  *
36938  * Fork - LGPL
36939  * <script type="text/javascript">
36940  */
36941  
36942 /**
36943  * @class Roo.menu.DateItem
36944  * @extends Roo.menu.Adapter
36945  * A menu item that wraps the {@link Roo.DatPicker} component.
36946  * @constructor
36947  * Creates a new DateItem
36948  * @param {Object} config Configuration options
36949  */
36950 Roo.menu.DateItem = function(config){
36951     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36952     /** The Roo.DatePicker object @type Roo.DatePicker */
36953     this.picker = this.component;
36954     this.addEvents({select: true});
36955     
36956     this.picker.on("render", function(picker){
36957         picker.getEl().swallowEvent("click");
36958         picker.container.addClass("x-menu-date-item");
36959     });
36960
36961     this.picker.on("select", this.onSelect, this);
36962 };
36963
36964 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36965     // private
36966     onSelect : function(picker, date){
36967         this.fireEvent("select", this, date, picker);
36968         Roo.menu.DateItem.superclass.handleClick.call(this);
36969     }
36970 });/*
36971  * Based on:
36972  * Ext JS Library 1.1.1
36973  * Copyright(c) 2006-2007, Ext JS, LLC.
36974  *
36975  * Originally Released Under LGPL - original licence link has changed is not relivant.
36976  *
36977  * Fork - LGPL
36978  * <script type="text/javascript">
36979  */
36980  
36981 /**
36982  * @class Roo.menu.ColorItem
36983  * @extends Roo.menu.Adapter
36984  * A menu item that wraps the {@link Roo.ColorPalette} component.
36985  * @constructor
36986  * Creates a new ColorItem
36987  * @param {Object} config Configuration options
36988  */
36989 Roo.menu.ColorItem = function(config){
36990     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36991     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36992     this.palette = this.component;
36993     this.relayEvents(this.palette, ["select"]);
36994     if(this.selectHandler){
36995         this.on('select', this.selectHandler, this.scope);
36996     }
36997 };
36998 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36999  * Based on:
37000  * Ext JS Library 1.1.1
37001  * Copyright(c) 2006-2007, Ext JS, LLC.
37002  *
37003  * Originally Released Under LGPL - original licence link has changed is not relivant.
37004  *
37005  * Fork - LGPL
37006  * <script type="text/javascript">
37007  */
37008  
37009
37010 /**
37011  * @class Roo.menu.DateMenu
37012  * @extends Roo.menu.Menu
37013  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
37014  * @constructor
37015  * Creates a new DateMenu
37016  * @param {Object} config Configuration options
37017  */
37018 Roo.menu.DateMenu = function(config){
37019     Roo.menu.DateMenu.superclass.constructor.call(this, config);
37020     this.plain = true;
37021     var di = new Roo.menu.DateItem(config);
37022     this.add(di);
37023     /**
37024      * The {@link Roo.DatePicker} instance for this DateMenu
37025      * @type DatePicker
37026      */
37027     this.picker = di.picker;
37028     /**
37029      * @event select
37030      * @param {DatePicker} picker
37031      * @param {Date} date
37032      */
37033     this.relayEvents(di, ["select"]);
37034     this.on('beforeshow', function(){
37035         if(this.picker){
37036             this.picker.hideMonthPicker(false);
37037         }
37038     }, this);
37039 };
37040 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
37041     cls:'x-date-menu'
37042 });/*
37043  * Based on:
37044  * Ext JS Library 1.1.1
37045  * Copyright(c) 2006-2007, Ext JS, LLC.
37046  *
37047  * Originally Released Under LGPL - original licence link has changed is not relivant.
37048  *
37049  * Fork - LGPL
37050  * <script type="text/javascript">
37051  */
37052  
37053
37054 /**
37055  * @class Roo.menu.ColorMenu
37056  * @extends Roo.menu.Menu
37057  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
37058  * @constructor
37059  * Creates a new ColorMenu
37060  * @param {Object} config Configuration options
37061  */
37062 Roo.menu.ColorMenu = function(config){
37063     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
37064     this.plain = true;
37065     var ci = new Roo.menu.ColorItem(config);
37066     this.add(ci);
37067     /**
37068      * The {@link Roo.ColorPalette} instance for this ColorMenu
37069      * @type ColorPalette
37070      */
37071     this.palette = ci.palette;
37072     /**
37073      * @event select
37074      * @param {ColorPalette} palette
37075      * @param {String} color
37076      */
37077     this.relayEvents(ci, ["select"]);
37078 };
37079 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
37080  * Based on:
37081  * Ext JS Library 1.1.1
37082  * Copyright(c) 2006-2007, Ext JS, LLC.
37083  *
37084  * Originally Released Under LGPL - original licence link has changed is not relivant.
37085  *
37086  * Fork - LGPL
37087  * <script type="text/javascript">
37088  */
37089  
37090 /**
37091  * @class Roo.form.Field
37092  * @extends Roo.BoxComponent
37093  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
37094  * @constructor
37095  * Creates a new Field
37096  * @param {Object} config Configuration options
37097  */
37098 Roo.form.Field = function(config){
37099     Roo.form.Field.superclass.constructor.call(this, config);
37100 };
37101
37102 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
37103     /**
37104      * @cfg {String} fieldLabel Label to use when rendering a form.
37105      */
37106        /**
37107      * @cfg {String} qtip Mouse over tip
37108      */
37109      
37110     /**
37111      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
37112      */
37113     invalidClass : "x-form-invalid",
37114     /**
37115      * @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")
37116      */
37117     invalidText : "The value in this field is invalid",
37118     /**
37119      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
37120      */
37121     focusClass : "x-form-focus",
37122     /**
37123      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
37124       automatic validation (defaults to "keyup").
37125      */
37126     validationEvent : "keyup",
37127     /**
37128      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
37129      */
37130     validateOnBlur : true,
37131     /**
37132      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
37133      */
37134     validationDelay : 250,
37135     /**
37136      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37137      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
37138      */
37139     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
37140     /**
37141      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
37142      */
37143     fieldClass : "x-form-field",
37144     /**
37145      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
37146      *<pre>
37147 Value         Description
37148 -----------   ----------------------------------------------------------------------
37149 qtip          Display a quick tip when the user hovers over the field
37150 title         Display a default browser title attribute popup
37151 under         Add a block div beneath the field containing the error text
37152 side          Add an error icon to the right of the field with a popup on hover
37153 [element id]  Add the error text directly to the innerHTML of the specified element
37154 </pre>
37155      */
37156     msgTarget : 'qtip',
37157     /**
37158      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
37159      */
37160     msgFx : 'normal',
37161
37162     /**
37163      * @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.
37164      */
37165     readOnly : false,
37166
37167     /**
37168      * @cfg {Boolean} disabled True to disable the field (defaults to false).
37169      */
37170     disabled : false,
37171
37172     /**
37173      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
37174      */
37175     inputType : undefined,
37176     
37177     /**
37178      * @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).
37179          */
37180         tabIndex : undefined,
37181         
37182     // private
37183     isFormField : true,
37184
37185     // private
37186     hasFocus : false,
37187     /**
37188      * @property {Roo.Element} fieldEl
37189      * Element Containing the rendered Field (with label etc.)
37190      */
37191     /**
37192      * @cfg {Mixed} value A value to initialize this field with.
37193      */
37194     value : undefined,
37195
37196     /**
37197      * @cfg {String} name The field's HTML name attribute.
37198      */
37199     /**
37200      * @cfg {String} cls A CSS class to apply to the field's underlying element.
37201      */
37202     // private
37203     loadedValue : false,
37204      
37205      
37206         // private ??
37207         initComponent : function(){
37208         Roo.form.Field.superclass.initComponent.call(this);
37209         this.addEvents({
37210             /**
37211              * @event focus
37212              * Fires when this field receives input focus.
37213              * @param {Roo.form.Field} this
37214              */
37215             focus : true,
37216             /**
37217              * @event blur
37218              * Fires when this field loses input focus.
37219              * @param {Roo.form.Field} this
37220              */
37221             blur : true,
37222             /**
37223              * @event specialkey
37224              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
37225              * {@link Roo.EventObject#getKey} to determine which key was pressed.
37226              * @param {Roo.form.Field} this
37227              * @param {Roo.EventObject} e The event object
37228              */
37229             specialkey : true,
37230             /**
37231              * @event change
37232              * Fires just before the field blurs if the field value has changed.
37233              * @param {Roo.form.Field} this
37234              * @param {Mixed} newValue The new value
37235              * @param {Mixed} oldValue The original value
37236              */
37237             change : true,
37238             /**
37239              * @event invalid
37240              * Fires after the field has been marked as invalid.
37241              * @param {Roo.form.Field} this
37242              * @param {String} msg The validation message
37243              */
37244             invalid : true,
37245             /**
37246              * @event valid
37247              * Fires after the field has been validated with no errors.
37248              * @param {Roo.form.Field} this
37249              */
37250             valid : true,
37251              /**
37252              * @event keyup
37253              * Fires after the key up
37254              * @param {Roo.form.Field} this
37255              * @param {Roo.EventObject}  e The event Object
37256              */
37257             keyup : true
37258         });
37259     },
37260
37261     /**
37262      * Returns the name attribute of the field if available
37263      * @return {String} name The field name
37264      */
37265     getName: function(){
37266          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37267     },
37268
37269     // private
37270     onRender : function(ct, position){
37271         Roo.form.Field.superclass.onRender.call(this, ct, position);
37272         if(!this.el){
37273             var cfg = this.getAutoCreate();
37274             if(!cfg.name){
37275                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37276             }
37277             if (!cfg.name.length) {
37278                 delete cfg.name;
37279             }
37280             if(this.inputType){
37281                 cfg.type = this.inputType;
37282             }
37283             this.el = ct.createChild(cfg, position);
37284         }
37285         var type = this.el.dom.type;
37286         if(type){
37287             if(type == 'password'){
37288                 type = 'text';
37289             }
37290             this.el.addClass('x-form-'+type);
37291         }
37292         if(this.readOnly){
37293             this.el.dom.readOnly = true;
37294         }
37295         if(this.tabIndex !== undefined){
37296             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37297         }
37298
37299         this.el.addClass([this.fieldClass, this.cls]);
37300         this.initValue();
37301     },
37302
37303     /**
37304      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37305      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37306      * @return {Roo.form.Field} this
37307      */
37308     applyTo : function(target){
37309         this.allowDomMove = false;
37310         this.el = Roo.get(target);
37311         this.render(this.el.dom.parentNode);
37312         return this;
37313     },
37314
37315     // private
37316     initValue : function(){
37317         if(this.value !== undefined){
37318             this.setValue(this.value);
37319         }else if(this.el.dom.value.length > 0){
37320             this.setValue(this.el.dom.value);
37321         }
37322     },
37323
37324     /**
37325      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37326      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
37327      */
37328     isDirty : function() {
37329         if(this.disabled) {
37330             return false;
37331         }
37332         return String(this.getValue()) !== String(this.originalValue);
37333     },
37334
37335     /**
37336      * stores the current value in loadedValue
37337      */
37338     resetHasChanged : function()
37339     {
37340         this.loadedValue = String(this.getValue());
37341     },
37342     /**
37343      * checks the current value against the 'loaded' value.
37344      * Note - will return false if 'resetHasChanged' has not been called first.
37345      */
37346     hasChanged : function()
37347     {
37348         if(this.disabled || this.readOnly) {
37349             return false;
37350         }
37351         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
37352     },
37353     
37354     
37355     
37356     // private
37357     afterRender : function(){
37358         Roo.form.Field.superclass.afterRender.call(this);
37359         this.initEvents();
37360     },
37361
37362     // private
37363     fireKey : function(e){
37364         //Roo.log('field ' + e.getKey());
37365         if(e.isNavKeyPress()){
37366             this.fireEvent("specialkey", this, e);
37367         }
37368     },
37369
37370     /**
37371      * Resets the current field value to the originally loaded value and clears any validation messages
37372      */
37373     reset : function(){
37374         this.setValue(this.resetValue);
37375         this.clearInvalid();
37376     },
37377
37378     // private
37379     initEvents : function(){
37380         // safari killled keypress - so keydown is now used..
37381         this.el.on("keydown" , this.fireKey,  this);
37382         this.el.on("focus", this.onFocus,  this);
37383         this.el.on("blur", this.onBlur,  this);
37384         this.el.relayEvent('keyup', this);
37385
37386         // reference to original value for reset
37387         this.originalValue = this.getValue();
37388         this.resetValue =  this.getValue();
37389     },
37390
37391     // private
37392     onFocus : function(){
37393         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37394             this.el.addClass(this.focusClass);
37395         }
37396         if(!this.hasFocus){
37397             this.hasFocus = true;
37398             this.startValue = this.getValue();
37399             this.fireEvent("focus", this);
37400         }
37401     },
37402
37403     beforeBlur : Roo.emptyFn,
37404
37405     // private
37406     onBlur : function(){
37407         this.beforeBlur();
37408         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37409             this.el.removeClass(this.focusClass);
37410         }
37411         this.hasFocus = false;
37412         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37413             this.validate();
37414         }
37415         var v = this.getValue();
37416         if(String(v) !== String(this.startValue)){
37417             this.fireEvent('change', this, v, this.startValue);
37418         }
37419         this.fireEvent("blur", this);
37420     },
37421
37422     /**
37423      * Returns whether or not the field value is currently valid
37424      * @param {Boolean} preventMark True to disable marking the field invalid
37425      * @return {Boolean} True if the value is valid, else false
37426      */
37427     isValid : function(preventMark){
37428         if(this.disabled){
37429             return true;
37430         }
37431         var restore = this.preventMark;
37432         this.preventMark = preventMark === true;
37433         var v = this.validateValue(this.processValue(this.getRawValue()));
37434         this.preventMark = restore;
37435         return v;
37436     },
37437
37438     /**
37439      * Validates the field value
37440      * @return {Boolean} True if the value is valid, else false
37441      */
37442     validate : function(){
37443         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37444             this.clearInvalid();
37445             return true;
37446         }
37447         return false;
37448     },
37449
37450     processValue : function(value){
37451         return value;
37452     },
37453
37454     // private
37455     // Subclasses should provide the validation implementation by overriding this
37456     validateValue : function(value){
37457         return true;
37458     },
37459
37460     /**
37461      * Mark this field as invalid
37462      * @param {String} msg The validation message
37463      */
37464     markInvalid : function(msg){
37465         if(!this.rendered || this.preventMark){ // not rendered
37466             return;
37467         }
37468         
37469         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37470         
37471         obj.el.addClass(this.invalidClass);
37472         msg = msg || this.invalidText;
37473         switch(this.msgTarget){
37474             case 'qtip':
37475                 obj.el.dom.qtip = msg;
37476                 obj.el.dom.qclass = 'x-form-invalid-tip';
37477                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37478                     Roo.QuickTips.enable();
37479                 }
37480                 break;
37481             case 'title':
37482                 this.el.dom.title = msg;
37483                 break;
37484             case 'under':
37485                 if(!this.errorEl){
37486                     var elp = this.el.findParent('.x-form-element', 5, true);
37487                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37488                     this.errorEl.setWidth(elp.getWidth(true)-20);
37489                 }
37490                 this.errorEl.update(msg);
37491                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37492                 break;
37493             case 'side':
37494                 if(!this.errorIcon){
37495                     var elp = this.el.findParent('.x-form-element', 5, true);
37496                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37497                 }
37498                 this.alignErrorIcon();
37499                 this.errorIcon.dom.qtip = msg;
37500                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37501                 this.errorIcon.show();
37502                 this.on('resize', this.alignErrorIcon, this);
37503                 break;
37504             default:
37505                 var t = Roo.getDom(this.msgTarget);
37506                 t.innerHTML = msg;
37507                 t.style.display = this.msgDisplay;
37508                 break;
37509         }
37510         this.fireEvent('invalid', this, msg);
37511     },
37512
37513     // private
37514     alignErrorIcon : function(){
37515         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37516     },
37517
37518     /**
37519      * Clear any invalid styles/messages for this field
37520      */
37521     clearInvalid : function(){
37522         if(!this.rendered || this.preventMark){ // not rendered
37523             return;
37524         }
37525         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37526         
37527         obj.el.removeClass(this.invalidClass);
37528         switch(this.msgTarget){
37529             case 'qtip':
37530                 obj.el.dom.qtip = '';
37531                 break;
37532             case 'title':
37533                 this.el.dom.title = '';
37534                 break;
37535             case 'under':
37536                 if(this.errorEl){
37537                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37538                 }
37539                 break;
37540             case 'side':
37541                 if(this.errorIcon){
37542                     this.errorIcon.dom.qtip = '';
37543                     this.errorIcon.hide();
37544                     this.un('resize', this.alignErrorIcon, this);
37545                 }
37546                 break;
37547             default:
37548                 var t = Roo.getDom(this.msgTarget);
37549                 t.innerHTML = '';
37550                 t.style.display = 'none';
37551                 break;
37552         }
37553         this.fireEvent('valid', this);
37554     },
37555
37556     /**
37557      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37558      * @return {Mixed} value The field value
37559      */
37560     getRawValue : function(){
37561         var v = this.el.getValue();
37562         
37563         return v;
37564     },
37565
37566     /**
37567      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37568      * @return {Mixed} value The field value
37569      */
37570     getValue : function(){
37571         var v = this.el.getValue();
37572          
37573         return v;
37574     },
37575
37576     /**
37577      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37578      * @param {Mixed} value The value to set
37579      */
37580     setRawValue : function(v){
37581         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37582     },
37583
37584     /**
37585      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37586      * @param {Mixed} value The value to set
37587      */
37588     setValue : function(v){
37589         this.value = v;
37590         if(this.rendered){
37591             this.el.dom.value = (v === null || v === undefined ? '' : v);
37592              this.validate();
37593         }
37594     },
37595
37596     adjustSize : function(w, h){
37597         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37598         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37599         return s;
37600     },
37601
37602     adjustWidth : function(tag, w){
37603         tag = tag.toLowerCase();
37604         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37605             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37606                 if(tag == 'input'){
37607                     return w + 2;
37608                 }
37609                 if(tag == 'textarea'){
37610                     return w-2;
37611                 }
37612             }else if(Roo.isOpera){
37613                 if(tag == 'input'){
37614                     return w + 2;
37615                 }
37616                 if(tag == 'textarea'){
37617                     return w-2;
37618                 }
37619             }
37620         }
37621         return w;
37622     }
37623 });
37624
37625
37626 // anything other than normal should be considered experimental
37627 Roo.form.Field.msgFx = {
37628     normal : {
37629         show: function(msgEl, f){
37630             msgEl.setDisplayed('block');
37631         },
37632
37633         hide : function(msgEl, f){
37634             msgEl.setDisplayed(false).update('');
37635         }
37636     },
37637
37638     slide : {
37639         show: function(msgEl, f){
37640             msgEl.slideIn('t', {stopFx:true});
37641         },
37642
37643         hide : function(msgEl, f){
37644             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37645         }
37646     },
37647
37648     slideRight : {
37649         show: function(msgEl, f){
37650             msgEl.fixDisplay();
37651             msgEl.alignTo(f.el, 'tl-tr');
37652             msgEl.slideIn('l', {stopFx:true});
37653         },
37654
37655         hide : function(msgEl, f){
37656             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37657         }
37658     }
37659 };/*
37660  * Based on:
37661  * Ext JS Library 1.1.1
37662  * Copyright(c) 2006-2007, Ext JS, LLC.
37663  *
37664  * Originally Released Under LGPL - original licence link has changed is not relivant.
37665  *
37666  * Fork - LGPL
37667  * <script type="text/javascript">
37668  */
37669  
37670
37671 /**
37672  * @class Roo.form.TextField
37673  * @extends Roo.form.Field
37674  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37675  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37676  * @constructor
37677  * Creates a new TextField
37678  * @param {Object} config Configuration options
37679  */
37680 Roo.form.TextField = function(config){
37681     Roo.form.TextField.superclass.constructor.call(this, config);
37682     this.addEvents({
37683         /**
37684          * @event autosize
37685          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37686          * according to the default logic, but this event provides a hook for the developer to apply additional
37687          * logic at runtime to resize the field if needed.
37688              * @param {Roo.form.Field} this This text field
37689              * @param {Number} width The new field width
37690              */
37691         autosize : true
37692     });
37693 };
37694
37695 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37696     /**
37697      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37698      */
37699     grow : false,
37700     /**
37701      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37702      */
37703     growMin : 30,
37704     /**
37705      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37706      */
37707     growMax : 800,
37708     /**
37709      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37710      */
37711     vtype : null,
37712     /**
37713      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37714      */
37715     maskRe : null,
37716     /**
37717      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37718      */
37719     disableKeyFilter : false,
37720     /**
37721      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37722      */
37723     allowBlank : true,
37724     /**
37725      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37726      */
37727     minLength : 0,
37728     /**
37729      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37730      */
37731     maxLength : Number.MAX_VALUE,
37732     /**
37733      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37734      */
37735     minLengthText : "The minimum length for this field is {0}",
37736     /**
37737      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37738      */
37739     maxLengthText : "The maximum length for this field is {0}",
37740     /**
37741      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37742      */
37743     selectOnFocus : false,
37744     /**
37745      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37746      */
37747     blankText : "This field is required",
37748     /**
37749      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37750      * If available, this function will be called only after the basic validators all return true, and will be passed the
37751      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37752      */
37753     validator : null,
37754     /**
37755      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37756      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37757      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37758      */
37759     regex : null,
37760     /**
37761      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37762      */
37763     regexText : "",
37764     /**
37765      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37766      */
37767     emptyText : null,
37768    
37769
37770     // private
37771     initEvents : function()
37772     {
37773         if (this.emptyText) {
37774             this.el.attr('placeholder', this.emptyText);
37775         }
37776         
37777         Roo.form.TextField.superclass.initEvents.call(this);
37778         if(this.validationEvent == 'keyup'){
37779             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37780             this.el.on('keyup', this.filterValidation, this);
37781         }
37782         else if(this.validationEvent !== false){
37783             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37784         }
37785         
37786         if(this.selectOnFocus){
37787             this.on("focus", this.preFocus, this);
37788             
37789         }
37790         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37791             this.el.on("keypress", this.filterKeys, this);
37792         }
37793         if(this.grow){
37794             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37795             this.el.on("click", this.autoSize,  this);
37796         }
37797         if(this.el.is('input[type=password]') && Roo.isSafari){
37798             this.el.on('keydown', this.SafariOnKeyDown, this);
37799         }
37800     },
37801
37802     processValue : function(value){
37803         if(this.stripCharsRe){
37804             var newValue = value.replace(this.stripCharsRe, '');
37805             if(newValue !== value){
37806                 this.setRawValue(newValue);
37807                 return newValue;
37808             }
37809         }
37810         return value;
37811     },
37812
37813     filterValidation : function(e){
37814         if(!e.isNavKeyPress()){
37815             this.validationTask.delay(this.validationDelay);
37816         }
37817     },
37818
37819     // private
37820     onKeyUp : function(e){
37821         if(!e.isNavKeyPress()){
37822             this.autoSize();
37823         }
37824     },
37825
37826     /**
37827      * Resets the current field value to the originally-loaded value and clears any validation messages.
37828      *  
37829      */
37830     reset : function(){
37831         Roo.form.TextField.superclass.reset.call(this);
37832        
37833     },
37834
37835     
37836     // private
37837     preFocus : function(){
37838         
37839         if(this.selectOnFocus){
37840             this.el.dom.select();
37841         }
37842     },
37843
37844     
37845     // private
37846     filterKeys : function(e){
37847         var k = e.getKey();
37848         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37849             return;
37850         }
37851         var c = e.getCharCode(), cc = String.fromCharCode(c);
37852         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37853             return;
37854         }
37855         if(!this.maskRe.test(cc)){
37856             e.stopEvent();
37857         }
37858     },
37859
37860     setValue : function(v){
37861         
37862         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37863         
37864         this.autoSize();
37865     },
37866
37867     /**
37868      * Validates a value according to the field's validation rules and marks the field as invalid
37869      * if the validation fails
37870      * @param {Mixed} value The value to validate
37871      * @return {Boolean} True if the value is valid, else false
37872      */
37873     validateValue : function(value){
37874         if(value.length < 1)  { // if it's blank
37875              if(this.allowBlank){
37876                 this.clearInvalid();
37877                 return true;
37878              }else{
37879                 this.markInvalid(this.blankText);
37880                 return false;
37881              }
37882         }
37883         if(value.length < this.minLength){
37884             this.markInvalid(String.format(this.minLengthText, this.minLength));
37885             return false;
37886         }
37887         if(value.length > this.maxLength){
37888             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37889             return false;
37890         }
37891         if(this.vtype){
37892             var vt = Roo.form.VTypes;
37893             if(!vt[this.vtype](value, this)){
37894                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37895                 return false;
37896             }
37897         }
37898         if(typeof this.validator == "function"){
37899             var msg = this.validator(value);
37900             if(msg !== true){
37901                 this.markInvalid(msg);
37902                 return false;
37903             }
37904         }
37905         if(this.regex && !this.regex.test(value)){
37906             this.markInvalid(this.regexText);
37907             return false;
37908         }
37909         return true;
37910     },
37911
37912     /**
37913      * Selects text in this field
37914      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37915      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37916      */
37917     selectText : function(start, end){
37918         var v = this.getRawValue();
37919         if(v.length > 0){
37920             start = start === undefined ? 0 : start;
37921             end = end === undefined ? v.length : end;
37922             var d = this.el.dom;
37923             if(d.setSelectionRange){
37924                 d.setSelectionRange(start, end);
37925             }else if(d.createTextRange){
37926                 var range = d.createTextRange();
37927                 range.moveStart("character", start);
37928                 range.moveEnd("character", v.length-end);
37929                 range.select();
37930             }
37931         }
37932     },
37933
37934     /**
37935      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37936      * This only takes effect if grow = true, and fires the autosize event.
37937      */
37938     autoSize : function(){
37939         if(!this.grow || !this.rendered){
37940             return;
37941         }
37942         if(!this.metrics){
37943             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37944         }
37945         var el = this.el;
37946         var v = el.dom.value;
37947         var d = document.createElement('div');
37948         d.appendChild(document.createTextNode(v));
37949         v = d.innerHTML;
37950         d = null;
37951         v += "&#160;";
37952         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37953         this.el.setWidth(w);
37954         this.fireEvent("autosize", this, w);
37955     },
37956     
37957     // private
37958     SafariOnKeyDown : function(event)
37959     {
37960         // this is a workaround for a password hang bug on chrome/ webkit.
37961         
37962         var isSelectAll = false;
37963         
37964         if(this.el.dom.selectionEnd > 0){
37965             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37966         }
37967         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37968             event.preventDefault();
37969             this.setValue('');
37970             return;
37971         }
37972         
37973         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
37974             
37975             event.preventDefault();
37976             // this is very hacky as keydown always get's upper case.
37977             
37978             var cc = String.fromCharCode(event.getCharCode());
37979             
37980             
37981             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37982             
37983         }
37984         
37985         
37986     }
37987 });/*
37988  * Based on:
37989  * Ext JS Library 1.1.1
37990  * Copyright(c) 2006-2007, Ext JS, LLC.
37991  *
37992  * Originally Released Under LGPL - original licence link has changed is not relivant.
37993  *
37994  * Fork - LGPL
37995  * <script type="text/javascript">
37996  */
37997  
37998 /**
37999  * @class Roo.form.Hidden
38000  * @extends Roo.form.TextField
38001  * Simple Hidden element used on forms 
38002  * 
38003  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
38004  * 
38005  * @constructor
38006  * Creates a new Hidden form element.
38007  * @param {Object} config Configuration options
38008  */
38009
38010
38011
38012 // easy hidden field...
38013 Roo.form.Hidden = function(config){
38014     Roo.form.Hidden.superclass.constructor.call(this, config);
38015 };
38016   
38017 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
38018     fieldLabel:      '',
38019     inputType:      'hidden',
38020     width:          50,
38021     allowBlank:     true,
38022     labelSeparator: '',
38023     hidden:         true,
38024     itemCls :       'x-form-item-display-none'
38025
38026
38027 });
38028
38029
38030 /*
38031  * Based on:
38032  * Ext JS Library 1.1.1
38033  * Copyright(c) 2006-2007, Ext JS, LLC.
38034  *
38035  * Originally Released Under LGPL - original licence link has changed is not relivant.
38036  *
38037  * Fork - LGPL
38038  * <script type="text/javascript">
38039  */
38040  
38041 /**
38042  * @class Roo.form.TriggerField
38043  * @extends Roo.form.TextField
38044  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
38045  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
38046  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
38047  * for which you can provide a custom implementation.  For example:
38048  * <pre><code>
38049 var trigger = new Roo.form.TriggerField();
38050 trigger.onTriggerClick = myTriggerFn;
38051 trigger.applyTo('my-field');
38052 </code></pre>
38053  *
38054  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
38055  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
38056  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
38057  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
38058  * @constructor
38059  * Create a new TriggerField.
38060  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
38061  * to the base TextField)
38062  */
38063 Roo.form.TriggerField = function(config){
38064     this.mimicing = false;
38065     Roo.form.TriggerField.superclass.constructor.call(this, config);
38066 };
38067
38068 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
38069     /**
38070      * @cfg {String} triggerClass A CSS class to apply to the trigger
38071      */
38072     /**
38073      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38074      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
38075      */
38076     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
38077     /**
38078      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
38079      */
38080     hideTrigger:false,
38081
38082     /** @cfg {Boolean} grow @hide */
38083     /** @cfg {Number} growMin @hide */
38084     /** @cfg {Number} growMax @hide */
38085
38086     /**
38087      * @hide 
38088      * @method
38089      */
38090     autoSize: Roo.emptyFn,
38091     // private
38092     monitorTab : true,
38093     // private
38094     deferHeight : true,
38095
38096     
38097     actionMode : 'wrap',
38098     // private
38099     onResize : function(w, h){
38100         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
38101         if(typeof w == 'number'){
38102             var x = w - this.trigger.getWidth();
38103             this.el.setWidth(this.adjustWidth('input', x));
38104             this.trigger.setStyle('left', x+'px');
38105         }
38106     },
38107
38108     // private
38109     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38110
38111     // private
38112     getResizeEl : function(){
38113         return this.wrap;
38114     },
38115
38116     // private
38117     getPositionEl : function(){
38118         return this.wrap;
38119     },
38120
38121     // private
38122     alignErrorIcon : function(){
38123         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
38124     },
38125
38126     // private
38127     onRender : function(ct, position){
38128         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
38129         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
38130         this.trigger = this.wrap.createChild(this.triggerConfig ||
38131                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
38132         if(this.hideTrigger){
38133             this.trigger.setDisplayed(false);
38134         }
38135         this.initTrigger();
38136         if(!this.width){
38137             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
38138         }
38139     },
38140
38141     // private
38142     initTrigger : function(){
38143         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
38144         this.trigger.addClassOnOver('x-form-trigger-over');
38145         this.trigger.addClassOnClick('x-form-trigger-click');
38146     },
38147
38148     // private
38149     onDestroy : function(){
38150         if(this.trigger){
38151             this.trigger.removeAllListeners();
38152             this.trigger.remove();
38153         }
38154         if(this.wrap){
38155             this.wrap.remove();
38156         }
38157         Roo.form.TriggerField.superclass.onDestroy.call(this);
38158     },
38159
38160     // private
38161     onFocus : function(){
38162         Roo.form.TriggerField.superclass.onFocus.call(this);
38163         if(!this.mimicing){
38164             this.wrap.addClass('x-trigger-wrap-focus');
38165             this.mimicing = true;
38166             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
38167             if(this.monitorTab){
38168                 this.el.on("keydown", this.checkTab, this);
38169             }
38170         }
38171     },
38172
38173     // private
38174     checkTab : function(e){
38175         if(e.getKey() == e.TAB){
38176             this.triggerBlur();
38177         }
38178     },
38179
38180     // private
38181     onBlur : function(){
38182         // do nothing
38183     },
38184
38185     // private
38186     mimicBlur : function(e, t){
38187         if(!this.wrap.contains(t) && this.validateBlur()){
38188             this.triggerBlur();
38189         }
38190     },
38191
38192     // private
38193     triggerBlur : function(){
38194         this.mimicing = false;
38195         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
38196         if(this.monitorTab){
38197             this.el.un("keydown", this.checkTab, this);
38198         }
38199         this.wrap.removeClass('x-trigger-wrap-focus');
38200         Roo.form.TriggerField.superclass.onBlur.call(this);
38201     },
38202
38203     // private
38204     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
38205     validateBlur : function(e, t){
38206         return true;
38207     },
38208
38209     // private
38210     onDisable : function(){
38211         Roo.form.TriggerField.superclass.onDisable.call(this);
38212         if(this.wrap){
38213             this.wrap.addClass('x-item-disabled');
38214         }
38215     },
38216
38217     // private
38218     onEnable : function(){
38219         Roo.form.TriggerField.superclass.onEnable.call(this);
38220         if(this.wrap){
38221             this.wrap.removeClass('x-item-disabled');
38222         }
38223     },
38224
38225     // private
38226     onShow : function(){
38227         var ae = this.getActionEl();
38228         
38229         if(ae){
38230             ae.dom.style.display = '';
38231             ae.dom.style.visibility = 'visible';
38232         }
38233     },
38234
38235     // private
38236     
38237     onHide : function(){
38238         var ae = this.getActionEl();
38239         ae.dom.style.display = 'none';
38240     },
38241
38242     /**
38243      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
38244      * by an implementing function.
38245      * @method
38246      * @param {EventObject} e
38247      */
38248     onTriggerClick : Roo.emptyFn
38249 });
38250
38251 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
38252 // to be extended by an implementing class.  For an example of implementing this class, see the custom
38253 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
38254 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
38255     initComponent : function(){
38256         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
38257
38258         this.triggerConfig = {
38259             tag:'span', cls:'x-form-twin-triggers', cn:[
38260             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
38261             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
38262         ]};
38263     },
38264
38265     getTrigger : function(index){
38266         return this.triggers[index];
38267     },
38268
38269     initTrigger : function(){
38270         var ts = this.trigger.select('.x-form-trigger', true);
38271         this.wrap.setStyle('overflow', 'hidden');
38272         var triggerField = this;
38273         ts.each(function(t, all, index){
38274             t.hide = function(){
38275                 var w = triggerField.wrap.getWidth();
38276                 this.dom.style.display = 'none';
38277                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38278             };
38279             t.show = function(){
38280                 var w = triggerField.wrap.getWidth();
38281                 this.dom.style.display = '';
38282                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38283             };
38284             var triggerIndex = 'Trigger'+(index+1);
38285
38286             if(this['hide'+triggerIndex]){
38287                 t.dom.style.display = 'none';
38288             }
38289             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38290             t.addClassOnOver('x-form-trigger-over');
38291             t.addClassOnClick('x-form-trigger-click');
38292         }, this);
38293         this.triggers = ts.elements;
38294     },
38295
38296     onTrigger1Click : Roo.emptyFn,
38297     onTrigger2Click : Roo.emptyFn
38298 });/*
38299  * Based on:
38300  * Ext JS Library 1.1.1
38301  * Copyright(c) 2006-2007, Ext JS, LLC.
38302  *
38303  * Originally Released Under LGPL - original licence link has changed is not relivant.
38304  *
38305  * Fork - LGPL
38306  * <script type="text/javascript">
38307  */
38308  
38309 /**
38310  * @class Roo.form.TextArea
38311  * @extends Roo.form.TextField
38312  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38313  * support for auto-sizing.
38314  * @constructor
38315  * Creates a new TextArea
38316  * @param {Object} config Configuration options
38317  */
38318 Roo.form.TextArea = function(config){
38319     Roo.form.TextArea.superclass.constructor.call(this, config);
38320     // these are provided exchanges for backwards compat
38321     // minHeight/maxHeight were replaced by growMin/growMax to be
38322     // compatible with TextField growing config values
38323     if(this.minHeight !== undefined){
38324         this.growMin = this.minHeight;
38325     }
38326     if(this.maxHeight !== undefined){
38327         this.growMax = this.maxHeight;
38328     }
38329 };
38330
38331 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38332     /**
38333      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38334      */
38335     growMin : 60,
38336     /**
38337      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38338      */
38339     growMax: 1000,
38340     /**
38341      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38342      * in the field (equivalent to setting overflow: hidden, defaults to false)
38343      */
38344     preventScrollbars: false,
38345     /**
38346      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38347      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38348      */
38349
38350     // private
38351     onRender : function(ct, position){
38352         if(!this.el){
38353             this.defaultAutoCreate = {
38354                 tag: "textarea",
38355                 style:"width:300px;height:60px;",
38356                 autocomplete: "new-password"
38357             };
38358         }
38359         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38360         if(this.grow){
38361             this.textSizeEl = Roo.DomHelper.append(document.body, {
38362                 tag: "pre", cls: "x-form-grow-sizer"
38363             });
38364             if(this.preventScrollbars){
38365                 this.el.setStyle("overflow", "hidden");
38366             }
38367             this.el.setHeight(this.growMin);
38368         }
38369     },
38370
38371     onDestroy : function(){
38372         if(this.textSizeEl){
38373             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38374         }
38375         Roo.form.TextArea.superclass.onDestroy.call(this);
38376     },
38377
38378     // private
38379     onKeyUp : function(e){
38380         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38381             this.autoSize();
38382         }
38383     },
38384
38385     /**
38386      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38387      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38388      */
38389     autoSize : function(){
38390         if(!this.grow || !this.textSizeEl){
38391             return;
38392         }
38393         var el = this.el;
38394         var v = el.dom.value;
38395         var ts = this.textSizeEl;
38396
38397         ts.innerHTML = '';
38398         ts.appendChild(document.createTextNode(v));
38399         v = ts.innerHTML;
38400
38401         Roo.fly(ts).setWidth(this.el.getWidth());
38402         if(v.length < 1){
38403             v = "&#160;&#160;";
38404         }else{
38405             if(Roo.isIE){
38406                 v = v.replace(/\n/g, '<p>&#160;</p>');
38407             }
38408             v += "&#160;\n&#160;";
38409         }
38410         ts.innerHTML = v;
38411         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38412         if(h != this.lastHeight){
38413             this.lastHeight = h;
38414             this.el.setHeight(h);
38415             this.fireEvent("autosize", this, h);
38416         }
38417     }
38418 });/*
38419  * Based on:
38420  * Ext JS Library 1.1.1
38421  * Copyright(c) 2006-2007, Ext JS, LLC.
38422  *
38423  * Originally Released Under LGPL - original licence link has changed is not relivant.
38424  *
38425  * Fork - LGPL
38426  * <script type="text/javascript">
38427  */
38428  
38429
38430 /**
38431  * @class Roo.form.NumberField
38432  * @extends Roo.form.TextField
38433  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38434  * @constructor
38435  * Creates a new NumberField
38436  * @param {Object} config Configuration options
38437  */
38438 Roo.form.NumberField = function(config){
38439     Roo.form.NumberField.superclass.constructor.call(this, config);
38440 };
38441
38442 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38443     /**
38444      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38445      */
38446     fieldClass: "x-form-field x-form-num-field",
38447     /**
38448      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38449      */
38450     allowDecimals : true,
38451     /**
38452      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38453      */
38454     decimalSeparator : ".",
38455     /**
38456      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38457      */
38458     decimalPrecision : 2,
38459     /**
38460      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38461      */
38462     allowNegative : true,
38463     /**
38464      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38465      */
38466     minValue : Number.NEGATIVE_INFINITY,
38467     /**
38468      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38469      */
38470     maxValue : Number.MAX_VALUE,
38471     /**
38472      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38473      */
38474     minText : "The minimum value for this field is {0}",
38475     /**
38476      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38477      */
38478     maxText : "The maximum value for this field is {0}",
38479     /**
38480      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38481      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38482      */
38483     nanText : "{0} is not a valid number",
38484
38485     // private
38486     initEvents : function(){
38487         Roo.form.NumberField.superclass.initEvents.call(this);
38488         var allowed = "0123456789";
38489         if(this.allowDecimals){
38490             allowed += this.decimalSeparator;
38491         }
38492         if(this.allowNegative){
38493             allowed += "-";
38494         }
38495         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38496         var keyPress = function(e){
38497             var k = e.getKey();
38498             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38499                 return;
38500             }
38501             var c = e.getCharCode();
38502             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38503                 e.stopEvent();
38504             }
38505         };
38506         this.el.on("keypress", keyPress, this);
38507     },
38508
38509     // private
38510     validateValue : function(value){
38511         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38512             return false;
38513         }
38514         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38515              return true;
38516         }
38517         var num = this.parseValue(value);
38518         if(isNaN(num)){
38519             this.markInvalid(String.format(this.nanText, value));
38520             return false;
38521         }
38522         if(num < this.minValue){
38523             this.markInvalid(String.format(this.minText, this.minValue));
38524             return false;
38525         }
38526         if(num > this.maxValue){
38527             this.markInvalid(String.format(this.maxText, this.maxValue));
38528             return false;
38529         }
38530         return true;
38531     },
38532
38533     getValue : function(){
38534         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38535     },
38536
38537     // private
38538     parseValue : function(value){
38539         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38540         return isNaN(value) ? '' : value;
38541     },
38542
38543     // private
38544     fixPrecision : function(value){
38545         var nan = isNaN(value);
38546         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38547             return nan ? '' : value;
38548         }
38549         return parseFloat(value).toFixed(this.decimalPrecision);
38550     },
38551
38552     setValue : function(v){
38553         v = this.fixPrecision(v);
38554         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38555     },
38556
38557     // private
38558     decimalPrecisionFcn : function(v){
38559         return Math.floor(v);
38560     },
38561
38562     beforeBlur : function(){
38563         var v = this.parseValue(this.getRawValue());
38564         if(v){
38565             this.setValue(v);
38566         }
38567     }
38568 });/*
38569  * Based on:
38570  * Ext JS Library 1.1.1
38571  * Copyright(c) 2006-2007, Ext JS, LLC.
38572  *
38573  * Originally Released Under LGPL - original licence link has changed is not relivant.
38574  *
38575  * Fork - LGPL
38576  * <script type="text/javascript">
38577  */
38578  
38579 /**
38580  * @class Roo.form.DateField
38581  * @extends Roo.form.TriggerField
38582  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38583 * @constructor
38584 * Create a new DateField
38585 * @param {Object} config
38586  */
38587 Roo.form.DateField = function(config){
38588     Roo.form.DateField.superclass.constructor.call(this, config);
38589     
38590       this.addEvents({
38591          
38592         /**
38593          * @event select
38594          * Fires when a date is selected
38595              * @param {Roo.form.DateField} combo This combo box
38596              * @param {Date} date The date selected
38597              */
38598         'select' : true
38599          
38600     });
38601     
38602     
38603     if(typeof this.minValue == "string") {
38604         this.minValue = this.parseDate(this.minValue);
38605     }
38606     if(typeof this.maxValue == "string") {
38607         this.maxValue = this.parseDate(this.maxValue);
38608     }
38609     this.ddMatch = null;
38610     if(this.disabledDates){
38611         var dd = this.disabledDates;
38612         var re = "(?:";
38613         for(var i = 0; i < dd.length; i++){
38614             re += dd[i];
38615             if(i != dd.length-1) {
38616                 re += "|";
38617             }
38618         }
38619         this.ddMatch = new RegExp(re + ")");
38620     }
38621 };
38622
38623 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38624     /**
38625      * @cfg {String} format
38626      * The default date format string which can be overriden for localization support.  The format must be
38627      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38628      */
38629     format : "m/d/y",
38630     /**
38631      * @cfg {String} altFormats
38632      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38633      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38634      */
38635     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38636     /**
38637      * @cfg {Array} disabledDays
38638      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38639      */
38640     disabledDays : null,
38641     /**
38642      * @cfg {String} disabledDaysText
38643      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38644      */
38645     disabledDaysText : "Disabled",
38646     /**
38647      * @cfg {Array} disabledDates
38648      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38649      * expression so they are very powerful. Some examples:
38650      * <ul>
38651      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38652      * <li>["03/08", "09/16"] would disable those days for every year</li>
38653      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38654      * <li>["03/../2006"] would disable every day in March 2006</li>
38655      * <li>["^03"] would disable every day in every March</li>
38656      * </ul>
38657      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38658      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38659      */
38660     disabledDates : null,
38661     /**
38662      * @cfg {String} disabledDatesText
38663      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38664      */
38665     disabledDatesText : "Disabled",
38666     /**
38667      * @cfg {Date/String} minValue
38668      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38669      * valid format (defaults to null).
38670      */
38671     minValue : null,
38672     /**
38673      * @cfg {Date/String} maxValue
38674      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38675      * valid format (defaults to null).
38676      */
38677     maxValue : null,
38678     /**
38679      * @cfg {String} minText
38680      * The error text to display when the date in the cell is before minValue (defaults to
38681      * 'The date in this field must be after {minValue}').
38682      */
38683     minText : "The date in this field must be equal to or after {0}",
38684     /**
38685      * @cfg {String} maxText
38686      * The error text to display when the date in the cell is after maxValue (defaults to
38687      * 'The date in this field must be before {maxValue}').
38688      */
38689     maxText : "The date in this field must be equal to or before {0}",
38690     /**
38691      * @cfg {String} invalidText
38692      * The error text to display when the date in the field is invalid (defaults to
38693      * '{value} is not a valid date - it must be in the format {format}').
38694      */
38695     invalidText : "{0} is not a valid date - it must be in the format {1}",
38696     /**
38697      * @cfg {String} triggerClass
38698      * An additional CSS class used to style the trigger button.  The trigger will always get the
38699      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38700      * which displays a calendar icon).
38701      */
38702     triggerClass : 'x-form-date-trigger',
38703     
38704
38705     /**
38706      * @cfg {Boolean} useIso
38707      * if enabled, then the date field will use a hidden field to store the 
38708      * real value as iso formated date. default (false)
38709      */ 
38710     useIso : false,
38711     /**
38712      * @cfg {String/Object} autoCreate
38713      * A DomHelper element spec, or true for a default element spec (defaults to
38714      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38715      */ 
38716     // private
38717     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38718     
38719     // private
38720     hiddenField: false,
38721     
38722     onRender : function(ct, position)
38723     {
38724         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38725         if (this.useIso) {
38726             //this.el.dom.removeAttribute('name'); 
38727             Roo.log("Changing name?");
38728             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38729             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38730                     'before', true);
38731             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38732             // prevent input submission
38733             this.hiddenName = this.name;
38734         }
38735             
38736             
38737     },
38738     
38739     // private
38740     validateValue : function(value)
38741     {
38742         value = this.formatDate(value);
38743         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38744             Roo.log('super failed');
38745             return false;
38746         }
38747         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38748              return true;
38749         }
38750         var svalue = value;
38751         value = this.parseDate(value);
38752         if(!value){
38753             Roo.log('parse date failed' + svalue);
38754             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38755             return false;
38756         }
38757         var time = value.getTime();
38758         if(this.minValue && time < this.minValue.getTime()){
38759             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38760             return false;
38761         }
38762         if(this.maxValue && time > this.maxValue.getTime()){
38763             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38764             return false;
38765         }
38766         if(this.disabledDays){
38767             var day = value.getDay();
38768             for(var i = 0; i < this.disabledDays.length; i++) {
38769                 if(day === this.disabledDays[i]){
38770                     this.markInvalid(this.disabledDaysText);
38771                     return false;
38772                 }
38773             }
38774         }
38775         var fvalue = this.formatDate(value);
38776         if(this.ddMatch && this.ddMatch.test(fvalue)){
38777             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38778             return false;
38779         }
38780         return true;
38781     },
38782
38783     // private
38784     // Provides logic to override the default TriggerField.validateBlur which just returns true
38785     validateBlur : function(){
38786         return !this.menu || !this.menu.isVisible();
38787     },
38788     
38789     getName: function()
38790     {
38791         // returns hidden if it's set..
38792         if (!this.rendered) {return ''};
38793         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38794         
38795     },
38796
38797     /**
38798      * Returns the current date value of the date field.
38799      * @return {Date} The date value
38800      */
38801     getValue : function(){
38802         
38803         return  this.hiddenField ?
38804                 this.hiddenField.value :
38805                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38806     },
38807
38808     /**
38809      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38810      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38811      * (the default format used is "m/d/y").
38812      * <br />Usage:
38813      * <pre><code>
38814 //All of these calls set the same date value (May 4, 2006)
38815
38816 //Pass a date object:
38817 var dt = new Date('5/4/06');
38818 dateField.setValue(dt);
38819
38820 //Pass a date string (default format):
38821 dateField.setValue('5/4/06');
38822
38823 //Pass a date string (custom format):
38824 dateField.format = 'Y-m-d';
38825 dateField.setValue('2006-5-4');
38826 </code></pre>
38827      * @param {String/Date} date The date or valid date string
38828      */
38829     setValue : function(date){
38830         if (this.hiddenField) {
38831             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38832         }
38833         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38834         // make sure the value field is always stored as a date..
38835         this.value = this.parseDate(date);
38836         
38837         
38838     },
38839
38840     // private
38841     parseDate : function(value){
38842         if(!value || value instanceof Date){
38843             return value;
38844         }
38845         var v = Date.parseDate(value, this.format);
38846          if (!v && this.useIso) {
38847             v = Date.parseDate(value, 'Y-m-d');
38848         }
38849         if(!v && this.altFormats){
38850             if(!this.altFormatsArray){
38851                 this.altFormatsArray = this.altFormats.split("|");
38852             }
38853             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38854                 v = Date.parseDate(value, this.altFormatsArray[i]);
38855             }
38856         }
38857         return v;
38858     },
38859
38860     // private
38861     formatDate : function(date, fmt){
38862         return (!date || !(date instanceof Date)) ?
38863                date : date.dateFormat(fmt || this.format);
38864     },
38865
38866     // private
38867     menuListeners : {
38868         select: function(m, d){
38869             
38870             this.setValue(d);
38871             this.fireEvent('select', this, d);
38872         },
38873         show : function(){ // retain focus styling
38874             this.onFocus();
38875         },
38876         hide : function(){
38877             this.focus.defer(10, this);
38878             var ml = this.menuListeners;
38879             this.menu.un("select", ml.select,  this);
38880             this.menu.un("show", ml.show,  this);
38881             this.menu.un("hide", ml.hide,  this);
38882         }
38883     },
38884
38885     // private
38886     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38887     onTriggerClick : function(){
38888         if(this.disabled){
38889             return;
38890         }
38891         if(this.menu == null){
38892             this.menu = new Roo.menu.DateMenu();
38893         }
38894         Roo.apply(this.menu.picker,  {
38895             showClear: this.allowBlank,
38896             minDate : this.minValue,
38897             maxDate : this.maxValue,
38898             disabledDatesRE : this.ddMatch,
38899             disabledDatesText : this.disabledDatesText,
38900             disabledDays : this.disabledDays,
38901             disabledDaysText : this.disabledDaysText,
38902             format : this.useIso ? 'Y-m-d' : this.format,
38903             minText : String.format(this.minText, this.formatDate(this.minValue)),
38904             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38905         });
38906         this.menu.on(Roo.apply({}, this.menuListeners, {
38907             scope:this
38908         }));
38909         this.menu.picker.setValue(this.getValue() || new Date());
38910         this.menu.show(this.el, "tl-bl?");
38911     },
38912
38913     beforeBlur : function(){
38914         var v = this.parseDate(this.getRawValue());
38915         if(v){
38916             this.setValue(v);
38917         }
38918     },
38919
38920     /*@
38921      * overide
38922      * 
38923      */
38924     isDirty : function() {
38925         if(this.disabled) {
38926             return false;
38927         }
38928         
38929         if(typeof(this.startValue) === 'undefined'){
38930             return false;
38931         }
38932         
38933         return String(this.getValue()) !== String(this.startValue);
38934         
38935     }
38936 });/*
38937  * Based on:
38938  * Ext JS Library 1.1.1
38939  * Copyright(c) 2006-2007, Ext JS, LLC.
38940  *
38941  * Originally Released Under LGPL - original licence link has changed is not relivant.
38942  *
38943  * Fork - LGPL
38944  * <script type="text/javascript">
38945  */
38946  
38947 /**
38948  * @class Roo.form.MonthField
38949  * @extends Roo.form.TriggerField
38950  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38951 * @constructor
38952 * Create a new MonthField
38953 * @param {Object} config
38954  */
38955 Roo.form.MonthField = function(config){
38956     
38957     Roo.form.MonthField.superclass.constructor.call(this, config);
38958     
38959       this.addEvents({
38960          
38961         /**
38962          * @event select
38963          * Fires when a date is selected
38964              * @param {Roo.form.MonthFieeld} combo This combo box
38965              * @param {Date} date The date selected
38966              */
38967         'select' : true
38968          
38969     });
38970     
38971     
38972     if(typeof this.minValue == "string") {
38973         this.minValue = this.parseDate(this.minValue);
38974     }
38975     if(typeof this.maxValue == "string") {
38976         this.maxValue = this.parseDate(this.maxValue);
38977     }
38978     this.ddMatch = null;
38979     if(this.disabledDates){
38980         var dd = this.disabledDates;
38981         var re = "(?:";
38982         for(var i = 0; i < dd.length; i++){
38983             re += dd[i];
38984             if(i != dd.length-1) {
38985                 re += "|";
38986             }
38987         }
38988         this.ddMatch = new RegExp(re + ")");
38989     }
38990 };
38991
38992 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38993     /**
38994      * @cfg {String} format
38995      * The default date format string which can be overriden for localization support.  The format must be
38996      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38997      */
38998     format : "M Y",
38999     /**
39000      * @cfg {String} altFormats
39001      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
39002      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
39003      */
39004     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
39005     /**
39006      * @cfg {Array} disabledDays
39007      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
39008      */
39009     disabledDays : [0,1,2,3,4,5,6],
39010     /**
39011      * @cfg {String} disabledDaysText
39012      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
39013      */
39014     disabledDaysText : "Disabled",
39015     /**
39016      * @cfg {Array} disabledDates
39017      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
39018      * expression so they are very powerful. Some examples:
39019      * <ul>
39020      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
39021      * <li>["03/08", "09/16"] would disable those days for every year</li>
39022      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
39023      * <li>["03/../2006"] would disable every day in March 2006</li>
39024      * <li>["^03"] would disable every day in every March</li>
39025      * </ul>
39026      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
39027      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
39028      */
39029     disabledDates : null,
39030     /**
39031      * @cfg {String} disabledDatesText
39032      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
39033      */
39034     disabledDatesText : "Disabled",
39035     /**
39036      * @cfg {Date/String} minValue
39037      * The minimum allowed date. Can be either a Javascript date object or a string date in a
39038      * valid format (defaults to null).
39039      */
39040     minValue : null,
39041     /**
39042      * @cfg {Date/String} maxValue
39043      * The maximum allowed date. Can be either a Javascript date object or a string date in a
39044      * valid format (defaults to null).
39045      */
39046     maxValue : null,
39047     /**
39048      * @cfg {String} minText
39049      * The error text to display when the date in the cell is before minValue (defaults to
39050      * 'The date in this field must be after {minValue}').
39051      */
39052     minText : "The date in this field must be equal to or after {0}",
39053     /**
39054      * @cfg {String} maxTextf
39055      * The error text to display when the date in the cell is after maxValue (defaults to
39056      * 'The date in this field must be before {maxValue}').
39057      */
39058     maxText : "The date in this field must be equal to or before {0}",
39059     /**
39060      * @cfg {String} invalidText
39061      * The error text to display when the date in the field is invalid (defaults to
39062      * '{value} is not a valid date - it must be in the format {format}').
39063      */
39064     invalidText : "{0} is not a valid date - it must be in the format {1}",
39065     /**
39066      * @cfg {String} triggerClass
39067      * An additional CSS class used to style the trigger button.  The trigger will always get the
39068      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
39069      * which displays a calendar icon).
39070      */
39071     triggerClass : 'x-form-date-trigger',
39072     
39073
39074     /**
39075      * @cfg {Boolean} useIso
39076      * if enabled, then the date field will use a hidden field to store the 
39077      * real value as iso formated date. default (true)
39078      */ 
39079     useIso : true,
39080     /**
39081      * @cfg {String/Object} autoCreate
39082      * A DomHelper element spec, or true for a default element spec (defaults to
39083      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
39084      */ 
39085     // private
39086     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
39087     
39088     // private
39089     hiddenField: false,
39090     
39091     hideMonthPicker : false,
39092     
39093     onRender : function(ct, position)
39094     {
39095         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
39096         if (this.useIso) {
39097             this.el.dom.removeAttribute('name'); 
39098             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
39099                     'before', true);
39100             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
39101             // prevent input submission
39102             this.hiddenName = this.name;
39103         }
39104             
39105             
39106     },
39107     
39108     // private
39109     validateValue : function(value)
39110     {
39111         value = this.formatDate(value);
39112         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
39113             return false;
39114         }
39115         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
39116              return true;
39117         }
39118         var svalue = value;
39119         value = this.parseDate(value);
39120         if(!value){
39121             this.markInvalid(String.format(this.invalidText, svalue, this.format));
39122             return false;
39123         }
39124         var time = value.getTime();
39125         if(this.minValue && time < this.minValue.getTime()){
39126             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
39127             return false;
39128         }
39129         if(this.maxValue && time > this.maxValue.getTime()){
39130             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
39131             return false;
39132         }
39133         /*if(this.disabledDays){
39134             var day = value.getDay();
39135             for(var i = 0; i < this.disabledDays.length; i++) {
39136                 if(day === this.disabledDays[i]){
39137                     this.markInvalid(this.disabledDaysText);
39138                     return false;
39139                 }
39140             }
39141         }
39142         */
39143         var fvalue = this.formatDate(value);
39144         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
39145             this.markInvalid(String.format(this.disabledDatesText, fvalue));
39146             return false;
39147         }
39148         */
39149         return true;
39150     },
39151
39152     // private
39153     // Provides logic to override the default TriggerField.validateBlur which just returns true
39154     validateBlur : function(){
39155         return !this.menu || !this.menu.isVisible();
39156     },
39157
39158     /**
39159      * Returns the current date value of the date field.
39160      * @return {Date} The date value
39161      */
39162     getValue : function(){
39163         
39164         
39165         
39166         return  this.hiddenField ?
39167                 this.hiddenField.value :
39168                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
39169     },
39170
39171     /**
39172      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
39173      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
39174      * (the default format used is "m/d/y").
39175      * <br />Usage:
39176      * <pre><code>
39177 //All of these calls set the same date value (May 4, 2006)
39178
39179 //Pass a date object:
39180 var dt = new Date('5/4/06');
39181 monthField.setValue(dt);
39182
39183 //Pass a date string (default format):
39184 monthField.setValue('5/4/06');
39185
39186 //Pass a date string (custom format):
39187 monthField.format = 'Y-m-d';
39188 monthField.setValue('2006-5-4');
39189 </code></pre>
39190      * @param {String/Date} date The date or valid date string
39191      */
39192     setValue : function(date){
39193         Roo.log('month setValue' + date);
39194         // can only be first of month..
39195         
39196         var val = this.parseDate(date);
39197         
39198         if (this.hiddenField) {
39199             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
39200         }
39201         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
39202         this.value = this.parseDate(date);
39203     },
39204
39205     // private
39206     parseDate : function(value){
39207         if(!value || value instanceof Date){
39208             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
39209             return value;
39210         }
39211         var v = Date.parseDate(value, this.format);
39212         if (!v && this.useIso) {
39213             v = Date.parseDate(value, 'Y-m-d');
39214         }
39215         if (v) {
39216             // 
39217             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
39218         }
39219         
39220         
39221         if(!v && this.altFormats){
39222             if(!this.altFormatsArray){
39223                 this.altFormatsArray = this.altFormats.split("|");
39224             }
39225             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
39226                 v = Date.parseDate(value, this.altFormatsArray[i]);
39227             }
39228         }
39229         return v;
39230     },
39231
39232     // private
39233     formatDate : function(date, fmt){
39234         return (!date || !(date instanceof Date)) ?
39235                date : date.dateFormat(fmt || this.format);
39236     },
39237
39238     // private
39239     menuListeners : {
39240         select: function(m, d){
39241             this.setValue(d);
39242             this.fireEvent('select', this, d);
39243         },
39244         show : function(){ // retain focus styling
39245             this.onFocus();
39246         },
39247         hide : function(){
39248             this.focus.defer(10, this);
39249             var ml = this.menuListeners;
39250             this.menu.un("select", ml.select,  this);
39251             this.menu.un("show", ml.show,  this);
39252             this.menu.un("hide", ml.hide,  this);
39253         }
39254     },
39255     // private
39256     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
39257     onTriggerClick : function(){
39258         if(this.disabled){
39259             return;
39260         }
39261         if(this.menu == null){
39262             this.menu = new Roo.menu.DateMenu();
39263            
39264         }
39265         
39266         Roo.apply(this.menu.picker,  {
39267             
39268             showClear: this.allowBlank,
39269             minDate : this.minValue,
39270             maxDate : this.maxValue,
39271             disabledDatesRE : this.ddMatch,
39272             disabledDatesText : this.disabledDatesText,
39273             
39274             format : this.useIso ? 'Y-m-d' : this.format,
39275             minText : String.format(this.minText, this.formatDate(this.minValue)),
39276             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39277             
39278         });
39279          this.menu.on(Roo.apply({}, this.menuListeners, {
39280             scope:this
39281         }));
39282        
39283         
39284         var m = this.menu;
39285         var p = m.picker;
39286         
39287         // hide month picker get's called when we called by 'before hide';
39288         
39289         var ignorehide = true;
39290         p.hideMonthPicker  = function(disableAnim){
39291             if (ignorehide) {
39292                 return;
39293             }
39294              if(this.monthPicker){
39295                 Roo.log("hideMonthPicker called");
39296                 if(disableAnim === true){
39297                     this.monthPicker.hide();
39298                 }else{
39299                     this.monthPicker.slideOut('t', {duration:.2});
39300                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39301                     p.fireEvent("select", this, this.value);
39302                     m.hide();
39303                 }
39304             }
39305         }
39306         
39307         Roo.log('picker set value');
39308         Roo.log(this.getValue());
39309         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39310         m.show(this.el, 'tl-bl?');
39311         ignorehide  = false;
39312         // this will trigger hideMonthPicker..
39313         
39314         
39315         // hidden the day picker
39316         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39317         
39318         
39319         
39320       
39321         
39322         p.showMonthPicker.defer(100, p);
39323     
39324         
39325        
39326     },
39327
39328     beforeBlur : function(){
39329         var v = this.parseDate(this.getRawValue());
39330         if(v){
39331             this.setValue(v);
39332         }
39333     }
39334
39335     /** @cfg {Boolean} grow @hide */
39336     /** @cfg {Number} growMin @hide */
39337     /** @cfg {Number} growMax @hide */
39338     /**
39339      * @hide
39340      * @method autoSize
39341      */
39342 });/*
39343  * Based on:
39344  * Ext JS Library 1.1.1
39345  * Copyright(c) 2006-2007, Ext JS, LLC.
39346  *
39347  * Originally Released Under LGPL - original licence link has changed is not relivant.
39348  *
39349  * Fork - LGPL
39350  * <script type="text/javascript">
39351  */
39352  
39353
39354 /**
39355  * @class Roo.form.ComboBox
39356  * @extends Roo.form.TriggerField
39357  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39358  * @constructor
39359  * Create a new ComboBox.
39360  * @param {Object} config Configuration options
39361  */
39362 Roo.form.ComboBox = function(config){
39363     Roo.form.ComboBox.superclass.constructor.call(this, config);
39364     this.addEvents({
39365         /**
39366          * @event expand
39367          * Fires when the dropdown list is expanded
39368              * @param {Roo.form.ComboBox} combo This combo box
39369              */
39370         'expand' : true,
39371         /**
39372          * @event collapse
39373          * Fires when the dropdown list is collapsed
39374              * @param {Roo.form.ComboBox} combo This combo box
39375              */
39376         'collapse' : true,
39377         /**
39378          * @event beforeselect
39379          * Fires before a list item is selected. Return false to cancel the selection.
39380              * @param {Roo.form.ComboBox} combo This combo box
39381              * @param {Roo.data.Record} record The data record returned from the underlying store
39382              * @param {Number} index The index of the selected item in the dropdown list
39383              */
39384         'beforeselect' : true,
39385         /**
39386          * @event select
39387          * Fires when a list item is selected
39388              * @param {Roo.form.ComboBox} combo This combo box
39389              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39390              * @param {Number} index The index of the selected item in the dropdown list
39391              */
39392         'select' : true,
39393         /**
39394          * @event beforequery
39395          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39396          * The event object passed has these properties:
39397              * @param {Roo.form.ComboBox} combo This combo box
39398              * @param {String} query The query
39399              * @param {Boolean} forceAll true to force "all" query
39400              * @param {Boolean} cancel true to cancel the query
39401              * @param {Object} e The query event object
39402              */
39403         'beforequery': true,
39404          /**
39405          * @event add
39406          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39407              * @param {Roo.form.ComboBox} combo This combo box
39408              */
39409         'add' : true,
39410         /**
39411          * @event edit
39412          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39413              * @param {Roo.form.ComboBox} combo This combo box
39414              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39415              */
39416         'edit' : true
39417         
39418         
39419     });
39420     if(this.transform){
39421         this.allowDomMove = false;
39422         var s = Roo.getDom(this.transform);
39423         if(!this.hiddenName){
39424             this.hiddenName = s.name;
39425         }
39426         if(!this.store){
39427             this.mode = 'local';
39428             var d = [], opts = s.options;
39429             for(var i = 0, len = opts.length;i < len; i++){
39430                 var o = opts[i];
39431                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39432                 if(o.selected) {
39433                     this.value = value;
39434                 }
39435                 d.push([value, o.text]);
39436             }
39437             this.store = new Roo.data.SimpleStore({
39438                 'id': 0,
39439                 fields: ['value', 'text'],
39440                 data : d
39441             });
39442             this.valueField = 'value';
39443             this.displayField = 'text';
39444         }
39445         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39446         if(!this.lazyRender){
39447             this.target = true;
39448             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39449             s.parentNode.removeChild(s); // remove it
39450             this.render(this.el.parentNode);
39451         }else{
39452             s.parentNode.removeChild(s); // remove it
39453         }
39454
39455     }
39456     if (this.store) {
39457         this.store = Roo.factory(this.store, Roo.data);
39458     }
39459     
39460     this.selectedIndex = -1;
39461     if(this.mode == 'local'){
39462         if(config.queryDelay === undefined){
39463             this.queryDelay = 10;
39464         }
39465         if(config.minChars === undefined){
39466             this.minChars = 0;
39467         }
39468     }
39469 };
39470
39471 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39472     /**
39473      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39474      */
39475     /**
39476      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39477      * rendering into an Roo.Editor, defaults to false)
39478      */
39479     /**
39480      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39481      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39482      */
39483     /**
39484      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39485      */
39486     /**
39487      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39488      * the dropdown list (defaults to undefined, with no header element)
39489      */
39490
39491      /**
39492      * @cfg {String/Roo.Template} tpl The template to use to render the output
39493      */
39494      
39495     // private
39496     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39497     /**
39498      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39499      */
39500     listWidth: undefined,
39501     /**
39502      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39503      * mode = 'remote' or 'text' if mode = 'local')
39504      */
39505     displayField: undefined,
39506     /**
39507      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39508      * mode = 'remote' or 'value' if mode = 'local'). 
39509      * Note: use of a valueField requires the user make a selection
39510      * in order for a value to be mapped.
39511      */
39512     valueField: undefined,
39513     
39514     
39515     /**
39516      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39517      * field's data value (defaults to the underlying DOM element's name)
39518      */
39519     hiddenName: undefined,
39520     /**
39521      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39522      */
39523     listClass: '',
39524     /**
39525      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39526      */
39527     selectedClass: 'x-combo-selected',
39528     /**
39529      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39530      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39531      * which displays a downward arrow icon).
39532      */
39533     triggerClass : 'x-form-arrow-trigger',
39534     /**
39535      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39536      */
39537     shadow:'sides',
39538     /**
39539      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39540      * anchor positions (defaults to 'tl-bl')
39541      */
39542     listAlign: 'tl-bl?',
39543     /**
39544      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39545      */
39546     maxHeight: 300,
39547     /**
39548      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39549      * query specified by the allQuery config option (defaults to 'query')
39550      */
39551     triggerAction: 'query',
39552     /**
39553      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39554      * (defaults to 4, does not apply if editable = false)
39555      */
39556     minChars : 4,
39557     /**
39558      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39559      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39560      */
39561     typeAhead: false,
39562     /**
39563      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39564      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39565      */
39566     queryDelay: 500,
39567     /**
39568      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39569      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39570      */
39571     pageSize: 0,
39572     /**
39573      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39574      * when editable = true (defaults to false)
39575      */
39576     selectOnFocus:false,
39577     /**
39578      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39579      */
39580     queryParam: 'query',
39581     /**
39582      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39583      * when mode = 'remote' (defaults to 'Loading...')
39584      */
39585     loadingText: 'Loading...',
39586     /**
39587      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39588      */
39589     resizable: false,
39590     /**
39591      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39592      */
39593     handleHeight : 8,
39594     /**
39595      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39596      * traditional select (defaults to true)
39597      */
39598     editable: true,
39599     /**
39600      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39601      */
39602     allQuery: '',
39603     /**
39604      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39605      */
39606     mode: 'remote',
39607     /**
39608      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39609      * listWidth has a higher value)
39610      */
39611     minListWidth : 70,
39612     /**
39613      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39614      * allow the user to set arbitrary text into the field (defaults to false)
39615      */
39616     forceSelection:false,
39617     /**
39618      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39619      * if typeAhead = true (defaults to 250)
39620      */
39621     typeAheadDelay : 250,
39622     /**
39623      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39624      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39625      */
39626     valueNotFoundText : undefined,
39627     /**
39628      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39629      */
39630     blockFocus : false,
39631     
39632     /**
39633      * @cfg {Boolean} disableClear Disable showing of clear button.
39634      */
39635     disableClear : false,
39636     /**
39637      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39638      */
39639     alwaysQuery : false,
39640     
39641     //private
39642     addicon : false,
39643     editicon: false,
39644     
39645     // element that contains real text value.. (when hidden is used..)
39646      
39647     // private
39648     onRender : function(ct, position){
39649         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39650         if(this.hiddenName){
39651             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39652                     'before', true);
39653             this.hiddenField.value =
39654                 this.hiddenValue !== undefined ? this.hiddenValue :
39655                 this.value !== undefined ? this.value : '';
39656
39657             // prevent input submission
39658             this.el.dom.removeAttribute('name');
39659              
39660              
39661         }
39662         if(Roo.isGecko){
39663             this.el.dom.setAttribute('autocomplete', 'off');
39664         }
39665
39666         var cls = 'x-combo-list';
39667
39668         this.list = new Roo.Layer({
39669             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39670         });
39671
39672         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39673         this.list.setWidth(lw);
39674         this.list.swallowEvent('mousewheel');
39675         this.assetHeight = 0;
39676
39677         if(this.title){
39678             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39679             this.assetHeight += this.header.getHeight();
39680         }
39681
39682         this.innerList = this.list.createChild({cls:cls+'-inner'});
39683         this.innerList.on('mouseover', this.onViewOver, this);
39684         this.innerList.on('mousemove', this.onViewMove, this);
39685         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39686         
39687         if(this.allowBlank && !this.pageSize && !this.disableClear){
39688             this.footer = this.list.createChild({cls:cls+'-ft'});
39689             this.pageTb = new Roo.Toolbar(this.footer);
39690            
39691         }
39692         if(this.pageSize){
39693             this.footer = this.list.createChild({cls:cls+'-ft'});
39694             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39695                     {pageSize: this.pageSize});
39696             
39697         }
39698         
39699         if (this.pageTb && this.allowBlank && !this.disableClear) {
39700             var _this = this;
39701             this.pageTb.add(new Roo.Toolbar.Fill(), {
39702                 cls: 'x-btn-icon x-btn-clear',
39703                 text: '&#160;',
39704                 handler: function()
39705                 {
39706                     _this.collapse();
39707                     _this.clearValue();
39708                     _this.onSelect(false, -1);
39709                 }
39710             });
39711         }
39712         if (this.footer) {
39713             this.assetHeight += this.footer.getHeight();
39714         }
39715         
39716
39717         if(!this.tpl){
39718             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39719         }
39720
39721         this.view = new Roo.View(this.innerList, this.tpl, {
39722             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39723         });
39724
39725         this.view.on('click', this.onViewClick, this);
39726
39727         this.store.on('beforeload', this.onBeforeLoad, this);
39728         this.store.on('load', this.onLoad, this);
39729         this.store.on('loadexception', this.onLoadException, this);
39730
39731         if(this.resizable){
39732             this.resizer = new Roo.Resizable(this.list,  {
39733                pinned:true, handles:'se'
39734             });
39735             this.resizer.on('resize', function(r, w, h){
39736                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39737                 this.listWidth = w;
39738                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39739                 this.restrictHeight();
39740             }, this);
39741             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39742         }
39743         if(!this.editable){
39744             this.editable = true;
39745             this.setEditable(false);
39746         }  
39747         
39748         
39749         if (typeof(this.events.add.listeners) != 'undefined') {
39750             
39751             this.addicon = this.wrap.createChild(
39752                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39753        
39754             this.addicon.on('click', function(e) {
39755                 this.fireEvent('add', this);
39756             }, this);
39757         }
39758         if (typeof(this.events.edit.listeners) != 'undefined') {
39759             
39760             this.editicon = this.wrap.createChild(
39761                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39762             if (this.addicon) {
39763                 this.editicon.setStyle('margin-left', '40px');
39764             }
39765             this.editicon.on('click', function(e) {
39766                 
39767                 // we fire even  if inothing is selected..
39768                 this.fireEvent('edit', this, this.lastData );
39769                 
39770             }, this);
39771         }
39772         
39773         
39774         
39775     },
39776
39777     // private
39778     initEvents : function(){
39779         Roo.form.ComboBox.superclass.initEvents.call(this);
39780
39781         this.keyNav = new Roo.KeyNav(this.el, {
39782             "up" : function(e){
39783                 this.inKeyMode = true;
39784                 this.selectPrev();
39785             },
39786
39787             "down" : function(e){
39788                 if(!this.isExpanded()){
39789                     this.onTriggerClick();
39790                 }else{
39791                     this.inKeyMode = true;
39792                     this.selectNext();
39793                 }
39794             },
39795
39796             "enter" : function(e){
39797                 this.onViewClick();
39798                 //return true;
39799             },
39800
39801             "esc" : function(e){
39802                 this.collapse();
39803             },
39804
39805             "tab" : function(e){
39806                 this.onViewClick(false);
39807                 this.fireEvent("specialkey", this, e);
39808                 return true;
39809             },
39810
39811             scope : this,
39812
39813             doRelay : function(foo, bar, hname){
39814                 if(hname == 'down' || this.scope.isExpanded()){
39815                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39816                 }
39817                 return true;
39818             },
39819
39820             forceKeyDown: true
39821         });
39822         this.queryDelay = Math.max(this.queryDelay || 10,
39823                 this.mode == 'local' ? 10 : 250);
39824         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39825         if(this.typeAhead){
39826             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39827         }
39828         if(this.editable !== false){
39829             this.el.on("keyup", this.onKeyUp, this);
39830         }
39831         if(this.forceSelection){
39832             this.on('blur', this.doForce, this);
39833         }
39834     },
39835
39836     onDestroy : function(){
39837         if(this.view){
39838             this.view.setStore(null);
39839             this.view.el.removeAllListeners();
39840             this.view.el.remove();
39841             this.view.purgeListeners();
39842         }
39843         if(this.list){
39844             this.list.destroy();
39845         }
39846         if(this.store){
39847             this.store.un('beforeload', this.onBeforeLoad, this);
39848             this.store.un('load', this.onLoad, this);
39849             this.store.un('loadexception', this.onLoadException, this);
39850         }
39851         Roo.form.ComboBox.superclass.onDestroy.call(this);
39852     },
39853
39854     // private
39855     fireKey : function(e){
39856         if(e.isNavKeyPress() && !this.list.isVisible()){
39857             this.fireEvent("specialkey", this, e);
39858         }
39859     },
39860
39861     // private
39862     onResize: function(w, h){
39863         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39864         
39865         if(typeof w != 'number'){
39866             // we do not handle it!?!?
39867             return;
39868         }
39869         var tw = this.trigger.getWidth();
39870         tw += this.addicon ? this.addicon.getWidth() : 0;
39871         tw += this.editicon ? this.editicon.getWidth() : 0;
39872         var x = w - tw;
39873         this.el.setWidth( this.adjustWidth('input', x));
39874             
39875         this.trigger.setStyle('left', x+'px');
39876         
39877         if(this.list && this.listWidth === undefined){
39878             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39879             this.list.setWidth(lw);
39880             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39881         }
39882         
39883     
39884         
39885     },
39886
39887     /**
39888      * Allow or prevent the user from directly editing the field text.  If false is passed,
39889      * the user will only be able to select from the items defined in the dropdown list.  This method
39890      * is the runtime equivalent of setting the 'editable' config option at config time.
39891      * @param {Boolean} value True to allow the user to directly edit the field text
39892      */
39893     setEditable : function(value){
39894         if(value == this.editable){
39895             return;
39896         }
39897         this.editable = value;
39898         if(!value){
39899             this.el.dom.setAttribute('readOnly', true);
39900             this.el.on('mousedown', this.onTriggerClick,  this);
39901             this.el.addClass('x-combo-noedit');
39902         }else{
39903             this.el.dom.setAttribute('readOnly', false);
39904             this.el.un('mousedown', this.onTriggerClick,  this);
39905             this.el.removeClass('x-combo-noedit');
39906         }
39907     },
39908
39909     // private
39910     onBeforeLoad : function(){
39911         if(!this.hasFocus){
39912             return;
39913         }
39914         this.innerList.update(this.loadingText ?
39915                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39916         this.restrictHeight();
39917         this.selectedIndex = -1;
39918     },
39919
39920     // private
39921     onLoad : function(){
39922         if(!this.hasFocus){
39923             return;
39924         }
39925         if(this.store.getCount() > 0){
39926             this.expand();
39927             this.restrictHeight();
39928             if(this.lastQuery == this.allQuery){
39929                 if(this.editable){
39930                     this.el.dom.select();
39931                 }
39932                 if(!this.selectByValue(this.value, true)){
39933                     this.select(0, true);
39934                 }
39935             }else{
39936                 this.selectNext();
39937                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39938                     this.taTask.delay(this.typeAheadDelay);
39939                 }
39940             }
39941         }else{
39942             this.onEmptyResults();
39943         }
39944         //this.el.focus();
39945     },
39946     // private
39947     onLoadException : function()
39948     {
39949         this.collapse();
39950         Roo.log(this.store.reader.jsonData);
39951         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39952             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39953         }
39954         
39955         
39956     },
39957     // private
39958     onTypeAhead : function(){
39959         if(this.store.getCount() > 0){
39960             var r = this.store.getAt(0);
39961             var newValue = r.data[this.displayField];
39962             var len = newValue.length;
39963             var selStart = this.getRawValue().length;
39964             if(selStart != len){
39965                 this.setRawValue(newValue);
39966                 this.selectText(selStart, newValue.length);
39967             }
39968         }
39969     },
39970
39971     // private
39972     onSelect : function(record, index){
39973         if(this.fireEvent('beforeselect', this, record, index) !== false){
39974             this.setFromData(index > -1 ? record.data : false);
39975             this.collapse();
39976             this.fireEvent('select', this, record, index);
39977         }
39978     },
39979
39980     /**
39981      * Returns the currently selected field value or empty string if no value is set.
39982      * @return {String} value The selected value
39983      */
39984     getValue : function(){
39985         if(this.valueField){
39986             return typeof this.value != 'undefined' ? this.value : '';
39987         }
39988         return Roo.form.ComboBox.superclass.getValue.call(this);
39989     },
39990
39991     /**
39992      * Clears any text/value currently set in the field
39993      */
39994     clearValue : function(){
39995         if(this.hiddenField){
39996             this.hiddenField.value = '';
39997         }
39998         this.value = '';
39999         this.setRawValue('');
40000         this.lastSelectionText = '';
40001         
40002     },
40003
40004     /**
40005      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
40006      * will be displayed in the field.  If the value does not match the data value of an existing item,
40007      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
40008      * Otherwise the field will be blank (although the value will still be set).
40009      * @param {String} value The value to match
40010      */
40011     setValue : function(v){
40012         var text = v;
40013         if(this.valueField){
40014             var r = this.findRecord(this.valueField, v);
40015             if(r){
40016                 text = r.data[this.displayField];
40017             }else if(this.valueNotFoundText !== undefined){
40018                 text = this.valueNotFoundText;
40019             }
40020         }
40021         this.lastSelectionText = text;
40022         if(this.hiddenField){
40023             this.hiddenField.value = v;
40024         }
40025         Roo.form.ComboBox.superclass.setValue.call(this, text);
40026         this.value = v;
40027     },
40028     /**
40029      * @property {Object} the last set data for the element
40030      */
40031     
40032     lastData : false,
40033     /**
40034      * Sets the value of the field based on a object which is related to the record format for the store.
40035      * @param {Object} value the value to set as. or false on reset?
40036      */
40037     setFromData : function(o){
40038         var dv = ''; // display value
40039         var vv = ''; // value value..
40040         this.lastData = o;
40041         if (this.displayField) {
40042             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
40043         } else {
40044             // this is an error condition!!!
40045             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
40046         }
40047         
40048         if(this.valueField){
40049             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
40050         }
40051         if(this.hiddenField){
40052             this.hiddenField.value = vv;
40053             
40054             this.lastSelectionText = dv;
40055             Roo.form.ComboBox.superclass.setValue.call(this, dv);
40056             this.value = vv;
40057             return;
40058         }
40059         // no hidden field.. - we store the value in 'value', but still display
40060         // display field!!!!
40061         this.lastSelectionText = dv;
40062         Roo.form.ComboBox.superclass.setValue.call(this, dv);
40063         this.value = vv;
40064         
40065         
40066     },
40067     // private
40068     reset : function(){
40069         // overridden so that last data is reset..
40070         this.setValue(this.resetValue);
40071         this.clearInvalid();
40072         this.lastData = false;
40073         if (this.view) {
40074             this.view.clearSelections();
40075         }
40076     },
40077     // private
40078     findRecord : function(prop, value){
40079         var record;
40080         if(this.store.getCount() > 0){
40081             this.store.each(function(r){
40082                 if(r.data[prop] == value){
40083                     record = r;
40084                     return false;
40085                 }
40086                 return true;
40087             });
40088         }
40089         return record;
40090     },
40091     
40092     getName: function()
40093     {
40094         // returns hidden if it's set..
40095         if (!this.rendered) {return ''};
40096         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40097         
40098     },
40099     // private
40100     onViewMove : function(e, t){
40101         this.inKeyMode = false;
40102     },
40103
40104     // private
40105     onViewOver : function(e, t){
40106         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
40107             return;
40108         }
40109         var item = this.view.findItemFromChild(t);
40110         if(item){
40111             var index = this.view.indexOf(item);
40112             this.select(index, false);
40113         }
40114     },
40115
40116     // private
40117     onViewClick : function(doFocus)
40118     {
40119         var index = this.view.getSelectedIndexes()[0];
40120         var r = this.store.getAt(index);
40121         if(r){
40122             this.onSelect(r, index);
40123         }
40124         if(doFocus !== false && !this.blockFocus){
40125             this.el.focus();
40126         }
40127     },
40128
40129     // private
40130     restrictHeight : function(){
40131         this.innerList.dom.style.height = '';
40132         var inner = this.innerList.dom;
40133         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
40134         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
40135         this.list.beginUpdate();
40136         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
40137         this.list.alignTo(this.el, this.listAlign);
40138         this.list.endUpdate();
40139     },
40140
40141     // private
40142     onEmptyResults : function(){
40143         this.collapse();
40144     },
40145
40146     /**
40147      * Returns true if the dropdown list is expanded, else false.
40148      */
40149     isExpanded : function(){
40150         return this.list.isVisible();
40151     },
40152
40153     /**
40154      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
40155      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40156      * @param {String} value The data value of the item to select
40157      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40158      * selected item if it is not currently in view (defaults to true)
40159      * @return {Boolean} True if the value matched an item in the list, else false
40160      */
40161     selectByValue : function(v, scrollIntoView){
40162         if(v !== undefined && v !== null){
40163             var r = this.findRecord(this.valueField || this.displayField, v);
40164             if(r){
40165                 this.select(this.store.indexOf(r), scrollIntoView);
40166                 return true;
40167             }
40168         }
40169         return false;
40170     },
40171
40172     /**
40173      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
40174      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40175      * @param {Number} index The zero-based index of the list item to select
40176      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40177      * selected item if it is not currently in view (defaults to true)
40178      */
40179     select : function(index, scrollIntoView){
40180         this.selectedIndex = index;
40181         this.view.select(index);
40182         if(scrollIntoView !== false){
40183             var el = this.view.getNode(index);
40184             if(el){
40185                 this.innerList.scrollChildIntoView(el, false);
40186             }
40187         }
40188     },
40189
40190     // private
40191     selectNext : function(){
40192         var ct = this.store.getCount();
40193         if(ct > 0){
40194             if(this.selectedIndex == -1){
40195                 this.select(0);
40196             }else if(this.selectedIndex < ct-1){
40197                 this.select(this.selectedIndex+1);
40198             }
40199         }
40200     },
40201
40202     // private
40203     selectPrev : function(){
40204         var ct = this.store.getCount();
40205         if(ct > 0){
40206             if(this.selectedIndex == -1){
40207                 this.select(0);
40208             }else if(this.selectedIndex != 0){
40209                 this.select(this.selectedIndex-1);
40210             }
40211         }
40212     },
40213
40214     // private
40215     onKeyUp : function(e){
40216         if(this.editable !== false && !e.isSpecialKey()){
40217             this.lastKey = e.getKey();
40218             this.dqTask.delay(this.queryDelay);
40219         }
40220     },
40221
40222     // private
40223     validateBlur : function(){
40224         return !this.list || !this.list.isVisible();   
40225     },
40226
40227     // private
40228     initQuery : function(){
40229         this.doQuery(this.getRawValue());
40230     },
40231
40232     // private
40233     doForce : function(){
40234         if(this.el.dom.value.length > 0){
40235             this.el.dom.value =
40236                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
40237              
40238         }
40239     },
40240
40241     /**
40242      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
40243      * query allowing the query action to be canceled if needed.
40244      * @param {String} query The SQL query to execute
40245      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
40246      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
40247      * saved in the current store (defaults to false)
40248      */
40249     doQuery : function(q, forceAll){
40250         if(q === undefined || q === null){
40251             q = '';
40252         }
40253         var qe = {
40254             query: q,
40255             forceAll: forceAll,
40256             combo: this,
40257             cancel:false
40258         };
40259         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
40260             return false;
40261         }
40262         q = qe.query;
40263         forceAll = qe.forceAll;
40264         if(forceAll === true || (q.length >= this.minChars)){
40265             if(this.lastQuery != q || this.alwaysQuery){
40266                 this.lastQuery = q;
40267                 if(this.mode == 'local'){
40268                     this.selectedIndex = -1;
40269                     if(forceAll){
40270                         this.store.clearFilter();
40271                     }else{
40272                         this.store.filter(this.displayField, q);
40273                     }
40274                     this.onLoad();
40275                 }else{
40276                     this.store.baseParams[this.queryParam] = q;
40277                     this.store.load({
40278                         params: this.getParams(q)
40279                     });
40280                     this.expand();
40281                 }
40282             }else{
40283                 this.selectedIndex = -1;
40284                 this.onLoad();   
40285             }
40286         }
40287     },
40288
40289     // private
40290     getParams : function(q){
40291         var p = {};
40292         //p[this.queryParam] = q;
40293         if(this.pageSize){
40294             p.start = 0;
40295             p.limit = this.pageSize;
40296         }
40297         return p;
40298     },
40299
40300     /**
40301      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40302      */
40303     collapse : function(){
40304         if(!this.isExpanded()){
40305             return;
40306         }
40307         this.list.hide();
40308         Roo.get(document).un('mousedown', this.collapseIf, this);
40309         Roo.get(document).un('mousewheel', this.collapseIf, this);
40310         if (!this.editable) {
40311             Roo.get(document).un('keydown', this.listKeyPress, this);
40312         }
40313         this.fireEvent('collapse', this);
40314     },
40315
40316     // private
40317     collapseIf : function(e){
40318         if(!e.within(this.wrap) && !e.within(this.list)){
40319             this.collapse();
40320         }
40321     },
40322
40323     /**
40324      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40325      */
40326     expand : function(){
40327         if(this.isExpanded() || !this.hasFocus){
40328             return;
40329         }
40330         this.list.alignTo(this.el, this.listAlign);
40331         this.list.show();
40332         Roo.get(document).on('mousedown', this.collapseIf, this);
40333         Roo.get(document).on('mousewheel', this.collapseIf, this);
40334         if (!this.editable) {
40335             Roo.get(document).on('keydown', this.listKeyPress, this);
40336         }
40337         
40338         this.fireEvent('expand', this);
40339     },
40340
40341     // private
40342     // Implements the default empty TriggerField.onTriggerClick function
40343     onTriggerClick : function(){
40344         if(this.disabled){
40345             return;
40346         }
40347         if(this.isExpanded()){
40348             this.collapse();
40349             if (!this.blockFocus) {
40350                 this.el.focus();
40351             }
40352             
40353         }else {
40354             this.hasFocus = true;
40355             if(this.triggerAction == 'all') {
40356                 this.doQuery(this.allQuery, true);
40357             } else {
40358                 this.doQuery(this.getRawValue());
40359             }
40360             if (!this.blockFocus) {
40361                 this.el.focus();
40362             }
40363         }
40364     },
40365     listKeyPress : function(e)
40366     {
40367         //Roo.log('listkeypress');
40368         // scroll to first matching element based on key pres..
40369         if (e.isSpecialKey()) {
40370             return false;
40371         }
40372         var k = String.fromCharCode(e.getKey()).toUpperCase();
40373         //Roo.log(k);
40374         var match  = false;
40375         var csel = this.view.getSelectedNodes();
40376         var cselitem = false;
40377         if (csel.length) {
40378             var ix = this.view.indexOf(csel[0]);
40379             cselitem  = this.store.getAt(ix);
40380             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40381                 cselitem = false;
40382             }
40383             
40384         }
40385         
40386         this.store.each(function(v) { 
40387             if (cselitem) {
40388                 // start at existing selection.
40389                 if (cselitem.id == v.id) {
40390                     cselitem = false;
40391                 }
40392                 return;
40393             }
40394                 
40395             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40396                 match = this.store.indexOf(v);
40397                 return false;
40398             }
40399         }, this);
40400         
40401         if (match === false) {
40402             return true; // no more action?
40403         }
40404         // scroll to?
40405         this.view.select(match);
40406         var sn = Roo.get(this.view.getSelectedNodes()[0]);
40407         sn.scrollIntoView(sn.dom.parentNode, false);
40408     }
40409
40410     /** 
40411     * @cfg {Boolean} grow 
40412     * @hide 
40413     */
40414     /** 
40415     * @cfg {Number} growMin 
40416     * @hide 
40417     */
40418     /** 
40419     * @cfg {Number} growMax 
40420     * @hide 
40421     */
40422     /**
40423      * @hide
40424      * @method autoSize
40425      */
40426 });/*
40427  * Copyright(c) 2010-2012, Roo J Solutions Limited
40428  *
40429  * Licence LGPL
40430  *
40431  */
40432
40433 /**
40434  * @class Roo.form.ComboBoxArray
40435  * @extends Roo.form.TextField
40436  * A facebook style adder... for lists of email / people / countries  etc...
40437  * pick multiple items from a combo box, and shows each one.
40438  *
40439  *  Fred [x]  Brian [x]  [Pick another |v]
40440  *
40441  *
40442  *  For this to work: it needs various extra information
40443  *    - normal combo problay has
40444  *      name, hiddenName
40445  *    + displayField, valueField
40446  *
40447  *    For our purpose...
40448  *
40449  *
40450  *   If we change from 'extends' to wrapping...
40451  *   
40452  *  
40453  *
40454  
40455  
40456  * @constructor
40457  * Create a new ComboBoxArray.
40458  * @param {Object} config Configuration options
40459  */
40460  
40461
40462 Roo.form.ComboBoxArray = function(config)
40463 {
40464     this.addEvents({
40465         /**
40466          * @event beforeremove
40467          * Fires before remove the value from the list
40468              * @param {Roo.form.ComboBoxArray} _self This combo box array
40469              * @param {Roo.form.ComboBoxArray.Item} item removed item
40470              */
40471         'beforeremove' : true,
40472         /**
40473          * @event remove
40474          * Fires when remove the value from the list
40475              * @param {Roo.form.ComboBoxArray} _self This combo box array
40476              * @param {Roo.form.ComboBoxArray.Item} item removed item
40477              */
40478         'remove' : true
40479         
40480         
40481     });
40482     
40483     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40484     
40485     this.items = new Roo.util.MixedCollection(false);
40486     
40487     // construct the child combo...
40488     
40489     
40490     
40491     
40492    
40493     
40494 }
40495
40496  
40497 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40498
40499     /**
40500      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40501      */
40502     
40503     lastData : false,
40504     
40505     // behavies liek a hiddne field
40506     inputType:      'hidden',
40507     /**
40508      * @cfg {Number} width The width of the box that displays the selected element
40509      */ 
40510     width:          300,
40511
40512     
40513     
40514     /**
40515      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40516      */
40517     name : false,
40518     /**
40519      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40520      */
40521     hiddenName : false,
40522     
40523     
40524     // private the array of items that are displayed..
40525     items  : false,
40526     // private - the hidden field el.
40527     hiddenEl : false,
40528     // private - the filed el..
40529     el : false,
40530     
40531     //validateValue : function() { return true; }, // all values are ok!
40532     //onAddClick: function() { },
40533     
40534     onRender : function(ct, position) 
40535     {
40536         
40537         // create the standard hidden element
40538         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40539         
40540         
40541         // give fake names to child combo;
40542         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40543         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40544         
40545         this.combo = Roo.factory(this.combo, Roo.form);
40546         this.combo.onRender(ct, position);
40547         if (typeof(this.combo.width) != 'undefined') {
40548             this.combo.onResize(this.combo.width,0);
40549         }
40550         
40551         this.combo.initEvents();
40552         
40553         // assigned so form know we need to do this..
40554         this.store          = this.combo.store;
40555         this.valueField     = this.combo.valueField;
40556         this.displayField   = this.combo.displayField ;
40557         
40558         
40559         this.combo.wrap.addClass('x-cbarray-grp');
40560         
40561         var cbwrap = this.combo.wrap.createChild(
40562             {tag: 'div', cls: 'x-cbarray-cb'},
40563             this.combo.el.dom
40564         );
40565         
40566              
40567         this.hiddenEl = this.combo.wrap.createChild({
40568             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40569         });
40570         this.el = this.combo.wrap.createChild({
40571             tag: 'input',  type:'hidden' , name: this.name, value : ''
40572         });
40573          //   this.el.dom.removeAttribute("name");
40574         
40575         
40576         this.outerWrap = this.combo.wrap;
40577         this.wrap = cbwrap;
40578         
40579         this.outerWrap.setWidth(this.width);
40580         this.outerWrap.dom.removeChild(this.el.dom);
40581         
40582         this.wrap.dom.appendChild(this.el.dom);
40583         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40584         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40585         
40586         this.combo.trigger.setStyle('position','relative');
40587         this.combo.trigger.setStyle('left', '0px');
40588         this.combo.trigger.setStyle('top', '2px');
40589         
40590         this.combo.el.setStyle('vertical-align', 'text-bottom');
40591         
40592         //this.trigger.setStyle('vertical-align', 'top');
40593         
40594         // this should use the code from combo really... on('add' ....)
40595         if (this.adder) {
40596             
40597         
40598             this.adder = this.outerWrap.createChild(
40599                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40600             var _t = this;
40601             this.adder.on('click', function(e) {
40602                 _t.fireEvent('adderclick', this, e);
40603             }, _t);
40604         }
40605         //var _t = this;
40606         //this.adder.on('click', this.onAddClick, _t);
40607         
40608         
40609         this.combo.on('select', function(cb, rec, ix) {
40610             this.addItem(rec.data);
40611             
40612             cb.setValue('');
40613             cb.el.dom.value = '';
40614             //cb.lastData = rec.data;
40615             // add to list
40616             
40617         }, this);
40618         
40619         
40620     },
40621     
40622     
40623     getName: function()
40624     {
40625         // returns hidden if it's set..
40626         if (!this.rendered) {return ''};
40627         return  this.hiddenName ? this.hiddenName : this.name;
40628         
40629     },
40630     
40631     
40632     onResize: function(w, h){
40633         
40634         return;
40635         // not sure if this is needed..
40636         //this.combo.onResize(w,h);
40637         
40638         if(typeof w != 'number'){
40639             // we do not handle it!?!?
40640             return;
40641         }
40642         var tw = this.combo.trigger.getWidth();
40643         tw += this.addicon ? this.addicon.getWidth() : 0;
40644         tw += this.editicon ? this.editicon.getWidth() : 0;
40645         var x = w - tw;
40646         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40647             
40648         this.combo.trigger.setStyle('left', '0px');
40649         
40650         if(this.list && this.listWidth === undefined){
40651             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40652             this.list.setWidth(lw);
40653             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40654         }
40655         
40656     
40657         
40658     },
40659     
40660     addItem: function(rec)
40661     {
40662         var valueField = this.combo.valueField;
40663         var displayField = this.combo.displayField;
40664         if (this.items.indexOfKey(rec[valueField]) > -1) {
40665             //console.log("GOT " + rec.data.id);
40666             return;
40667         }
40668         
40669         var x = new Roo.form.ComboBoxArray.Item({
40670             //id : rec[this.idField],
40671             data : rec,
40672             displayField : displayField ,
40673             tipField : displayField ,
40674             cb : this
40675         });
40676         // use the 
40677         this.items.add(rec[valueField],x);
40678         // add it before the element..
40679         this.updateHiddenEl();
40680         x.render(this.outerWrap, this.wrap.dom);
40681         // add the image handler..
40682     },
40683     
40684     updateHiddenEl : function()
40685     {
40686         this.validate();
40687         if (!this.hiddenEl) {
40688             return;
40689         }
40690         var ar = [];
40691         var idField = this.combo.valueField;
40692         
40693         this.items.each(function(f) {
40694             ar.push(f.data[idField]);
40695            
40696         });
40697         this.hiddenEl.dom.value = ar.join(',');
40698         this.validate();
40699     },
40700     
40701     reset : function()
40702     {
40703         this.items.clear();
40704         
40705         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
40706            el.remove();
40707         });
40708         
40709         this.el.dom.value = '';
40710         if (this.hiddenEl) {
40711             this.hiddenEl.dom.value = '';
40712         }
40713         
40714     },
40715     getValue: function()
40716     {
40717         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40718     },
40719     setValue: function(v) // not a valid action - must use addItems..
40720     {
40721          
40722         this.reset();
40723         
40724         
40725         
40726         if (this.store.isLocal && (typeof(v) == 'string')) {
40727             // then we can use the store to find the values..
40728             // comma seperated at present.. this needs to allow JSON based encoding..
40729             this.hiddenEl.value  = v;
40730             var v_ar = [];
40731             Roo.each(v.split(','), function(k) {
40732                 Roo.log("CHECK " + this.valueField + ',' + k);
40733                 var li = this.store.query(this.valueField, k);
40734                 if (!li.length) {
40735                     return;
40736                 }
40737                 var add = {};
40738                 add[this.valueField] = k;
40739                 add[this.displayField] = li.item(0).data[this.displayField];
40740                 
40741                 this.addItem(add);
40742             }, this) 
40743              
40744         }
40745         if (typeof(v) == 'object' ) {
40746             // then let's assume it's an array of objects..
40747             Roo.each(v, function(l) {
40748                 this.addItem(l);
40749             }, this);
40750              
40751         }
40752         
40753         
40754     },
40755     setFromData: function(v)
40756     {
40757         // this recieves an object, if setValues is called.
40758         this.reset();
40759         this.el.dom.value = v[this.displayField];
40760         this.hiddenEl.dom.value = v[this.valueField];
40761         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40762             return;
40763         }
40764         var kv = v[this.valueField];
40765         var dv = v[this.displayField];
40766         kv = typeof(kv) != 'string' ? '' : kv;
40767         dv = typeof(dv) != 'string' ? '' : dv;
40768         
40769         
40770         var keys = kv.split(',');
40771         var display = dv.split(',');
40772         for (var i = 0 ; i < keys.length; i++) {
40773             
40774             add = {};
40775             add[this.valueField] = keys[i];
40776             add[this.displayField] = display[i];
40777             this.addItem(add);
40778         }
40779       
40780         
40781     },
40782     
40783     /**
40784      * Validates the combox array value
40785      * @return {Boolean} True if the value is valid, else false
40786      */
40787     validate : function(){
40788         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40789             this.clearInvalid();
40790             return true;
40791         }
40792         return false;
40793     },
40794     
40795     validateValue : function(value){
40796         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40797         
40798     },
40799     
40800     /*@
40801      * overide
40802      * 
40803      */
40804     isDirty : function() {
40805         if(this.disabled) {
40806             return false;
40807         }
40808         
40809         try {
40810             var d = Roo.decode(String(this.originalValue));
40811         } catch (e) {
40812             return String(this.getValue()) !== String(this.originalValue);
40813         }
40814         
40815         var originalValue = [];
40816         
40817         for (var i = 0; i < d.length; i++){
40818             originalValue.push(d[i][this.valueField]);
40819         }
40820         
40821         return String(this.getValue()) !== String(originalValue.join(','));
40822         
40823     }
40824     
40825 });
40826
40827
40828
40829 /**
40830  * @class Roo.form.ComboBoxArray.Item
40831  * @extends Roo.BoxComponent
40832  * A selected item in the list
40833  *  Fred [x]  Brian [x]  [Pick another |v]
40834  * 
40835  * @constructor
40836  * Create a new item.
40837  * @param {Object} config Configuration options
40838  */
40839  
40840 Roo.form.ComboBoxArray.Item = function(config) {
40841     config.id = Roo.id();
40842     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40843 }
40844
40845 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40846     data : {},
40847     cb: false,
40848     displayField : false,
40849     tipField : false,
40850     
40851     
40852     defaultAutoCreate : {
40853         tag: 'div',
40854         cls: 'x-cbarray-item',
40855         cn : [ 
40856             { tag: 'div' },
40857             {
40858                 tag: 'img',
40859                 width:16,
40860                 height : 16,
40861                 src : Roo.BLANK_IMAGE_URL ,
40862                 align: 'center'
40863             }
40864         ]
40865         
40866     },
40867     
40868  
40869     onRender : function(ct, position)
40870     {
40871         Roo.form.Field.superclass.onRender.call(this, ct, position);
40872         
40873         if(!this.el){
40874             var cfg = this.getAutoCreate();
40875             this.el = ct.createChild(cfg, position);
40876         }
40877         
40878         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40879         
40880         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40881             this.cb.renderer(this.data) :
40882             String.format('{0}',this.data[this.displayField]);
40883         
40884             
40885         this.el.child('div').dom.setAttribute('qtip',
40886                         String.format('{0}',this.data[this.tipField])
40887         );
40888         
40889         this.el.child('img').on('click', this.remove, this);
40890         
40891     },
40892    
40893     remove : function()
40894     {
40895         if(this.cb.disabled){
40896             return;
40897         }
40898         
40899         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
40900             this.cb.items.remove(this);
40901             this.el.child('img').un('click', this.remove, this);
40902             this.el.remove();
40903             this.cb.updateHiddenEl();
40904
40905             this.cb.fireEvent('remove', this.cb, this);
40906         }
40907         
40908     }
40909 });/*
40910  * Based on:
40911  * Ext JS Library 1.1.1
40912  * Copyright(c) 2006-2007, Ext JS, LLC.
40913  *
40914  * Originally Released Under LGPL - original licence link has changed is not relivant.
40915  *
40916  * Fork - LGPL
40917  * <script type="text/javascript">
40918  */
40919 /**
40920  * @class Roo.form.Checkbox
40921  * @extends Roo.form.Field
40922  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40923  * @constructor
40924  * Creates a new Checkbox
40925  * @param {Object} config Configuration options
40926  */
40927 Roo.form.Checkbox = function(config){
40928     Roo.form.Checkbox.superclass.constructor.call(this, config);
40929     this.addEvents({
40930         /**
40931          * @event check
40932          * Fires when the checkbox is checked or unchecked.
40933              * @param {Roo.form.Checkbox} this This checkbox
40934              * @param {Boolean} checked The new checked value
40935              */
40936         check : true
40937     });
40938 };
40939
40940 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40941     /**
40942      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40943      */
40944     focusClass : undefined,
40945     /**
40946      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40947      */
40948     fieldClass: "x-form-field",
40949     /**
40950      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40951      */
40952     checked: false,
40953     /**
40954      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40955      * {tag: "input", type: "checkbox", autocomplete: "off"})
40956      */
40957     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40958     /**
40959      * @cfg {String} boxLabel The text that appears beside the checkbox
40960      */
40961     boxLabel : "",
40962     /**
40963      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40964      */  
40965     inputValue : '1',
40966     /**
40967      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40968      */
40969      valueOff: '0', // value when not checked..
40970
40971     actionMode : 'viewEl', 
40972     //
40973     // private
40974     itemCls : 'x-menu-check-item x-form-item',
40975     groupClass : 'x-menu-group-item',
40976     inputType : 'hidden',
40977     
40978     
40979     inSetChecked: false, // check that we are not calling self...
40980     
40981     inputElement: false, // real input element?
40982     basedOn: false, // ????
40983     
40984     isFormField: true, // not sure where this is needed!!!!
40985
40986     onResize : function(){
40987         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40988         if(!this.boxLabel){
40989             this.el.alignTo(this.wrap, 'c-c');
40990         }
40991     },
40992
40993     initEvents : function(){
40994         Roo.form.Checkbox.superclass.initEvents.call(this);
40995         this.el.on("click", this.onClick,  this);
40996         this.el.on("change", this.onClick,  this);
40997     },
40998
40999
41000     getResizeEl : function(){
41001         return this.wrap;
41002     },
41003
41004     getPositionEl : function(){
41005         return this.wrap;
41006     },
41007
41008     // private
41009     onRender : function(ct, position){
41010         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
41011         /*
41012         if(this.inputValue !== undefined){
41013             this.el.dom.value = this.inputValue;
41014         }
41015         */
41016         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
41017         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
41018         var viewEl = this.wrap.createChild({ 
41019             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
41020         this.viewEl = viewEl;   
41021         this.wrap.on('click', this.onClick,  this); 
41022         
41023         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
41024         this.el.on('propertychange', this.setFromHidden,  this);  //ie
41025         
41026         
41027         
41028         if(this.boxLabel){
41029             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
41030         //    viewEl.on('click', this.onClick,  this); 
41031         }
41032         //if(this.checked){
41033             this.setChecked(this.checked);
41034         //}else{
41035             //this.checked = this.el.dom;
41036         //}
41037
41038     },
41039
41040     // private
41041     initValue : Roo.emptyFn,
41042
41043     /**
41044      * Returns the checked state of the checkbox.
41045      * @return {Boolean} True if checked, else false
41046      */
41047     getValue : function(){
41048         if(this.el){
41049             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
41050         }
41051         return this.valueOff;
41052         
41053     },
41054
41055         // private
41056     onClick : function(){ 
41057         if (this.disabled) {
41058             return;
41059         }
41060         this.setChecked(!this.checked);
41061
41062         //if(this.el.dom.checked != this.checked){
41063         //    this.setValue(this.el.dom.checked);
41064        // }
41065     },
41066
41067     /**
41068      * Sets the checked state of the checkbox.
41069      * On is always based on a string comparison between inputValue and the param.
41070      * @param {Boolean/String} value - the value to set 
41071      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
41072      */
41073     setValue : function(v,suppressEvent){
41074         
41075         
41076         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
41077         //if(this.el && this.el.dom){
41078         //    this.el.dom.checked = this.checked;
41079         //    this.el.dom.defaultChecked = this.checked;
41080         //}
41081         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
41082         //this.fireEvent("check", this, this.checked);
41083     },
41084     // private..
41085     setChecked : function(state,suppressEvent)
41086     {
41087         if (this.inSetChecked) {
41088             this.checked = state;
41089             return;
41090         }
41091         
41092     
41093         if(this.wrap){
41094             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
41095         }
41096         this.checked = state;
41097         if(suppressEvent !== true){
41098             this.fireEvent('check', this, state);
41099         }
41100         this.inSetChecked = true;
41101         this.el.dom.value = state ? this.inputValue : this.valueOff;
41102         this.inSetChecked = false;
41103         
41104     },
41105     // handle setting of hidden value by some other method!!?!?
41106     setFromHidden: function()
41107     {
41108         if(!this.el){
41109             return;
41110         }
41111         //console.log("SET FROM HIDDEN");
41112         //alert('setFrom hidden');
41113         this.setValue(this.el.dom.value);
41114     },
41115     
41116     onDestroy : function()
41117     {
41118         if(this.viewEl){
41119             Roo.get(this.viewEl).remove();
41120         }
41121          
41122         Roo.form.Checkbox.superclass.onDestroy.call(this);
41123     },
41124     
41125     setBoxLabel : function(str)
41126     {
41127         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
41128     }
41129
41130 });/*
41131  * Based on:
41132  * Ext JS Library 1.1.1
41133  * Copyright(c) 2006-2007, Ext JS, LLC.
41134  *
41135  * Originally Released Under LGPL - original licence link has changed is not relivant.
41136  *
41137  * Fork - LGPL
41138  * <script type="text/javascript">
41139  */
41140  
41141 /**
41142  * @class Roo.form.Radio
41143  * @extends Roo.form.Checkbox
41144  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
41145  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
41146  * @constructor
41147  * Creates a new Radio
41148  * @param {Object} config Configuration options
41149  */
41150 Roo.form.Radio = function(){
41151     Roo.form.Radio.superclass.constructor.apply(this, arguments);
41152 };
41153 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
41154     inputType: 'radio',
41155
41156     /**
41157      * If this radio is part of a group, it will return the selected value
41158      * @return {String}
41159      */
41160     getGroupValue : function(){
41161         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
41162     },
41163     
41164     
41165     onRender : function(ct, position){
41166         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
41167         
41168         if(this.inputValue !== undefined){
41169             this.el.dom.value = this.inputValue;
41170         }
41171          
41172         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
41173         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
41174         //var viewEl = this.wrap.createChild({ 
41175         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
41176         //this.viewEl = viewEl;   
41177         //this.wrap.on('click', this.onClick,  this); 
41178         
41179         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
41180         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
41181         
41182         
41183         
41184         if(this.boxLabel){
41185             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
41186         //    viewEl.on('click', this.onClick,  this); 
41187         }
41188          if(this.checked){
41189             this.el.dom.checked =   'checked' ;
41190         }
41191          
41192     } 
41193     
41194     
41195 });//<script type="text/javascript">
41196
41197 /*
41198  * Based  Ext JS Library 1.1.1
41199  * Copyright(c) 2006-2007, Ext JS, LLC.
41200  * LGPL
41201  *
41202  */
41203  
41204 /**
41205  * @class Roo.HtmlEditorCore
41206  * @extends Roo.Component
41207  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
41208  *
41209  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
41210  */
41211
41212 Roo.HtmlEditorCore = function(config){
41213     
41214     
41215     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
41216     
41217     
41218     this.addEvents({
41219         /**
41220          * @event initialize
41221          * Fires when the editor is fully initialized (including the iframe)
41222          * @param {Roo.HtmlEditorCore} this
41223          */
41224         initialize: true,
41225         /**
41226          * @event activate
41227          * Fires when the editor is first receives the focus. Any insertion must wait
41228          * until after this event.
41229          * @param {Roo.HtmlEditorCore} this
41230          */
41231         activate: true,
41232          /**
41233          * @event beforesync
41234          * Fires before the textarea is updated with content from the editor iframe. Return false
41235          * to cancel the sync.
41236          * @param {Roo.HtmlEditorCore} this
41237          * @param {String} html
41238          */
41239         beforesync: true,
41240          /**
41241          * @event beforepush
41242          * Fires before the iframe editor is updated with content from the textarea. Return false
41243          * to cancel the push.
41244          * @param {Roo.HtmlEditorCore} this
41245          * @param {String} html
41246          */
41247         beforepush: true,
41248          /**
41249          * @event sync
41250          * Fires when the textarea is updated with content from the editor iframe.
41251          * @param {Roo.HtmlEditorCore} this
41252          * @param {String} html
41253          */
41254         sync: true,
41255          /**
41256          * @event push
41257          * Fires when the iframe editor is updated with content from the textarea.
41258          * @param {Roo.HtmlEditorCore} this
41259          * @param {String} html
41260          */
41261         push: true,
41262         
41263         /**
41264          * @event editorevent
41265          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41266          * @param {Roo.HtmlEditorCore} this
41267          */
41268         editorevent: true
41269         
41270     });
41271     
41272     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
41273     
41274     // defaults : white / black...
41275     this.applyBlacklists();
41276     
41277     
41278     
41279 };
41280
41281
41282 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41283
41284
41285      /**
41286      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41287      */
41288     
41289     owner : false,
41290     
41291      /**
41292      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41293      *                        Roo.resizable.
41294      */
41295     resizable : false,
41296      /**
41297      * @cfg {Number} height (in pixels)
41298      */   
41299     height: 300,
41300    /**
41301      * @cfg {Number} width (in pixels)
41302      */   
41303     width: 500,
41304     
41305     /**
41306      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41307      * 
41308      */
41309     stylesheets: false,
41310     
41311     // id of frame..
41312     frameId: false,
41313     
41314     // private properties
41315     validationEvent : false,
41316     deferHeight: true,
41317     initialized : false,
41318     activated : false,
41319     sourceEditMode : false,
41320     onFocus : Roo.emptyFn,
41321     iframePad:3,
41322     hideMode:'offsets',
41323     
41324     clearUp: true,
41325     
41326     // blacklist + whitelisted elements..
41327     black: false,
41328     white: false,
41329      
41330     
41331
41332     /**
41333      * Protected method that will not generally be called directly. It
41334      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41335      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41336      */
41337     getDocMarkup : function(){
41338         // body styles..
41339         var st = '';
41340         
41341         // inherit styels from page...?? 
41342         if (this.stylesheets === false) {
41343             
41344             Roo.get(document.head).select('style').each(function(node) {
41345                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41346             });
41347             
41348             Roo.get(document.head).select('link').each(function(node) { 
41349                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41350             });
41351             
41352         } else if (!this.stylesheets.length) {
41353                 // simple..
41354                 st = '<style type="text/css">' +
41355                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41356                    '</style>';
41357         } else { 
41358             
41359         }
41360         
41361         st +=  '<style type="text/css">' +
41362             'IMG { cursor: pointer } ' +
41363         '</style>';
41364
41365         
41366         return '<html><head>' + st  +
41367             //<style type="text/css">' +
41368             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41369             //'</style>' +
41370             ' </head><body class="roo-htmleditor-body"></body></html>';
41371     },
41372
41373     // private
41374     onRender : function(ct, position)
41375     {
41376         var _t = this;
41377         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41378         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41379         
41380         
41381         this.el.dom.style.border = '0 none';
41382         this.el.dom.setAttribute('tabIndex', -1);
41383         this.el.addClass('x-hidden hide');
41384         
41385         
41386         
41387         if(Roo.isIE){ // fix IE 1px bogus margin
41388             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41389         }
41390        
41391         
41392         this.frameId = Roo.id();
41393         
41394          
41395         
41396         var iframe = this.owner.wrap.createChild({
41397             tag: 'iframe',
41398             cls: 'form-control', // bootstrap..
41399             id: this.frameId,
41400             name: this.frameId,
41401             frameBorder : 'no',
41402             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41403         }, this.el
41404         );
41405         
41406         
41407         this.iframe = iframe.dom;
41408
41409          this.assignDocWin();
41410         
41411         this.doc.designMode = 'on';
41412        
41413         this.doc.open();
41414         this.doc.write(this.getDocMarkup());
41415         this.doc.close();
41416
41417         
41418         var task = { // must defer to wait for browser to be ready
41419             run : function(){
41420                 //console.log("run task?" + this.doc.readyState);
41421                 this.assignDocWin();
41422                 if(this.doc.body || this.doc.readyState == 'complete'){
41423                     try {
41424                         this.doc.designMode="on";
41425                     } catch (e) {
41426                         return;
41427                     }
41428                     Roo.TaskMgr.stop(task);
41429                     this.initEditor.defer(10, this);
41430                 }
41431             },
41432             interval : 10,
41433             duration: 10000,
41434             scope: this
41435         };
41436         Roo.TaskMgr.start(task);
41437
41438     },
41439
41440     // private
41441     onResize : function(w, h)
41442     {
41443          Roo.log('resize: ' +w + ',' + h );
41444         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41445         if(!this.iframe){
41446             return;
41447         }
41448         if(typeof w == 'number'){
41449             
41450             this.iframe.style.width = w + 'px';
41451         }
41452         if(typeof h == 'number'){
41453             
41454             this.iframe.style.height = h + 'px';
41455             if(this.doc){
41456                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41457             }
41458         }
41459         
41460     },
41461
41462     /**
41463      * Toggles the editor between standard and source edit mode.
41464      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41465      */
41466     toggleSourceEdit : function(sourceEditMode){
41467         
41468         this.sourceEditMode = sourceEditMode === true;
41469         
41470         if(this.sourceEditMode){
41471  
41472             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41473             
41474         }else{
41475             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41476             //this.iframe.className = '';
41477             this.deferFocus();
41478         }
41479         //this.setSize(this.owner.wrap.getSize());
41480         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41481     },
41482
41483     
41484   
41485
41486     /**
41487      * Protected method that will not generally be called directly. If you need/want
41488      * custom HTML cleanup, this is the method you should override.
41489      * @param {String} html The HTML to be cleaned
41490      * return {String} The cleaned HTML
41491      */
41492     cleanHtml : function(html){
41493         html = String(html);
41494         if(html.length > 5){
41495             if(Roo.isSafari){ // strip safari nonsense
41496                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41497             }
41498         }
41499         if(html == '&nbsp;'){
41500             html = '';
41501         }
41502         return html;
41503     },
41504
41505     /**
41506      * HTML Editor -> Textarea
41507      * Protected method that will not generally be called directly. Syncs the contents
41508      * of the editor iframe with the textarea.
41509      */
41510     syncValue : function(){
41511         if(this.initialized){
41512             var bd = (this.doc.body || this.doc.documentElement);
41513             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41514             var html = bd.innerHTML;
41515             if(Roo.isSafari){
41516                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41517                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41518                 if(m && m[1]){
41519                     html = '<div style="'+m[0]+'">' + html + '</div>';
41520                 }
41521             }
41522             html = this.cleanHtml(html);
41523             // fix up the special chars.. normaly like back quotes in word...
41524             // however we do not want to do this with chinese..
41525             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41526                 var cc = b.charCodeAt();
41527                 if (
41528                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41529                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41530                     (cc >= 0xf900 && cc < 0xfb00 )
41531                 ) {
41532                         return b;
41533                 }
41534                 return "&#"+cc+";" 
41535             });
41536             if(this.owner.fireEvent('beforesync', this, html) !== false){
41537                 this.el.dom.value = html;
41538                 this.owner.fireEvent('sync', this, html);
41539             }
41540         }
41541     },
41542
41543     /**
41544      * Protected method that will not generally be called directly. Pushes the value of the textarea
41545      * into the iframe editor.
41546      */
41547     pushValue : function(){
41548         if(this.initialized){
41549             var v = this.el.dom.value.trim();
41550             
41551 //            if(v.length < 1){
41552 //                v = '&#160;';
41553 //            }
41554             
41555             if(this.owner.fireEvent('beforepush', this, v) !== false){
41556                 var d = (this.doc.body || this.doc.documentElement);
41557                 d.innerHTML = v;
41558                 this.cleanUpPaste();
41559                 this.el.dom.value = d.innerHTML;
41560                 this.owner.fireEvent('push', this, v);
41561             }
41562         }
41563     },
41564
41565     // private
41566     deferFocus : function(){
41567         this.focus.defer(10, this);
41568     },
41569
41570     // doc'ed in Field
41571     focus : function(){
41572         if(this.win && !this.sourceEditMode){
41573             this.win.focus();
41574         }else{
41575             this.el.focus();
41576         }
41577     },
41578     
41579     assignDocWin: function()
41580     {
41581         var iframe = this.iframe;
41582         
41583          if(Roo.isIE){
41584             this.doc = iframe.contentWindow.document;
41585             this.win = iframe.contentWindow;
41586         } else {
41587 //            if (!Roo.get(this.frameId)) {
41588 //                return;
41589 //            }
41590 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41591 //            this.win = Roo.get(this.frameId).dom.contentWindow;
41592             
41593             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
41594                 return;
41595             }
41596             
41597             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41598             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
41599         }
41600     },
41601     
41602     // private
41603     initEditor : function(){
41604         //console.log("INIT EDITOR");
41605         this.assignDocWin();
41606         
41607         
41608         
41609         this.doc.designMode="on";
41610         this.doc.open();
41611         this.doc.write(this.getDocMarkup());
41612         this.doc.close();
41613         
41614         var dbody = (this.doc.body || this.doc.documentElement);
41615         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41616         // this copies styles from the containing element into thsi one..
41617         // not sure why we need all of this..
41618         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41619         
41620         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41621         //ss['background-attachment'] = 'fixed'; // w3c
41622         dbody.bgProperties = 'fixed'; // ie
41623         //Roo.DomHelper.applyStyles(dbody, ss);
41624         Roo.EventManager.on(this.doc, {
41625             //'mousedown': this.onEditorEvent,
41626             'mouseup': this.onEditorEvent,
41627             'dblclick': this.onEditorEvent,
41628             'click': this.onEditorEvent,
41629             'keyup': this.onEditorEvent,
41630             buffer:100,
41631             scope: this
41632         });
41633         if(Roo.isGecko){
41634             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41635         }
41636         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41637             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41638         }
41639         this.initialized = true;
41640
41641         this.owner.fireEvent('initialize', this);
41642         this.pushValue();
41643     },
41644
41645     // private
41646     onDestroy : function(){
41647         
41648         
41649         
41650         if(this.rendered){
41651             
41652             //for (var i =0; i < this.toolbars.length;i++) {
41653             //    // fixme - ask toolbars for heights?
41654             //    this.toolbars[i].onDestroy();
41655            // }
41656             
41657             //this.wrap.dom.innerHTML = '';
41658             //this.wrap.remove();
41659         }
41660     },
41661
41662     // private
41663     onFirstFocus : function(){
41664         
41665         this.assignDocWin();
41666         
41667         
41668         this.activated = true;
41669          
41670     
41671         if(Roo.isGecko){ // prevent silly gecko errors
41672             this.win.focus();
41673             var s = this.win.getSelection();
41674             if(!s.focusNode || s.focusNode.nodeType != 3){
41675                 var r = s.getRangeAt(0);
41676                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41677                 r.collapse(true);
41678                 this.deferFocus();
41679             }
41680             try{
41681                 this.execCmd('useCSS', true);
41682                 this.execCmd('styleWithCSS', false);
41683             }catch(e){}
41684         }
41685         this.owner.fireEvent('activate', this);
41686     },
41687
41688     // private
41689     adjustFont: function(btn){
41690         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41691         //if(Roo.isSafari){ // safari
41692         //    adjust *= 2;
41693        // }
41694         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41695         if(Roo.isSafari){ // safari
41696             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41697             v =  (v < 10) ? 10 : v;
41698             v =  (v > 48) ? 48 : v;
41699             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41700             
41701         }
41702         
41703         
41704         v = Math.max(1, v+adjust);
41705         
41706         this.execCmd('FontSize', v  );
41707     },
41708
41709     onEditorEvent : function(e)
41710     {
41711         this.owner.fireEvent('editorevent', this, e);
41712       //  this.updateToolbar();
41713         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41714     },
41715
41716     insertTag : function(tg)
41717     {
41718         // could be a bit smarter... -> wrap the current selected tRoo..
41719         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41720             
41721             range = this.createRange(this.getSelection());
41722             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41723             wrappingNode.appendChild(range.extractContents());
41724             range.insertNode(wrappingNode);
41725
41726             return;
41727             
41728             
41729             
41730         }
41731         this.execCmd("formatblock",   tg);
41732         
41733     },
41734     
41735     insertText : function(txt)
41736     {
41737         
41738         
41739         var range = this.createRange();
41740         range.deleteContents();
41741                //alert(Sender.getAttribute('label'));
41742                
41743         range.insertNode(this.doc.createTextNode(txt));
41744     } ,
41745     
41746      
41747
41748     /**
41749      * Executes a Midas editor command on the editor document and performs necessary focus and
41750      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41751      * @param {String} cmd The Midas command
41752      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41753      */
41754     relayCmd : function(cmd, value){
41755         this.win.focus();
41756         this.execCmd(cmd, value);
41757         this.owner.fireEvent('editorevent', this);
41758         //this.updateToolbar();
41759         this.owner.deferFocus();
41760     },
41761
41762     /**
41763      * Executes a Midas editor command directly on the editor document.
41764      * For visual commands, you should use {@link #relayCmd} instead.
41765      * <b>This should only be called after the editor is initialized.</b>
41766      * @param {String} cmd The Midas command
41767      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41768      */
41769     execCmd : function(cmd, value){
41770         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41771         this.syncValue();
41772     },
41773  
41774  
41775    
41776     /**
41777      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41778      * to insert tRoo.
41779      * @param {String} text | dom node.. 
41780      */
41781     insertAtCursor : function(text)
41782     {
41783         
41784         
41785         
41786         if(!this.activated){
41787             return;
41788         }
41789         /*
41790         if(Roo.isIE){
41791             this.win.focus();
41792             var r = this.doc.selection.createRange();
41793             if(r){
41794                 r.collapse(true);
41795                 r.pasteHTML(text);
41796                 this.syncValue();
41797                 this.deferFocus();
41798             
41799             }
41800             return;
41801         }
41802         */
41803         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41804             this.win.focus();
41805             
41806             
41807             // from jquery ui (MIT licenced)
41808             var range, node;
41809             var win = this.win;
41810             
41811             if (win.getSelection && win.getSelection().getRangeAt) {
41812                 range = win.getSelection().getRangeAt(0);
41813                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41814                 range.insertNode(node);
41815             } else if (win.document.selection && win.document.selection.createRange) {
41816                 // no firefox support
41817                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41818                 win.document.selection.createRange().pasteHTML(txt);
41819             } else {
41820                 // no firefox support
41821                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41822                 this.execCmd('InsertHTML', txt);
41823             } 
41824             
41825             this.syncValue();
41826             
41827             this.deferFocus();
41828         }
41829     },
41830  // private
41831     mozKeyPress : function(e){
41832         if(e.ctrlKey){
41833             var c = e.getCharCode(), cmd;
41834           
41835             if(c > 0){
41836                 c = String.fromCharCode(c).toLowerCase();
41837                 switch(c){
41838                     case 'b':
41839                         cmd = 'bold';
41840                         break;
41841                     case 'i':
41842                         cmd = 'italic';
41843                         break;
41844                     
41845                     case 'u':
41846                         cmd = 'underline';
41847                         break;
41848                     
41849                     case 'v':
41850                         this.cleanUpPaste.defer(100, this);
41851                         return;
41852                         
41853                 }
41854                 if(cmd){
41855                     this.win.focus();
41856                     this.execCmd(cmd);
41857                     this.deferFocus();
41858                     e.preventDefault();
41859                 }
41860                 
41861             }
41862         }
41863     },
41864
41865     // private
41866     fixKeys : function(){ // load time branching for fastest keydown performance
41867         if(Roo.isIE){
41868             return function(e){
41869                 var k = e.getKey(), r;
41870                 if(k == e.TAB){
41871                     e.stopEvent();
41872                     r = this.doc.selection.createRange();
41873                     if(r){
41874                         r.collapse(true);
41875                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41876                         this.deferFocus();
41877                     }
41878                     return;
41879                 }
41880                 
41881                 if(k == e.ENTER){
41882                     r = this.doc.selection.createRange();
41883                     if(r){
41884                         var target = r.parentElement();
41885                         if(!target || target.tagName.toLowerCase() != 'li'){
41886                             e.stopEvent();
41887                             r.pasteHTML('<br />');
41888                             r.collapse(false);
41889                             r.select();
41890                         }
41891                     }
41892                 }
41893                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41894                     this.cleanUpPaste.defer(100, this);
41895                     return;
41896                 }
41897                 
41898                 
41899             };
41900         }else if(Roo.isOpera){
41901             return function(e){
41902                 var k = e.getKey();
41903                 if(k == e.TAB){
41904                     e.stopEvent();
41905                     this.win.focus();
41906                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41907                     this.deferFocus();
41908                 }
41909                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41910                     this.cleanUpPaste.defer(100, this);
41911                     return;
41912                 }
41913                 
41914             };
41915         }else if(Roo.isSafari){
41916             return function(e){
41917                 var k = e.getKey();
41918                 
41919                 if(k == e.TAB){
41920                     e.stopEvent();
41921                     this.execCmd('InsertText','\t');
41922                     this.deferFocus();
41923                     return;
41924                 }
41925                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41926                     this.cleanUpPaste.defer(100, this);
41927                     return;
41928                 }
41929                 
41930              };
41931         }
41932     }(),
41933     
41934     getAllAncestors: function()
41935     {
41936         var p = this.getSelectedNode();
41937         var a = [];
41938         if (!p) {
41939             a.push(p); // push blank onto stack..
41940             p = this.getParentElement();
41941         }
41942         
41943         
41944         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41945             a.push(p);
41946             p = p.parentNode;
41947         }
41948         a.push(this.doc.body);
41949         return a;
41950     },
41951     lastSel : false,
41952     lastSelNode : false,
41953     
41954     
41955     getSelection : function() 
41956     {
41957         this.assignDocWin();
41958         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41959     },
41960     
41961     getSelectedNode: function() 
41962     {
41963         // this may only work on Gecko!!!
41964         
41965         // should we cache this!!!!
41966         
41967         
41968         
41969          
41970         var range = this.createRange(this.getSelection()).cloneRange();
41971         
41972         if (Roo.isIE) {
41973             var parent = range.parentElement();
41974             while (true) {
41975                 var testRange = range.duplicate();
41976                 testRange.moveToElementText(parent);
41977                 if (testRange.inRange(range)) {
41978                     break;
41979                 }
41980                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41981                     break;
41982                 }
41983                 parent = parent.parentElement;
41984             }
41985             return parent;
41986         }
41987         
41988         // is ancestor a text element.
41989         var ac =  range.commonAncestorContainer;
41990         if (ac.nodeType == 3) {
41991             ac = ac.parentNode;
41992         }
41993         
41994         var ar = ac.childNodes;
41995          
41996         var nodes = [];
41997         var other_nodes = [];
41998         var has_other_nodes = false;
41999         for (var i=0;i<ar.length;i++) {
42000             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
42001                 continue;
42002             }
42003             // fullly contained node.
42004             
42005             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
42006                 nodes.push(ar[i]);
42007                 continue;
42008             }
42009             
42010             // probably selected..
42011             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
42012                 other_nodes.push(ar[i]);
42013                 continue;
42014             }
42015             // outer..
42016             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
42017                 continue;
42018             }
42019             
42020             
42021             has_other_nodes = true;
42022         }
42023         if (!nodes.length && other_nodes.length) {
42024             nodes= other_nodes;
42025         }
42026         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
42027             return false;
42028         }
42029         
42030         return nodes[0];
42031     },
42032     createRange: function(sel)
42033     {
42034         // this has strange effects when using with 
42035         // top toolbar - not sure if it's a great idea.
42036         //this.editor.contentWindow.focus();
42037         if (typeof sel != "undefined") {
42038             try {
42039                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
42040             } catch(e) {
42041                 return this.doc.createRange();
42042             }
42043         } else {
42044             return this.doc.createRange();
42045         }
42046     },
42047     getParentElement: function()
42048     {
42049         
42050         this.assignDocWin();
42051         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
42052         
42053         var range = this.createRange(sel);
42054          
42055         try {
42056             var p = range.commonAncestorContainer;
42057             while (p.nodeType == 3) { // text node
42058                 p = p.parentNode;
42059             }
42060             return p;
42061         } catch (e) {
42062             return null;
42063         }
42064     
42065     },
42066     /***
42067      *
42068      * Range intersection.. the hard stuff...
42069      *  '-1' = before
42070      *  '0' = hits..
42071      *  '1' = after.
42072      *         [ -- selected range --- ]
42073      *   [fail]                        [fail]
42074      *
42075      *    basically..
42076      *      if end is before start or  hits it. fail.
42077      *      if start is after end or hits it fail.
42078      *
42079      *   if either hits (but other is outside. - then it's not 
42080      *   
42081      *    
42082      **/
42083     
42084     
42085     // @see http://www.thismuchiknow.co.uk/?p=64.
42086     rangeIntersectsNode : function(range, node)
42087     {
42088         var nodeRange = node.ownerDocument.createRange();
42089         try {
42090             nodeRange.selectNode(node);
42091         } catch (e) {
42092             nodeRange.selectNodeContents(node);
42093         }
42094     
42095         var rangeStartRange = range.cloneRange();
42096         rangeStartRange.collapse(true);
42097     
42098         var rangeEndRange = range.cloneRange();
42099         rangeEndRange.collapse(false);
42100     
42101         var nodeStartRange = nodeRange.cloneRange();
42102         nodeStartRange.collapse(true);
42103     
42104         var nodeEndRange = nodeRange.cloneRange();
42105         nodeEndRange.collapse(false);
42106     
42107         return rangeStartRange.compareBoundaryPoints(
42108                  Range.START_TO_START, nodeEndRange) == -1 &&
42109                rangeEndRange.compareBoundaryPoints(
42110                  Range.START_TO_START, nodeStartRange) == 1;
42111         
42112          
42113     },
42114     rangeCompareNode : function(range, node)
42115     {
42116         var nodeRange = node.ownerDocument.createRange();
42117         try {
42118             nodeRange.selectNode(node);
42119         } catch (e) {
42120             nodeRange.selectNodeContents(node);
42121         }
42122         
42123         
42124         range.collapse(true);
42125     
42126         nodeRange.collapse(true);
42127      
42128         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
42129         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
42130          
42131         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
42132         
42133         var nodeIsBefore   =  ss == 1;
42134         var nodeIsAfter    = ee == -1;
42135         
42136         if (nodeIsBefore && nodeIsAfter) {
42137             return 0; // outer
42138         }
42139         if (!nodeIsBefore && nodeIsAfter) {
42140             return 1; //right trailed.
42141         }
42142         
42143         if (nodeIsBefore && !nodeIsAfter) {
42144             return 2;  // left trailed.
42145         }
42146         // fully contined.
42147         return 3;
42148     },
42149
42150     // private? - in a new class?
42151     cleanUpPaste :  function()
42152     {
42153         // cleans up the whole document..
42154         Roo.log('cleanuppaste');
42155         
42156         this.cleanUpChildren(this.doc.body);
42157         var clean = this.cleanWordChars(this.doc.body.innerHTML);
42158         if (clean != this.doc.body.innerHTML) {
42159             this.doc.body.innerHTML = clean;
42160         }
42161         
42162     },
42163     
42164     cleanWordChars : function(input) {// change the chars to hex code
42165         var he = Roo.HtmlEditorCore;
42166         
42167         var output = input;
42168         Roo.each(he.swapCodes, function(sw) { 
42169             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
42170             
42171             output = output.replace(swapper, sw[1]);
42172         });
42173         
42174         return output;
42175     },
42176     
42177     
42178     cleanUpChildren : function (n)
42179     {
42180         if (!n.childNodes.length) {
42181             return;
42182         }
42183         for (var i = n.childNodes.length-1; i > -1 ; i--) {
42184            this.cleanUpChild(n.childNodes[i]);
42185         }
42186     },
42187     
42188     
42189         
42190     
42191     cleanUpChild : function (node)
42192     {
42193         var ed = this;
42194         //console.log(node);
42195         if (node.nodeName == "#text") {
42196             // clean up silly Windows -- stuff?
42197             return; 
42198         }
42199         if (node.nodeName == "#comment") {
42200             node.parentNode.removeChild(node);
42201             // clean up silly Windows -- stuff?
42202             return; 
42203         }
42204         var lcname = node.tagName.toLowerCase();
42205         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
42206         // whitelist of tags..
42207         
42208         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
42209             // remove node.
42210             node.parentNode.removeChild(node);
42211             return;
42212             
42213         }
42214         
42215         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
42216         
42217         // remove <a name=....> as rendering on yahoo mailer is borked with this.
42218         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
42219         
42220         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
42221         //    remove_keep_children = true;
42222         //}
42223         
42224         if (remove_keep_children) {
42225             this.cleanUpChildren(node);
42226             // inserts everything just before this node...
42227             while (node.childNodes.length) {
42228                 var cn = node.childNodes[0];
42229                 node.removeChild(cn);
42230                 node.parentNode.insertBefore(cn, node);
42231             }
42232             node.parentNode.removeChild(node);
42233             return;
42234         }
42235         
42236         if (!node.attributes || !node.attributes.length) {
42237             this.cleanUpChildren(node);
42238             return;
42239         }
42240         
42241         function cleanAttr(n,v)
42242         {
42243             
42244             if (v.match(/^\./) || v.match(/^\//)) {
42245                 return;
42246             }
42247             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
42248                 return;
42249             }
42250             if (v.match(/^#/)) {
42251                 return;
42252             }
42253 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
42254             node.removeAttribute(n);
42255             
42256         }
42257         
42258         var cwhite = this.cwhite;
42259         var cblack = this.cblack;
42260             
42261         function cleanStyle(n,v)
42262         {
42263             if (v.match(/expression/)) { //XSS?? should we even bother..
42264                 node.removeAttribute(n);
42265                 return;
42266             }
42267             
42268             var parts = v.split(/;/);
42269             var clean = [];
42270             
42271             Roo.each(parts, function(p) {
42272                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42273                 if (!p.length) {
42274                     return true;
42275                 }
42276                 var l = p.split(':').shift().replace(/\s+/g,'');
42277                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42278                 
42279                 if ( cwhite.length && cblack.indexOf(l) > -1) {
42280 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42281                     //node.removeAttribute(n);
42282                     return true;
42283                 }
42284                 //Roo.log()
42285                 // only allow 'c whitelisted system attributes'
42286                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42287 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42288                     //node.removeAttribute(n);
42289                     return true;
42290                 }
42291                 
42292                 
42293                  
42294                 
42295                 clean.push(p);
42296                 return true;
42297             });
42298             if (clean.length) { 
42299                 node.setAttribute(n, clean.join(';'));
42300             } else {
42301                 node.removeAttribute(n);
42302             }
42303             
42304         }
42305         
42306         
42307         for (var i = node.attributes.length-1; i > -1 ; i--) {
42308             var a = node.attributes[i];
42309             //console.log(a);
42310             
42311             if (a.name.toLowerCase().substr(0,2)=='on')  {
42312                 node.removeAttribute(a.name);
42313                 continue;
42314             }
42315             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42316                 node.removeAttribute(a.name);
42317                 continue;
42318             }
42319             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42320                 cleanAttr(a.name,a.value); // fixme..
42321                 continue;
42322             }
42323             if (a.name == 'style') {
42324                 cleanStyle(a.name,a.value);
42325                 continue;
42326             }
42327             /// clean up MS crap..
42328             // tecnically this should be a list of valid class'es..
42329             
42330             
42331             if (a.name == 'class') {
42332                 if (a.value.match(/^Mso/)) {
42333                     node.className = '';
42334                 }
42335                 
42336                 if (a.value.match(/body/)) {
42337                     node.className = '';
42338                 }
42339                 continue;
42340             }
42341             
42342             // style cleanup!?
42343             // class cleanup?
42344             
42345         }
42346         
42347         
42348         this.cleanUpChildren(node);
42349         
42350         
42351     },
42352     
42353     /**
42354      * Clean up MS wordisms...
42355      */
42356     cleanWord : function(node)
42357     {
42358         
42359         
42360         if (!node) {
42361             this.cleanWord(this.doc.body);
42362             return;
42363         }
42364         if (node.nodeName == "#text") {
42365             // clean up silly Windows -- stuff?
42366             return; 
42367         }
42368         if (node.nodeName == "#comment") {
42369             node.parentNode.removeChild(node);
42370             // clean up silly Windows -- stuff?
42371             return; 
42372         }
42373         
42374         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42375             node.parentNode.removeChild(node);
42376             return;
42377         }
42378         
42379         // remove - but keep children..
42380         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42381             while (node.childNodes.length) {
42382                 var cn = node.childNodes[0];
42383                 node.removeChild(cn);
42384                 node.parentNode.insertBefore(cn, node);
42385             }
42386             node.parentNode.removeChild(node);
42387             this.iterateChildren(node, this.cleanWord);
42388             return;
42389         }
42390         // clean styles
42391         if (node.className.length) {
42392             
42393             var cn = node.className.split(/\W+/);
42394             var cna = [];
42395             Roo.each(cn, function(cls) {
42396                 if (cls.match(/Mso[a-zA-Z]+/)) {
42397                     return;
42398                 }
42399                 cna.push(cls);
42400             });
42401             node.className = cna.length ? cna.join(' ') : '';
42402             if (!cna.length) {
42403                 node.removeAttribute("class");
42404             }
42405         }
42406         
42407         if (node.hasAttribute("lang")) {
42408             node.removeAttribute("lang");
42409         }
42410         
42411         if (node.hasAttribute("style")) {
42412             
42413             var styles = node.getAttribute("style").split(";");
42414             var nstyle = [];
42415             Roo.each(styles, function(s) {
42416                 if (!s.match(/:/)) {
42417                     return;
42418                 }
42419                 var kv = s.split(":");
42420                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42421                     return;
42422                 }
42423                 // what ever is left... we allow.
42424                 nstyle.push(s);
42425             });
42426             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42427             if (!nstyle.length) {
42428                 node.removeAttribute('style');
42429             }
42430         }
42431         this.iterateChildren(node, this.cleanWord);
42432         
42433         
42434         
42435     },
42436     /**
42437      * iterateChildren of a Node, calling fn each time, using this as the scole..
42438      * @param {DomNode} node node to iterate children of.
42439      * @param {Function} fn method of this class to call on each item.
42440      */
42441     iterateChildren : function(node, fn)
42442     {
42443         if (!node.childNodes.length) {
42444                 return;
42445         }
42446         for (var i = node.childNodes.length-1; i > -1 ; i--) {
42447            fn.call(this, node.childNodes[i])
42448         }
42449     },
42450     
42451     
42452     /**
42453      * cleanTableWidths.
42454      *
42455      * Quite often pasting from word etc.. results in tables with column and widths.
42456      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
42457      *
42458      */
42459     cleanTableWidths : function(node)
42460     {
42461          
42462          
42463         if (!node) {
42464             this.cleanTableWidths(this.doc.body);
42465             return;
42466         }
42467         
42468         // ignore list...
42469         if (node.nodeName == "#text" || node.nodeName == "#comment") {
42470             return; 
42471         }
42472         Roo.log(node.tagName);
42473         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
42474             this.iterateChildren(node, this.cleanTableWidths);
42475             return;
42476         }
42477         if (node.hasAttribute('width')) {
42478             node.removeAttribute('width');
42479         }
42480         
42481          
42482         if (node.hasAttribute("style")) {
42483             // pretty basic...
42484             
42485             var styles = node.getAttribute("style").split(";");
42486             var nstyle = [];
42487             Roo.each(styles, function(s) {
42488                 if (!s.match(/:/)) {
42489                     return;
42490                 }
42491                 var kv = s.split(":");
42492                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
42493                     return;
42494                 }
42495                 // what ever is left... we allow.
42496                 nstyle.push(s);
42497             });
42498             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42499             if (!nstyle.length) {
42500                 node.removeAttribute('style');
42501             }
42502         }
42503         
42504         this.iterateChildren(node, this.cleanTableWidths);
42505         
42506         
42507     },
42508     
42509     
42510     
42511     
42512     domToHTML : function(currentElement, depth, nopadtext) {
42513         
42514         depth = depth || 0;
42515         nopadtext = nopadtext || false;
42516     
42517         if (!currentElement) {
42518             return this.domToHTML(this.doc.body);
42519         }
42520         
42521         //Roo.log(currentElement);
42522         var j;
42523         var allText = false;
42524         var nodeName = currentElement.nodeName;
42525         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42526         
42527         if  (nodeName == '#text') {
42528             
42529             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
42530         }
42531         
42532         
42533         var ret = '';
42534         if (nodeName != 'BODY') {
42535              
42536             var i = 0;
42537             // Prints the node tagName, such as <A>, <IMG>, etc
42538             if (tagName) {
42539                 var attr = [];
42540                 for(i = 0; i < currentElement.attributes.length;i++) {
42541                     // quoting?
42542                     var aname = currentElement.attributes.item(i).name;
42543                     if (!currentElement.attributes.item(i).value.length) {
42544                         continue;
42545                     }
42546                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42547                 }
42548                 
42549                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42550             } 
42551             else {
42552                 
42553                 // eack
42554             }
42555         } else {
42556             tagName = false;
42557         }
42558         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42559             return ret;
42560         }
42561         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42562             nopadtext = true;
42563         }
42564         
42565         
42566         // Traverse the tree
42567         i = 0;
42568         var currentElementChild = currentElement.childNodes.item(i);
42569         var allText = true;
42570         var innerHTML  = '';
42571         lastnode = '';
42572         while (currentElementChild) {
42573             // Formatting code (indent the tree so it looks nice on the screen)
42574             var nopad = nopadtext;
42575             if (lastnode == 'SPAN') {
42576                 nopad  = true;
42577             }
42578             // text
42579             if  (currentElementChild.nodeName == '#text') {
42580                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42581                 toadd = nopadtext ? toadd : toadd.trim();
42582                 if (!nopad && toadd.length > 80) {
42583                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42584                 }
42585                 innerHTML  += toadd;
42586                 
42587                 i++;
42588                 currentElementChild = currentElement.childNodes.item(i);
42589                 lastNode = '';
42590                 continue;
42591             }
42592             allText = false;
42593             
42594             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42595                 
42596             // Recursively traverse the tree structure of the child node
42597             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42598             lastnode = currentElementChild.nodeName;
42599             i++;
42600             currentElementChild=currentElement.childNodes.item(i);
42601         }
42602         
42603         ret += innerHTML;
42604         
42605         if (!allText) {
42606                 // The remaining code is mostly for formatting the tree
42607             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42608         }
42609         
42610         
42611         if (tagName) {
42612             ret+= "</"+tagName+">";
42613         }
42614         return ret;
42615         
42616     },
42617         
42618     applyBlacklists : function()
42619     {
42620         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
42621         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
42622         
42623         this.white = [];
42624         this.black = [];
42625         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
42626             if (b.indexOf(tag) > -1) {
42627                 return;
42628             }
42629             this.white.push(tag);
42630             
42631         }, this);
42632         
42633         Roo.each(w, function(tag) {
42634             if (b.indexOf(tag) > -1) {
42635                 return;
42636             }
42637             if (this.white.indexOf(tag) > -1) {
42638                 return;
42639             }
42640             this.white.push(tag);
42641             
42642         }, this);
42643         
42644         
42645         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
42646             if (w.indexOf(tag) > -1) {
42647                 return;
42648             }
42649             this.black.push(tag);
42650             
42651         }, this);
42652         
42653         Roo.each(b, function(tag) {
42654             if (w.indexOf(tag) > -1) {
42655                 return;
42656             }
42657             if (this.black.indexOf(tag) > -1) {
42658                 return;
42659             }
42660             this.black.push(tag);
42661             
42662         }, this);
42663         
42664         
42665         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
42666         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
42667         
42668         this.cwhite = [];
42669         this.cblack = [];
42670         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
42671             if (b.indexOf(tag) > -1) {
42672                 return;
42673             }
42674             this.cwhite.push(tag);
42675             
42676         }, this);
42677         
42678         Roo.each(w, function(tag) {
42679             if (b.indexOf(tag) > -1) {
42680                 return;
42681             }
42682             if (this.cwhite.indexOf(tag) > -1) {
42683                 return;
42684             }
42685             this.cwhite.push(tag);
42686             
42687         }, this);
42688         
42689         
42690         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
42691             if (w.indexOf(tag) > -1) {
42692                 return;
42693             }
42694             this.cblack.push(tag);
42695             
42696         }, this);
42697         
42698         Roo.each(b, function(tag) {
42699             if (w.indexOf(tag) > -1) {
42700                 return;
42701             }
42702             if (this.cblack.indexOf(tag) > -1) {
42703                 return;
42704             }
42705             this.cblack.push(tag);
42706             
42707         }, this);
42708     },
42709     
42710     setStylesheets : function(stylesheets)
42711     {
42712         if(typeof(stylesheets) == 'string'){
42713             Roo.get(this.iframe.contentDocument.head).createChild({
42714                 tag : 'link',
42715                 rel : 'stylesheet',
42716                 type : 'text/css',
42717                 href : stylesheets
42718             });
42719             
42720             return;
42721         }
42722         var _this = this;
42723      
42724         Roo.each(stylesheets, function(s) {
42725             if(!s.length){
42726                 return;
42727             }
42728             
42729             Roo.get(_this.iframe.contentDocument.head).createChild({
42730                 tag : 'link',
42731                 rel : 'stylesheet',
42732                 type : 'text/css',
42733                 href : s
42734             });
42735         });
42736
42737         
42738     },
42739     
42740     removeStylesheets : function()
42741     {
42742         var _this = this;
42743         
42744         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
42745             s.remove();
42746         });
42747     }
42748     
42749     // hide stuff that is not compatible
42750     /**
42751      * @event blur
42752      * @hide
42753      */
42754     /**
42755      * @event change
42756      * @hide
42757      */
42758     /**
42759      * @event focus
42760      * @hide
42761      */
42762     /**
42763      * @event specialkey
42764      * @hide
42765      */
42766     /**
42767      * @cfg {String} fieldClass @hide
42768      */
42769     /**
42770      * @cfg {String} focusClass @hide
42771      */
42772     /**
42773      * @cfg {String} autoCreate @hide
42774      */
42775     /**
42776      * @cfg {String} inputType @hide
42777      */
42778     /**
42779      * @cfg {String} invalidClass @hide
42780      */
42781     /**
42782      * @cfg {String} invalidText @hide
42783      */
42784     /**
42785      * @cfg {String} msgFx @hide
42786      */
42787     /**
42788      * @cfg {String} validateOnBlur @hide
42789      */
42790 });
42791
42792 Roo.HtmlEditorCore.white = [
42793         'area', 'br', 'img', 'input', 'hr', 'wbr',
42794         
42795        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42796        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42797        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42798        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42799        'table',   'ul',         'xmp', 
42800        
42801        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42802       'thead',   'tr', 
42803      
42804       'dir', 'menu', 'ol', 'ul', 'dl',
42805        
42806       'embed',  'object'
42807 ];
42808
42809
42810 Roo.HtmlEditorCore.black = [
42811     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42812         'applet', // 
42813         'base',   'basefont', 'bgsound', 'blink',  'body', 
42814         'frame',  'frameset', 'head',    'html',   'ilayer', 
42815         'iframe', 'layer',  'link',     'meta',    'object',   
42816         'script', 'style' ,'title',  'xml' // clean later..
42817 ];
42818 Roo.HtmlEditorCore.clean = [
42819     'script', 'style', 'title', 'xml'
42820 ];
42821 Roo.HtmlEditorCore.remove = [
42822     'font'
42823 ];
42824 // attributes..
42825
42826 Roo.HtmlEditorCore.ablack = [
42827     'on'
42828 ];
42829     
42830 Roo.HtmlEditorCore.aclean = [ 
42831     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42832 ];
42833
42834 // protocols..
42835 Roo.HtmlEditorCore.pwhite= [
42836         'http',  'https',  'mailto'
42837 ];
42838
42839 // white listed style attributes.
42840 Roo.HtmlEditorCore.cwhite= [
42841       //  'text-align', /// default is to allow most things..
42842       
42843          
42844 //        'font-size'//??
42845 ];
42846
42847 // black listed style attributes.
42848 Roo.HtmlEditorCore.cblack= [
42849       //  'font-size' -- this can be set by the project 
42850 ];
42851
42852
42853 Roo.HtmlEditorCore.swapCodes   =[ 
42854     [    8211, "--" ], 
42855     [    8212, "--" ], 
42856     [    8216,  "'" ],  
42857     [    8217, "'" ],  
42858     [    8220, '"' ],  
42859     [    8221, '"' ],  
42860     [    8226, "*" ],  
42861     [    8230, "..." ]
42862 ]; 
42863
42864     //<script type="text/javascript">
42865
42866 /*
42867  * Ext JS Library 1.1.1
42868  * Copyright(c) 2006-2007, Ext JS, LLC.
42869  * Licence LGPL
42870  * 
42871  */
42872  
42873  
42874 Roo.form.HtmlEditor = function(config){
42875     
42876     
42877     
42878     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42879     
42880     if (!this.toolbars) {
42881         this.toolbars = [];
42882     }
42883     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42884     
42885     
42886 };
42887
42888 /**
42889  * @class Roo.form.HtmlEditor
42890  * @extends Roo.form.Field
42891  * Provides a lightweight HTML Editor component.
42892  *
42893  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42894  * 
42895  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42896  * supported by this editor.</b><br/><br/>
42897  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42898  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42899  */
42900 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42901     /**
42902      * @cfg {Boolean} clearUp
42903      */
42904     clearUp : true,
42905       /**
42906      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42907      */
42908     toolbars : false,
42909    
42910      /**
42911      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42912      *                        Roo.resizable.
42913      */
42914     resizable : false,
42915      /**
42916      * @cfg {Number} height (in pixels)
42917      */   
42918     height: 300,
42919    /**
42920      * @cfg {Number} width (in pixels)
42921      */   
42922     width: 500,
42923     
42924     /**
42925      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42926      * 
42927      */
42928     stylesheets: false,
42929     
42930     
42931      /**
42932      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
42933      * 
42934      */
42935     cblack: false,
42936     /**
42937      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
42938      * 
42939      */
42940     cwhite: false,
42941     
42942      /**
42943      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
42944      * 
42945      */
42946     black: false,
42947     /**
42948      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
42949      * 
42950      */
42951     white: false,
42952     
42953     // id of frame..
42954     frameId: false,
42955     
42956     // private properties
42957     validationEvent : false,
42958     deferHeight: true,
42959     initialized : false,
42960     activated : false,
42961     
42962     onFocus : Roo.emptyFn,
42963     iframePad:3,
42964     hideMode:'offsets',
42965     
42966     actionMode : 'container', // defaults to hiding it...
42967     
42968     defaultAutoCreate : { // modified by initCompnoent..
42969         tag: "textarea",
42970         style:"width:500px;height:300px;",
42971         autocomplete: "new-password"
42972     },
42973
42974     // private
42975     initComponent : function(){
42976         this.addEvents({
42977             /**
42978              * @event initialize
42979              * Fires when the editor is fully initialized (including the iframe)
42980              * @param {HtmlEditor} this
42981              */
42982             initialize: true,
42983             /**
42984              * @event activate
42985              * Fires when the editor is first receives the focus. Any insertion must wait
42986              * until after this event.
42987              * @param {HtmlEditor} this
42988              */
42989             activate: true,
42990              /**
42991              * @event beforesync
42992              * Fires before the textarea is updated with content from the editor iframe. Return false
42993              * to cancel the sync.
42994              * @param {HtmlEditor} this
42995              * @param {String} html
42996              */
42997             beforesync: true,
42998              /**
42999              * @event beforepush
43000              * Fires before the iframe editor is updated with content from the textarea. Return false
43001              * to cancel the push.
43002              * @param {HtmlEditor} this
43003              * @param {String} html
43004              */
43005             beforepush: true,
43006              /**
43007              * @event sync
43008              * Fires when the textarea is updated with content from the editor iframe.
43009              * @param {HtmlEditor} this
43010              * @param {String} html
43011              */
43012             sync: true,
43013              /**
43014              * @event push
43015              * Fires when the iframe editor is updated with content from the textarea.
43016              * @param {HtmlEditor} this
43017              * @param {String} html
43018              */
43019             push: true,
43020              /**
43021              * @event editmodechange
43022              * Fires when the editor switches edit modes
43023              * @param {HtmlEditor} this
43024              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
43025              */
43026             editmodechange: true,
43027             /**
43028              * @event editorevent
43029              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
43030              * @param {HtmlEditor} this
43031              */
43032             editorevent: true,
43033             /**
43034              * @event firstfocus
43035              * Fires when on first focus - needed by toolbars..
43036              * @param {HtmlEditor} this
43037              */
43038             firstfocus: true,
43039             /**
43040              * @event autosave
43041              * Auto save the htmlEditor value as a file into Events
43042              * @param {HtmlEditor} this
43043              */
43044             autosave: true,
43045             /**
43046              * @event savedpreview
43047              * preview the saved version of htmlEditor
43048              * @param {HtmlEditor} this
43049              */
43050             savedpreview: true,
43051             
43052             /**
43053             * @event stylesheetsclick
43054             * Fires when press the Sytlesheets button
43055             * @param {Roo.HtmlEditorCore} this
43056             */
43057             stylesheetsclick: true
43058         });
43059         this.defaultAutoCreate =  {
43060             tag: "textarea",
43061             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
43062             autocomplete: "new-password"
43063         };
43064     },
43065
43066     /**
43067      * Protected method that will not generally be called directly. It
43068      * is called when the editor creates its toolbar. Override this method if you need to
43069      * add custom toolbar buttons.
43070      * @param {HtmlEditor} editor
43071      */
43072     createToolbar : function(editor){
43073         Roo.log("create toolbars");
43074         if (!editor.toolbars || !editor.toolbars.length) {
43075             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
43076         }
43077         
43078         for (var i =0 ; i < editor.toolbars.length;i++) {
43079             editor.toolbars[i] = Roo.factory(
43080                     typeof(editor.toolbars[i]) == 'string' ?
43081                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
43082                 Roo.form.HtmlEditor);
43083             editor.toolbars[i].init(editor);
43084         }
43085          
43086         
43087     },
43088
43089      
43090     // private
43091     onRender : function(ct, position)
43092     {
43093         var _t = this;
43094         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
43095         
43096         this.wrap = this.el.wrap({
43097             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
43098         });
43099         
43100         this.editorcore.onRender(ct, position);
43101          
43102         if (this.resizable) {
43103             this.resizeEl = new Roo.Resizable(this.wrap, {
43104                 pinned : true,
43105                 wrap: true,
43106                 dynamic : true,
43107                 minHeight : this.height,
43108                 height: this.height,
43109                 handles : this.resizable,
43110                 width: this.width,
43111                 listeners : {
43112                     resize : function(r, w, h) {
43113                         _t.onResize(w,h); // -something
43114                     }
43115                 }
43116             });
43117             
43118         }
43119         this.createToolbar(this);
43120        
43121         
43122         if(!this.width){
43123             this.setSize(this.wrap.getSize());
43124         }
43125         if (this.resizeEl) {
43126             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
43127             // should trigger onReize..
43128         }
43129         
43130         this.keyNav = new Roo.KeyNav(this.el, {
43131             
43132             "tab" : function(e){
43133                 e.preventDefault();
43134                 
43135                 var value = this.getValue();
43136                 
43137                 var start = this.el.dom.selectionStart;
43138                 var end = this.el.dom.selectionEnd;
43139                 
43140                 if(!e.shiftKey){
43141                     
43142                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
43143                     this.el.dom.setSelectionRange(end + 1, end + 1);
43144                     return;
43145                 }
43146                 
43147                 var f = value.substring(0, start).split("\t");
43148                 
43149                 if(f.pop().length != 0){
43150                     return;
43151                 }
43152                 
43153                 this.setValue(f.join("\t") + value.substring(end));
43154                 this.el.dom.setSelectionRange(start - 1, start - 1);
43155                 
43156             },
43157             
43158             "home" : function(e){
43159                 e.preventDefault();
43160                 
43161                 var curr = this.el.dom.selectionStart;
43162                 var lines = this.getValue().split("\n");
43163                 
43164                 if(!lines.length){
43165                     return;
43166                 }
43167                 
43168                 if(e.ctrlKey){
43169                     this.el.dom.setSelectionRange(0, 0);
43170                     return;
43171                 }
43172                 
43173                 var pos = 0;
43174                 
43175                 for (var i = 0; i < lines.length;i++) {
43176                     pos += lines[i].length;
43177                     
43178                     if(i != 0){
43179                         pos += 1;
43180                     }
43181                     
43182                     if(pos < curr){
43183                         continue;
43184                     }
43185                     
43186                     pos -= lines[i].length;
43187                     
43188                     break;
43189                 }
43190                 
43191                 if(!e.shiftKey){
43192                     this.el.dom.setSelectionRange(pos, pos);
43193                     return;
43194                 }
43195                 
43196                 this.el.dom.selectionStart = pos;
43197                 this.el.dom.selectionEnd = curr;
43198             },
43199             
43200             "end" : function(e){
43201                 e.preventDefault();
43202                 
43203                 var curr = this.el.dom.selectionStart;
43204                 var lines = this.getValue().split("\n");
43205                 
43206                 if(!lines.length){
43207                     return;
43208                 }
43209                 
43210                 if(e.ctrlKey){
43211                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
43212                     return;
43213                 }
43214                 
43215                 var pos = 0;
43216                 
43217                 for (var i = 0; i < lines.length;i++) {
43218                     
43219                     pos += lines[i].length;
43220                     
43221                     if(i != 0){
43222                         pos += 1;
43223                     }
43224                     
43225                     if(pos < curr){
43226                         continue;
43227                     }
43228                     
43229                     break;
43230                 }
43231                 
43232                 if(!e.shiftKey){
43233                     this.el.dom.setSelectionRange(pos, pos);
43234                     return;
43235                 }
43236                 
43237                 this.el.dom.selectionStart = curr;
43238                 this.el.dom.selectionEnd = pos;
43239             },
43240
43241             scope : this,
43242
43243             doRelay : function(foo, bar, hname){
43244                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43245             },
43246
43247             forceKeyDown: true
43248         });
43249         
43250 //        if(this.autosave && this.w){
43251 //            this.autoSaveFn = setInterval(this.autosave, 1000);
43252 //        }
43253     },
43254
43255     // private
43256     onResize : function(w, h)
43257     {
43258         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
43259         var ew = false;
43260         var eh = false;
43261         
43262         if(this.el ){
43263             if(typeof w == 'number'){
43264                 var aw = w - this.wrap.getFrameWidth('lr');
43265                 this.el.setWidth(this.adjustWidth('textarea', aw));
43266                 ew = aw;
43267             }
43268             if(typeof h == 'number'){
43269                 var tbh = 0;
43270                 for (var i =0; i < this.toolbars.length;i++) {
43271                     // fixme - ask toolbars for heights?
43272                     tbh += this.toolbars[i].tb.el.getHeight();
43273                     if (this.toolbars[i].footer) {
43274                         tbh += this.toolbars[i].footer.el.getHeight();
43275                     }
43276                 }
43277                 
43278                 
43279                 
43280                 
43281                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
43282                 ah -= 5; // knock a few pixes off for look..
43283 //                Roo.log(ah);
43284                 this.el.setHeight(this.adjustWidth('textarea', ah));
43285                 var eh = ah;
43286             }
43287         }
43288         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
43289         this.editorcore.onResize(ew,eh);
43290         
43291     },
43292
43293     /**
43294      * Toggles the editor between standard and source edit mode.
43295      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43296      */
43297     toggleSourceEdit : function(sourceEditMode)
43298     {
43299         this.editorcore.toggleSourceEdit(sourceEditMode);
43300         
43301         if(this.editorcore.sourceEditMode){
43302             Roo.log('editor - showing textarea');
43303             
43304 //            Roo.log('in');
43305 //            Roo.log(this.syncValue());
43306             this.editorcore.syncValue();
43307             this.el.removeClass('x-hidden');
43308             this.el.dom.removeAttribute('tabIndex');
43309             this.el.focus();
43310             
43311             for (var i = 0; i < this.toolbars.length; i++) {
43312                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43313                     this.toolbars[i].tb.hide();
43314                     this.toolbars[i].footer.hide();
43315                 }
43316             }
43317             
43318         }else{
43319             Roo.log('editor - hiding textarea');
43320 //            Roo.log('out')
43321 //            Roo.log(this.pushValue()); 
43322             this.editorcore.pushValue();
43323             
43324             this.el.addClass('x-hidden');
43325             this.el.dom.setAttribute('tabIndex', -1);
43326             
43327             for (var i = 0; i < this.toolbars.length; i++) {
43328                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43329                     this.toolbars[i].tb.show();
43330                     this.toolbars[i].footer.show();
43331                 }
43332             }
43333             
43334             //this.deferFocus();
43335         }
43336         
43337         this.setSize(this.wrap.getSize());
43338         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
43339         
43340         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
43341     },
43342  
43343     // private (for BoxComponent)
43344     adjustSize : Roo.BoxComponent.prototype.adjustSize,
43345
43346     // private (for BoxComponent)
43347     getResizeEl : function(){
43348         return this.wrap;
43349     },
43350
43351     // private (for BoxComponent)
43352     getPositionEl : function(){
43353         return this.wrap;
43354     },
43355
43356     // private
43357     initEvents : function(){
43358         this.originalValue = this.getValue();
43359     },
43360
43361     /**
43362      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43363      * @method
43364      */
43365     markInvalid : Roo.emptyFn,
43366     /**
43367      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43368      * @method
43369      */
43370     clearInvalid : Roo.emptyFn,
43371
43372     setValue : function(v){
43373         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
43374         this.editorcore.pushValue();
43375     },
43376
43377      
43378     // private
43379     deferFocus : function(){
43380         this.focus.defer(10, this);
43381     },
43382
43383     // doc'ed in Field
43384     focus : function(){
43385         this.editorcore.focus();
43386         
43387     },
43388       
43389
43390     // private
43391     onDestroy : function(){
43392         
43393         
43394         
43395         if(this.rendered){
43396             
43397             for (var i =0; i < this.toolbars.length;i++) {
43398                 // fixme - ask toolbars for heights?
43399                 this.toolbars[i].onDestroy();
43400             }
43401             
43402             this.wrap.dom.innerHTML = '';
43403             this.wrap.remove();
43404         }
43405     },
43406
43407     // private
43408     onFirstFocus : function(){
43409         //Roo.log("onFirstFocus");
43410         this.editorcore.onFirstFocus();
43411          for (var i =0; i < this.toolbars.length;i++) {
43412             this.toolbars[i].onFirstFocus();
43413         }
43414         
43415     },
43416     
43417     // private
43418     syncValue : function()
43419     {
43420         this.editorcore.syncValue();
43421     },
43422     
43423     pushValue : function()
43424     {
43425         this.editorcore.pushValue();
43426     },
43427     
43428     setStylesheets : function(stylesheets)
43429     {
43430         this.editorcore.setStylesheets(stylesheets);
43431     },
43432     
43433     removeStylesheets : function()
43434     {
43435         this.editorcore.removeStylesheets();
43436     }
43437      
43438     
43439     // hide stuff that is not compatible
43440     /**
43441      * @event blur
43442      * @hide
43443      */
43444     /**
43445      * @event change
43446      * @hide
43447      */
43448     /**
43449      * @event focus
43450      * @hide
43451      */
43452     /**
43453      * @event specialkey
43454      * @hide
43455      */
43456     /**
43457      * @cfg {String} fieldClass @hide
43458      */
43459     /**
43460      * @cfg {String} focusClass @hide
43461      */
43462     /**
43463      * @cfg {String} autoCreate @hide
43464      */
43465     /**
43466      * @cfg {String} inputType @hide
43467      */
43468     /**
43469      * @cfg {String} invalidClass @hide
43470      */
43471     /**
43472      * @cfg {String} invalidText @hide
43473      */
43474     /**
43475      * @cfg {String} msgFx @hide
43476      */
43477     /**
43478      * @cfg {String} validateOnBlur @hide
43479      */
43480 });
43481  
43482     // <script type="text/javascript">
43483 /*
43484  * Based on
43485  * Ext JS Library 1.1.1
43486  * Copyright(c) 2006-2007, Ext JS, LLC.
43487  *  
43488  
43489  */
43490
43491 /**
43492  * @class Roo.form.HtmlEditorToolbar1
43493  * Basic Toolbar
43494  * 
43495  * Usage:
43496  *
43497  new Roo.form.HtmlEditor({
43498     ....
43499     toolbars : [
43500         new Roo.form.HtmlEditorToolbar1({
43501             disable : { fonts: 1 , format: 1, ..., ... , ...],
43502             btns : [ .... ]
43503         })
43504     }
43505      
43506  * 
43507  * @cfg {Object} disable List of elements to disable..
43508  * @cfg {Array} btns List of additional buttons.
43509  * 
43510  * 
43511  * NEEDS Extra CSS? 
43512  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
43513  */
43514  
43515 Roo.form.HtmlEditor.ToolbarStandard = function(config)
43516 {
43517     
43518     Roo.apply(this, config);
43519     
43520     // default disabled, based on 'good practice'..
43521     this.disable = this.disable || {};
43522     Roo.applyIf(this.disable, {
43523         fontSize : true,
43524         colors : true,
43525         specialElements : true
43526     });
43527     
43528     
43529     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43530     // dont call parent... till later.
43531 }
43532
43533 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
43534     
43535     tb: false,
43536     
43537     rendered: false,
43538     
43539     editor : false,
43540     editorcore : false,
43541     /**
43542      * @cfg {Object} disable  List of toolbar elements to disable
43543          
43544      */
43545     disable : false,
43546     
43547     
43548      /**
43549      * @cfg {String} createLinkText The default text for the create link prompt
43550      */
43551     createLinkText : 'Please enter the URL for the link:',
43552     /**
43553      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
43554      */
43555     defaultLinkValue : 'http:/'+'/',
43556    
43557     
43558       /**
43559      * @cfg {Array} fontFamilies An array of available font families
43560      */
43561     fontFamilies : [
43562         'Arial',
43563         'Courier New',
43564         'Tahoma',
43565         'Times New Roman',
43566         'Verdana'
43567     ],
43568     
43569     specialChars : [
43570            "&#169;",
43571           "&#174;",     
43572           "&#8482;",    
43573           "&#163;" ,    
43574          // "&#8212;",    
43575           "&#8230;",    
43576           "&#247;" ,    
43577         //  "&#225;" ,     ?? a acute?
43578            "&#8364;"    , //Euro
43579        //   "&#8220;"    ,
43580         //  "&#8221;"    ,
43581         //  "&#8226;"    ,
43582           "&#176;"  //   , // degrees
43583
43584          // "&#233;"     , // e ecute
43585          // "&#250;"     , // u ecute?
43586     ],
43587     
43588     specialElements : [
43589         {
43590             text: "Insert Table",
43591             xtype: 'MenuItem',
43592             xns : Roo.Menu,
43593             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
43594                 
43595         },
43596         {    
43597             text: "Insert Image",
43598             xtype: 'MenuItem',
43599             xns : Roo.Menu,
43600             ihtml : '<img src="about:blank"/>'
43601             
43602         }
43603         
43604          
43605     ],
43606     
43607     
43608     inputElements : [ 
43609             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
43610             "input:submit", "input:button", "select", "textarea", "label" ],
43611     formats : [
43612         ["p"] ,  
43613         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
43614         ["pre"],[ "code"], 
43615         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
43616         ['div'],['span']
43617     ],
43618     
43619     cleanStyles : [
43620         "font-size"
43621     ],
43622      /**
43623      * @cfg {String} defaultFont default font to use.
43624      */
43625     defaultFont: 'tahoma',
43626    
43627     fontSelect : false,
43628     
43629     
43630     formatCombo : false,
43631     
43632     init : function(editor)
43633     {
43634         this.editor = editor;
43635         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43636         var editorcore = this.editorcore;
43637         
43638         var _t = this;
43639         
43640         var fid = editorcore.frameId;
43641         var etb = this;
43642         function btn(id, toggle, handler){
43643             var xid = fid + '-'+ id ;
43644             return {
43645                 id : xid,
43646                 cmd : id,
43647                 cls : 'x-btn-icon x-edit-'+id,
43648                 enableToggle:toggle !== false,
43649                 scope: _t, // was editor...
43650                 handler:handler||_t.relayBtnCmd,
43651                 clickEvent:'mousedown',
43652                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43653                 tabIndex:-1
43654             };
43655         }
43656         
43657         
43658         
43659         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43660         this.tb = tb;
43661          // stop form submits
43662         tb.el.on('click', function(e){
43663             e.preventDefault(); // what does this do?
43664         });
43665
43666         if(!this.disable.font) { // && !Roo.isSafari){
43667             /* why no safari for fonts 
43668             editor.fontSelect = tb.el.createChild({
43669                 tag:'select',
43670                 tabIndex: -1,
43671                 cls:'x-font-select',
43672                 html: this.createFontOptions()
43673             });
43674             
43675             editor.fontSelect.on('change', function(){
43676                 var font = editor.fontSelect.dom.value;
43677                 editor.relayCmd('fontname', font);
43678                 editor.deferFocus();
43679             }, editor);
43680             
43681             tb.add(
43682                 editor.fontSelect.dom,
43683                 '-'
43684             );
43685             */
43686             
43687         };
43688         if(!this.disable.formats){
43689             this.formatCombo = new Roo.form.ComboBox({
43690                 store: new Roo.data.SimpleStore({
43691                     id : 'tag',
43692                     fields: ['tag'],
43693                     data : this.formats // from states.js
43694                 }),
43695                 blockFocus : true,
43696                 name : '',
43697                 //autoCreate : {tag: "div",  size: "20"},
43698                 displayField:'tag',
43699                 typeAhead: false,
43700                 mode: 'local',
43701                 editable : false,
43702                 triggerAction: 'all',
43703                 emptyText:'Add tag',
43704                 selectOnFocus:true,
43705                 width:135,
43706                 listeners : {
43707                     'select': function(c, r, i) {
43708                         editorcore.insertTag(r.get('tag'));
43709                         editor.focus();
43710                     }
43711                 }
43712
43713             });
43714             tb.addField(this.formatCombo);
43715             
43716         }
43717         
43718         if(!this.disable.format){
43719             tb.add(
43720                 btn('bold'),
43721                 btn('italic'),
43722                 btn('underline'),
43723                 btn('strikethrough')
43724             );
43725         };
43726         if(!this.disable.fontSize){
43727             tb.add(
43728                 '-',
43729                 
43730                 
43731                 btn('increasefontsize', false, editorcore.adjustFont),
43732                 btn('decreasefontsize', false, editorcore.adjustFont)
43733             );
43734         };
43735         
43736         
43737         if(!this.disable.colors){
43738             tb.add(
43739                 '-', {
43740                     id:editorcore.frameId +'-forecolor',
43741                     cls:'x-btn-icon x-edit-forecolor',
43742                     clickEvent:'mousedown',
43743                     tooltip: this.buttonTips['forecolor'] || undefined,
43744                     tabIndex:-1,
43745                     menu : new Roo.menu.ColorMenu({
43746                         allowReselect: true,
43747                         focus: Roo.emptyFn,
43748                         value:'000000',
43749                         plain:true,
43750                         selectHandler: function(cp, color){
43751                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43752                             editor.deferFocus();
43753                         },
43754                         scope: editorcore,
43755                         clickEvent:'mousedown'
43756                     })
43757                 }, {
43758                     id:editorcore.frameId +'backcolor',
43759                     cls:'x-btn-icon x-edit-backcolor',
43760                     clickEvent:'mousedown',
43761                     tooltip: this.buttonTips['backcolor'] || undefined,
43762                     tabIndex:-1,
43763                     menu : new Roo.menu.ColorMenu({
43764                         focus: Roo.emptyFn,
43765                         value:'FFFFFF',
43766                         plain:true,
43767                         allowReselect: true,
43768                         selectHandler: function(cp, color){
43769                             if(Roo.isGecko){
43770                                 editorcore.execCmd('useCSS', false);
43771                                 editorcore.execCmd('hilitecolor', color);
43772                                 editorcore.execCmd('useCSS', true);
43773                                 editor.deferFocus();
43774                             }else{
43775                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43776                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43777                                 editor.deferFocus();
43778                             }
43779                         },
43780                         scope:editorcore,
43781                         clickEvent:'mousedown'
43782                     })
43783                 }
43784             );
43785         };
43786         // now add all the items...
43787         
43788
43789         if(!this.disable.alignments){
43790             tb.add(
43791                 '-',
43792                 btn('justifyleft'),
43793                 btn('justifycenter'),
43794                 btn('justifyright')
43795             );
43796         };
43797
43798         //if(!Roo.isSafari){
43799             if(!this.disable.links){
43800                 tb.add(
43801                     '-',
43802                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43803                 );
43804             };
43805
43806             if(!this.disable.lists){
43807                 tb.add(
43808                     '-',
43809                     btn('insertorderedlist'),
43810                     btn('insertunorderedlist')
43811                 );
43812             }
43813             if(!this.disable.sourceEdit){
43814                 tb.add(
43815                     '-',
43816                     btn('sourceedit', true, function(btn){
43817                         this.toggleSourceEdit(btn.pressed);
43818                     })
43819                 );
43820             }
43821         //}
43822         
43823         var smenu = { };
43824         // special menu.. - needs to be tidied up..
43825         if (!this.disable.special) {
43826             smenu = {
43827                 text: "&#169;",
43828                 cls: 'x-edit-none',
43829                 
43830                 menu : {
43831                     items : []
43832                 }
43833             };
43834             for (var i =0; i < this.specialChars.length; i++) {
43835                 smenu.menu.items.push({
43836                     
43837                     html: this.specialChars[i],
43838                     handler: function(a,b) {
43839                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43840                         //editor.insertAtCursor(a.html);
43841                         
43842                     },
43843                     tabIndex:-1
43844                 });
43845             }
43846             
43847             
43848             tb.add(smenu);
43849             
43850             
43851         }
43852         
43853         var cmenu = { };
43854         if (!this.disable.cleanStyles) {
43855             cmenu = {
43856                 cls: 'x-btn-icon x-btn-clear',
43857                 
43858                 menu : {
43859                     items : []
43860                 }
43861             };
43862             for (var i =0; i < this.cleanStyles.length; i++) {
43863                 cmenu.menu.items.push({
43864                     actiontype : this.cleanStyles[i],
43865                     html: 'Remove ' + this.cleanStyles[i],
43866                     handler: function(a,b) {
43867 //                        Roo.log(a);
43868 //                        Roo.log(b);
43869                         var c = Roo.get(editorcore.doc.body);
43870                         c.select('[style]').each(function(s) {
43871                             s.dom.style.removeProperty(a.actiontype);
43872                         });
43873                         editorcore.syncValue();
43874                     },
43875                     tabIndex:-1
43876                 });
43877             }
43878              cmenu.menu.items.push({
43879                 actiontype : 'tablewidths',
43880                 html: 'Remove Table Widths',
43881                 handler: function(a,b) {
43882                     editorcore.cleanTableWidths();
43883                     editorcore.syncValue();
43884                 },
43885                 tabIndex:-1
43886             });
43887             cmenu.menu.items.push({
43888                 actiontype : 'word',
43889                 html: 'Remove MS Word Formating',
43890                 handler: function(a,b) {
43891                     editorcore.cleanWord();
43892                     editorcore.syncValue();
43893                 },
43894                 tabIndex:-1
43895             });
43896             
43897             cmenu.menu.items.push({
43898                 actiontype : 'all',
43899                 html: 'Remove All Styles',
43900                 handler: function(a,b) {
43901                     
43902                     var c = Roo.get(editorcore.doc.body);
43903                     c.select('[style]').each(function(s) {
43904                         s.dom.removeAttribute('style');
43905                     });
43906                     editorcore.syncValue();
43907                 },
43908                 tabIndex:-1
43909             });
43910             
43911             cmenu.menu.items.push({
43912                 actiontype : 'all',
43913                 html: 'Remove All CSS Classes',
43914                 handler: function(a,b) {
43915                     
43916                     var c = Roo.get(editorcore.doc.body);
43917                     c.select('[class]').each(function(s) {
43918                         s.dom.className = '';
43919                     });
43920                     editorcore.syncValue();
43921                 },
43922                 tabIndex:-1
43923             });
43924             
43925              cmenu.menu.items.push({
43926                 actiontype : 'tidy',
43927                 html: 'Tidy HTML Source',
43928                 handler: function(a,b) {
43929                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43930                     editorcore.syncValue();
43931                 },
43932                 tabIndex:-1
43933             });
43934             
43935             
43936             tb.add(cmenu);
43937         }
43938          
43939         if (!this.disable.specialElements) {
43940             var semenu = {
43941                 text: "Other;",
43942                 cls: 'x-edit-none',
43943                 menu : {
43944                     items : []
43945                 }
43946             };
43947             for (var i =0; i < this.specialElements.length; i++) {
43948                 semenu.menu.items.push(
43949                     Roo.apply({ 
43950                         handler: function(a,b) {
43951                             editor.insertAtCursor(this.ihtml);
43952                         }
43953                     }, this.specialElements[i])
43954                 );
43955                     
43956             }
43957             
43958             tb.add(semenu);
43959             
43960             
43961         }
43962          
43963         
43964         if (this.btns) {
43965             for(var i =0; i< this.btns.length;i++) {
43966                 var b = Roo.factory(this.btns[i],Roo.form);
43967                 b.cls =  'x-edit-none';
43968                 
43969                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
43970                     b.cls += ' x-init-enable';
43971                 }
43972                 
43973                 b.scope = editorcore;
43974                 tb.add(b);
43975             }
43976         
43977         }
43978         
43979         
43980         
43981         // disable everything...
43982         
43983         this.tb.items.each(function(item){
43984             
43985            if(
43986                 item.id != editorcore.frameId+ '-sourceedit' && 
43987                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
43988             ){
43989                 
43990                 item.disable();
43991             }
43992         });
43993         this.rendered = true;
43994         
43995         // the all the btns;
43996         editor.on('editorevent', this.updateToolbar, this);
43997         // other toolbars need to implement this..
43998         //editor.on('editmodechange', this.updateToolbar, this);
43999     },
44000     
44001     
44002     relayBtnCmd : function(btn) {
44003         this.editorcore.relayCmd(btn.cmd);
44004     },
44005     // private used internally
44006     createLink : function(){
44007         Roo.log("create link?");
44008         var url = prompt(this.createLinkText, this.defaultLinkValue);
44009         if(url && url != 'http:/'+'/'){
44010             this.editorcore.relayCmd('createlink', url);
44011         }
44012     },
44013
44014     
44015     /**
44016      * Protected method that will not generally be called directly. It triggers
44017      * a toolbar update by reading the markup state of the current selection in the editor.
44018      */
44019     updateToolbar: function(){
44020
44021         if(!this.editorcore.activated){
44022             this.editor.onFirstFocus();
44023             return;
44024         }
44025
44026         var btns = this.tb.items.map, 
44027             doc = this.editorcore.doc,
44028             frameId = this.editorcore.frameId;
44029
44030         if(!this.disable.font && !Roo.isSafari){
44031             /*
44032             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
44033             if(name != this.fontSelect.dom.value){
44034                 this.fontSelect.dom.value = name;
44035             }
44036             */
44037         }
44038         if(!this.disable.format){
44039             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
44040             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
44041             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
44042             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
44043         }
44044         if(!this.disable.alignments){
44045             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
44046             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
44047             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
44048         }
44049         if(!Roo.isSafari && !this.disable.lists){
44050             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
44051             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
44052         }
44053         
44054         var ans = this.editorcore.getAllAncestors();
44055         if (this.formatCombo) {
44056             
44057             
44058             var store = this.formatCombo.store;
44059             this.formatCombo.setValue("");
44060             for (var i =0; i < ans.length;i++) {
44061                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
44062                     // select it..
44063                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
44064                     break;
44065                 }
44066             }
44067         }
44068         
44069         
44070         
44071         // hides menus... - so this cant be on a menu...
44072         Roo.menu.MenuMgr.hideAll();
44073
44074         //this.editorsyncValue();
44075     },
44076    
44077     
44078     createFontOptions : function(){
44079         var buf = [], fs = this.fontFamilies, ff, lc;
44080         
44081         
44082         
44083         for(var i = 0, len = fs.length; i< len; i++){
44084             ff = fs[i];
44085             lc = ff.toLowerCase();
44086             buf.push(
44087                 '<option value="',lc,'" style="font-family:',ff,';"',
44088                     (this.defaultFont == lc ? ' selected="true">' : '>'),
44089                     ff,
44090                 '</option>'
44091             );
44092         }
44093         return buf.join('');
44094     },
44095     
44096     toggleSourceEdit : function(sourceEditMode){
44097         
44098         Roo.log("toolbar toogle");
44099         if(sourceEditMode === undefined){
44100             sourceEditMode = !this.sourceEditMode;
44101         }
44102         this.sourceEditMode = sourceEditMode === true;
44103         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
44104         // just toggle the button?
44105         if(btn.pressed !== this.sourceEditMode){
44106             btn.toggle(this.sourceEditMode);
44107             return;
44108         }
44109         
44110         if(sourceEditMode){
44111             Roo.log("disabling buttons");
44112             this.tb.items.each(function(item){
44113                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
44114                     item.disable();
44115                 }
44116             });
44117           
44118         }else{
44119             Roo.log("enabling buttons");
44120             if(this.editorcore.initialized){
44121                 this.tb.items.each(function(item){
44122                     item.enable();
44123                 });
44124             }
44125             
44126         }
44127         Roo.log("calling toggole on editor");
44128         // tell the editor that it's been pressed..
44129         this.editor.toggleSourceEdit(sourceEditMode);
44130        
44131     },
44132      /**
44133      * Object collection of toolbar tooltips for the buttons in the editor. The key
44134      * is the command id associated with that button and the value is a valid QuickTips object.
44135      * For example:
44136 <pre><code>
44137 {
44138     bold : {
44139         title: 'Bold (Ctrl+B)',
44140         text: 'Make the selected text bold.',
44141         cls: 'x-html-editor-tip'
44142     },
44143     italic : {
44144         title: 'Italic (Ctrl+I)',
44145         text: 'Make the selected text italic.',
44146         cls: 'x-html-editor-tip'
44147     },
44148     ...
44149 </code></pre>
44150     * @type Object
44151      */
44152     buttonTips : {
44153         bold : {
44154             title: 'Bold (Ctrl+B)',
44155             text: 'Make the selected text bold.',
44156             cls: 'x-html-editor-tip'
44157         },
44158         italic : {
44159             title: 'Italic (Ctrl+I)',
44160             text: 'Make the selected text italic.',
44161             cls: 'x-html-editor-tip'
44162         },
44163         underline : {
44164             title: 'Underline (Ctrl+U)',
44165             text: 'Underline the selected text.',
44166             cls: 'x-html-editor-tip'
44167         },
44168         strikethrough : {
44169             title: 'Strikethrough',
44170             text: 'Strikethrough the selected text.',
44171             cls: 'x-html-editor-tip'
44172         },
44173         increasefontsize : {
44174             title: 'Grow Text',
44175             text: 'Increase the font size.',
44176             cls: 'x-html-editor-tip'
44177         },
44178         decreasefontsize : {
44179             title: 'Shrink Text',
44180             text: 'Decrease the font size.',
44181             cls: 'x-html-editor-tip'
44182         },
44183         backcolor : {
44184             title: 'Text Highlight Color',
44185             text: 'Change the background color of the selected text.',
44186             cls: 'x-html-editor-tip'
44187         },
44188         forecolor : {
44189             title: 'Font Color',
44190             text: 'Change the color of the selected text.',
44191             cls: 'x-html-editor-tip'
44192         },
44193         justifyleft : {
44194             title: 'Align Text Left',
44195             text: 'Align text to the left.',
44196             cls: 'x-html-editor-tip'
44197         },
44198         justifycenter : {
44199             title: 'Center Text',
44200             text: 'Center text in the editor.',
44201             cls: 'x-html-editor-tip'
44202         },
44203         justifyright : {
44204             title: 'Align Text Right',
44205             text: 'Align text to the right.',
44206             cls: 'x-html-editor-tip'
44207         },
44208         insertunorderedlist : {
44209             title: 'Bullet List',
44210             text: 'Start a bulleted list.',
44211             cls: 'x-html-editor-tip'
44212         },
44213         insertorderedlist : {
44214             title: 'Numbered List',
44215             text: 'Start a numbered list.',
44216             cls: 'x-html-editor-tip'
44217         },
44218         createlink : {
44219             title: 'Hyperlink',
44220             text: 'Make the selected text a hyperlink.',
44221             cls: 'x-html-editor-tip'
44222         },
44223         sourceedit : {
44224             title: 'Source Edit',
44225             text: 'Switch to source editing mode.',
44226             cls: 'x-html-editor-tip'
44227         }
44228     },
44229     // private
44230     onDestroy : function(){
44231         if(this.rendered){
44232             
44233             this.tb.items.each(function(item){
44234                 if(item.menu){
44235                     item.menu.removeAll();
44236                     if(item.menu.el){
44237                         item.menu.el.destroy();
44238                     }
44239                 }
44240                 item.destroy();
44241             });
44242              
44243         }
44244     },
44245     onFirstFocus: function() {
44246         this.tb.items.each(function(item){
44247            item.enable();
44248         });
44249     }
44250 });
44251
44252
44253
44254
44255 // <script type="text/javascript">
44256 /*
44257  * Based on
44258  * Ext JS Library 1.1.1
44259  * Copyright(c) 2006-2007, Ext JS, LLC.
44260  *  
44261  
44262  */
44263
44264  
44265 /**
44266  * @class Roo.form.HtmlEditor.ToolbarContext
44267  * Context Toolbar
44268  * 
44269  * Usage:
44270  *
44271  new Roo.form.HtmlEditor({
44272     ....
44273     toolbars : [
44274         { xtype: 'ToolbarStandard', styles : {} }
44275         { xtype: 'ToolbarContext', disable : {} }
44276     ]
44277 })
44278
44279      
44280  * 
44281  * @config : {Object} disable List of elements to disable.. (not done yet.)
44282  * @config : {Object} styles  Map of styles available.
44283  * 
44284  */
44285
44286 Roo.form.HtmlEditor.ToolbarContext = function(config)
44287 {
44288     
44289     Roo.apply(this, config);
44290     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
44291     // dont call parent... till later.
44292     this.styles = this.styles || {};
44293 }
44294
44295  
44296
44297 Roo.form.HtmlEditor.ToolbarContext.types = {
44298     'IMG' : {
44299         width : {
44300             title: "Width",
44301             width: 40
44302         },
44303         height:  {
44304             title: "Height",
44305             width: 40
44306         },
44307         align: {
44308             title: "Align",
44309             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
44310             width : 80
44311             
44312         },
44313         border: {
44314             title: "Border",
44315             width: 40
44316         },
44317         alt: {
44318             title: "Alt",
44319             width: 120
44320         },
44321         src : {
44322             title: "Src",
44323             width: 220
44324         }
44325         
44326     },
44327     'A' : {
44328         name : {
44329             title: "Name",
44330             width: 50
44331         },
44332         target:  {
44333             title: "Target",
44334             width: 120
44335         },
44336         href:  {
44337             title: "Href",
44338             width: 220
44339         } // border?
44340         
44341     },
44342     'TABLE' : {
44343         rows : {
44344             title: "Rows",
44345             width: 20
44346         },
44347         cols : {
44348             title: "Cols",
44349             width: 20
44350         },
44351         width : {
44352             title: "Width",
44353             width: 40
44354         },
44355         height : {
44356             title: "Height",
44357             width: 40
44358         },
44359         border : {
44360             title: "Border",
44361             width: 20
44362         }
44363     },
44364     'TD' : {
44365         width : {
44366             title: "Width",
44367             width: 40
44368         },
44369         height : {
44370             title: "Height",
44371             width: 40
44372         },   
44373         align: {
44374             title: "Align",
44375             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
44376             width: 80
44377         },
44378         valign: {
44379             title: "Valign",
44380             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
44381             width: 80
44382         },
44383         colspan: {
44384             title: "Colspan",
44385             width: 20
44386             
44387         },
44388          'font-family'  : {
44389             title : "Font",
44390             style : 'fontFamily',
44391             displayField: 'display',
44392             optname : 'font-family',
44393             width: 140
44394         }
44395     },
44396     'INPUT' : {
44397         name : {
44398             title: "name",
44399             width: 120
44400         },
44401         value : {
44402             title: "Value",
44403             width: 120
44404         },
44405         width : {
44406             title: "Width",
44407             width: 40
44408         }
44409     },
44410     'LABEL' : {
44411         'for' : {
44412             title: "For",
44413             width: 120
44414         }
44415     },
44416     'TEXTAREA' : {
44417           name : {
44418             title: "name",
44419             width: 120
44420         },
44421         rows : {
44422             title: "Rows",
44423             width: 20
44424         },
44425         cols : {
44426             title: "Cols",
44427             width: 20
44428         }
44429     },
44430     'SELECT' : {
44431         name : {
44432             title: "name",
44433             width: 120
44434         },
44435         selectoptions : {
44436             title: "Options",
44437             width: 200
44438         }
44439     },
44440     
44441     // should we really allow this??
44442     // should this just be 
44443     'BODY' : {
44444         title : {
44445             title: "Title",
44446             width: 200,
44447             disabled : true
44448         }
44449     },
44450     'SPAN' : {
44451         'font-family'  : {
44452             title : "Font",
44453             style : 'fontFamily',
44454             displayField: 'display',
44455             optname : 'font-family',
44456             width: 140
44457         }
44458     },
44459     'DIV' : {
44460         'font-family'  : {
44461             title : "Font",
44462             style : 'fontFamily',
44463             displayField: 'display',
44464             optname : 'font-family',
44465             width: 140
44466         }
44467     },
44468      'P' : {
44469         'font-family'  : {
44470             title : "Font",
44471             style : 'fontFamily',
44472             displayField: 'display',
44473             optname : 'font-family',
44474             width: 140
44475         }
44476     },
44477     
44478     '*' : {
44479         // empty..
44480     }
44481
44482 };
44483
44484 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
44485 Roo.form.HtmlEditor.ToolbarContext.stores = false;
44486
44487 Roo.form.HtmlEditor.ToolbarContext.options = {
44488         'font-family'  : [ 
44489                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
44490                 [ 'Courier New', 'Courier New'],
44491                 [ 'Tahoma', 'Tahoma'],
44492                 [ 'Times New Roman,serif', 'Times'],
44493                 [ 'Verdana','Verdana' ]
44494         ]
44495 };
44496
44497 // fixme - these need to be configurable..
44498  
44499
44500 //Roo.form.HtmlEditor.ToolbarContext.types
44501
44502
44503 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
44504     
44505     tb: false,
44506     
44507     rendered: false,
44508     
44509     editor : false,
44510     editorcore : false,
44511     /**
44512      * @cfg {Object} disable  List of toolbar elements to disable
44513          
44514      */
44515     disable : false,
44516     /**
44517      * @cfg {Object} styles List of styles 
44518      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
44519      *
44520      * These must be defined in the page, so they get rendered correctly..
44521      * .headline { }
44522      * TD.underline { }
44523      * 
44524      */
44525     styles : false,
44526     
44527     options: false,
44528     
44529     toolbars : false,
44530     
44531     init : function(editor)
44532     {
44533         this.editor = editor;
44534         this.editorcore = editor.editorcore ? editor.editorcore : editor;
44535         var editorcore = this.editorcore;
44536         
44537         var fid = editorcore.frameId;
44538         var etb = this;
44539         function btn(id, toggle, handler){
44540             var xid = fid + '-'+ id ;
44541             return {
44542                 id : xid,
44543                 cmd : id,
44544                 cls : 'x-btn-icon x-edit-'+id,
44545                 enableToggle:toggle !== false,
44546                 scope: editorcore, // was editor...
44547                 handler:handler||editorcore.relayBtnCmd,
44548                 clickEvent:'mousedown',
44549                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
44550                 tabIndex:-1
44551             };
44552         }
44553         // create a new element.
44554         var wdiv = editor.wrap.createChild({
44555                 tag: 'div'
44556             }, editor.wrap.dom.firstChild.nextSibling, true);
44557         
44558         // can we do this more than once??
44559         
44560          // stop form submits
44561       
44562  
44563         // disable everything...
44564         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44565         this.toolbars = {};
44566            
44567         for (var i in  ty) {
44568           
44569             this.toolbars[i] = this.buildToolbar(ty[i],i);
44570         }
44571         this.tb = this.toolbars.BODY;
44572         this.tb.el.show();
44573         this.buildFooter();
44574         this.footer.show();
44575         editor.on('hide', function( ) { this.footer.hide() }, this);
44576         editor.on('show', function( ) { this.footer.show() }, this);
44577         
44578          
44579         this.rendered = true;
44580         
44581         // the all the btns;
44582         editor.on('editorevent', this.updateToolbar, this);
44583         // other toolbars need to implement this..
44584         //editor.on('editmodechange', this.updateToolbar, this);
44585     },
44586     
44587     
44588     
44589     /**
44590      * Protected method that will not generally be called directly. It triggers
44591      * a toolbar update by reading the markup state of the current selection in the editor.
44592      *
44593      * Note you can force an update by calling on('editorevent', scope, false)
44594      */
44595     updateToolbar: function(editor,ev,sel){
44596
44597         //Roo.log(ev);
44598         // capture mouse up - this is handy for selecting images..
44599         // perhaps should go somewhere else...
44600         if(!this.editorcore.activated){
44601              this.editor.onFirstFocus();
44602             return;
44603         }
44604         
44605         
44606         
44607         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
44608         // selectNode - might want to handle IE?
44609         if (ev &&
44610             (ev.type == 'mouseup' || ev.type == 'click' ) &&
44611             ev.target && ev.target.tagName == 'IMG') {
44612             // they have click on an image...
44613             // let's see if we can change the selection...
44614             sel = ev.target;
44615          
44616               var nodeRange = sel.ownerDocument.createRange();
44617             try {
44618                 nodeRange.selectNode(sel);
44619             } catch (e) {
44620                 nodeRange.selectNodeContents(sel);
44621             }
44622             //nodeRange.collapse(true);
44623             var s = this.editorcore.win.getSelection();
44624             s.removeAllRanges();
44625             s.addRange(nodeRange);
44626         }  
44627         
44628       
44629         var updateFooter = sel ? false : true;
44630         
44631         
44632         var ans = this.editorcore.getAllAncestors();
44633         
44634         // pick
44635         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44636         
44637         if (!sel) { 
44638             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
44639             sel = sel ? sel : this.editorcore.doc.body;
44640             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
44641             
44642         }
44643         // pick a menu that exists..
44644         var tn = sel.tagName.toUpperCase();
44645         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
44646         
44647         tn = sel.tagName.toUpperCase();
44648         
44649         var lastSel = this.tb.selectedNode;
44650         
44651         this.tb.selectedNode = sel;
44652         
44653         // if current menu does not match..
44654         
44655         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
44656                 
44657             this.tb.el.hide();
44658             ///console.log("show: " + tn);
44659             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
44660             this.tb.el.show();
44661             // update name
44662             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
44663             
44664             
44665             // update attributes
44666             if (this.tb.fields) {
44667                 this.tb.fields.each(function(e) {
44668                     if (e.stylename) {
44669                         e.setValue(sel.style[e.stylename]);
44670                         return;
44671                     } 
44672                    e.setValue(sel.getAttribute(e.attrname));
44673                 });
44674             }
44675             
44676             var hasStyles = false;
44677             for(var i in this.styles) {
44678                 hasStyles = true;
44679                 break;
44680             }
44681             
44682             // update styles
44683             if (hasStyles) { 
44684                 var st = this.tb.fields.item(0);
44685                 
44686                 st.store.removeAll();
44687                
44688                 
44689                 var cn = sel.className.split(/\s+/);
44690                 
44691                 var avs = [];
44692                 if (this.styles['*']) {
44693                     
44694                     Roo.each(this.styles['*'], function(v) {
44695                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44696                     });
44697                 }
44698                 if (this.styles[tn]) { 
44699                     Roo.each(this.styles[tn], function(v) {
44700                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44701                     });
44702                 }
44703                 
44704                 st.store.loadData(avs);
44705                 st.collapse();
44706                 st.setValue(cn);
44707             }
44708             // flag our selected Node.
44709             this.tb.selectedNode = sel;
44710            
44711            
44712             Roo.menu.MenuMgr.hideAll();
44713
44714         }
44715         
44716         if (!updateFooter) {
44717             //this.footDisp.dom.innerHTML = ''; 
44718             return;
44719         }
44720         // update the footer
44721         //
44722         var html = '';
44723         
44724         this.footerEls = ans.reverse();
44725         Roo.each(this.footerEls, function(a,i) {
44726             if (!a) { return; }
44727             html += html.length ? ' &gt; '  :  '';
44728             
44729             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44730             
44731         });
44732        
44733         // 
44734         var sz = this.footDisp.up('td').getSize();
44735         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44736         this.footDisp.dom.style.marginLeft = '5px';
44737         
44738         this.footDisp.dom.style.overflow = 'hidden';
44739         
44740         this.footDisp.dom.innerHTML = html;
44741             
44742         //this.editorsyncValue();
44743     },
44744      
44745     
44746    
44747        
44748     // private
44749     onDestroy : function(){
44750         if(this.rendered){
44751             
44752             this.tb.items.each(function(item){
44753                 if(item.menu){
44754                     item.menu.removeAll();
44755                     if(item.menu.el){
44756                         item.menu.el.destroy();
44757                     }
44758                 }
44759                 item.destroy();
44760             });
44761              
44762         }
44763     },
44764     onFirstFocus: function() {
44765         // need to do this for all the toolbars..
44766         this.tb.items.each(function(item){
44767            item.enable();
44768         });
44769     },
44770     buildToolbar: function(tlist, nm)
44771     {
44772         var editor = this.editor;
44773         var editorcore = this.editorcore;
44774          // create a new element.
44775         var wdiv = editor.wrap.createChild({
44776                 tag: 'div'
44777             }, editor.wrap.dom.firstChild.nextSibling, true);
44778         
44779        
44780         var tb = new Roo.Toolbar(wdiv);
44781         // add the name..
44782         
44783         tb.add(nm+ ":&nbsp;");
44784         
44785         var styles = [];
44786         for(var i in this.styles) {
44787             styles.push(i);
44788         }
44789         
44790         // styles...
44791         if (styles && styles.length) {
44792             
44793             // this needs a multi-select checkbox...
44794             tb.addField( new Roo.form.ComboBox({
44795                 store: new Roo.data.SimpleStore({
44796                     id : 'val',
44797                     fields: ['val', 'selected'],
44798                     data : [] 
44799                 }),
44800                 name : '-roo-edit-className',
44801                 attrname : 'className',
44802                 displayField: 'val',
44803                 typeAhead: false,
44804                 mode: 'local',
44805                 editable : false,
44806                 triggerAction: 'all',
44807                 emptyText:'Select Style',
44808                 selectOnFocus:true,
44809                 width: 130,
44810                 listeners : {
44811                     'select': function(c, r, i) {
44812                         // initial support only for on class per el..
44813                         tb.selectedNode.className =  r ? r.get('val') : '';
44814                         editorcore.syncValue();
44815                     }
44816                 }
44817     
44818             }));
44819         }
44820         
44821         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44822         var tbops = tbc.options;
44823         
44824         for (var i in tlist) {
44825             
44826             var item = tlist[i];
44827             tb.add(item.title + ":&nbsp;");
44828             
44829             
44830             //optname == used so you can configure the options available..
44831             var opts = item.opts ? item.opts : false;
44832             if (item.optname) {
44833                 opts = tbops[item.optname];
44834            
44835             }
44836             
44837             if (opts) {
44838                 // opts == pulldown..
44839                 tb.addField( new Roo.form.ComboBox({
44840                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44841                         id : 'val',
44842                         fields: ['val', 'display'],
44843                         data : opts  
44844                     }),
44845                     name : '-roo-edit-' + i,
44846                     attrname : i,
44847                     stylename : item.style ? item.style : false,
44848                     displayField: item.displayField ? item.displayField : 'val',
44849                     valueField :  'val',
44850                     typeAhead: false,
44851                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44852                     editable : false,
44853                     triggerAction: 'all',
44854                     emptyText:'Select',
44855                     selectOnFocus:true,
44856                     width: item.width ? item.width  : 130,
44857                     listeners : {
44858                         'select': function(c, r, i) {
44859                             if (c.stylename) {
44860                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44861                                 return;
44862                             }
44863                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44864                         }
44865                     }
44866
44867                 }));
44868                 continue;
44869                     
44870                  
44871                 
44872                 tb.addField( new Roo.form.TextField({
44873                     name: i,
44874                     width: 100,
44875                     //allowBlank:false,
44876                     value: ''
44877                 }));
44878                 continue;
44879             }
44880             tb.addField( new Roo.form.TextField({
44881                 name: '-roo-edit-' + i,
44882                 attrname : i,
44883                 
44884                 width: item.width,
44885                 //allowBlank:true,
44886                 value: '',
44887                 listeners: {
44888                     'change' : function(f, nv, ov) {
44889                         tb.selectedNode.setAttribute(f.attrname, nv);
44890                     }
44891                 }
44892             }));
44893              
44894         }
44895         
44896         var _this = this;
44897         
44898         if(nm == 'BODY'){
44899             tb.addSeparator();
44900         
44901             tb.addButton( {
44902                 text: 'Stylesheets',
44903
44904                 listeners : {
44905                     click : function ()
44906                     {
44907                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
44908                     }
44909                 }
44910             });
44911         }
44912         
44913         tb.addFill();
44914         tb.addButton( {
44915             text: 'Remove Tag',
44916     
44917             listeners : {
44918                 click : function ()
44919                 {
44920                     // remove
44921                     // undo does not work.
44922                      
44923                     var sn = tb.selectedNode;
44924                     
44925                     var pn = sn.parentNode;
44926                     
44927                     var stn =  sn.childNodes[0];
44928                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44929                     while (sn.childNodes.length) {
44930                         var node = sn.childNodes[0];
44931                         sn.removeChild(node);
44932                         //Roo.log(node);
44933                         pn.insertBefore(node, sn);
44934                         
44935                     }
44936                     pn.removeChild(sn);
44937                     var range = editorcore.createRange();
44938         
44939                     range.setStart(stn,0);
44940                     range.setEnd(en,0); //????
44941                     //range.selectNode(sel);
44942                     
44943                     
44944                     var selection = editorcore.getSelection();
44945                     selection.removeAllRanges();
44946                     selection.addRange(range);
44947                     
44948                     
44949                     
44950                     //_this.updateToolbar(null, null, pn);
44951                     _this.updateToolbar(null, null, null);
44952                     _this.footDisp.dom.innerHTML = ''; 
44953                 }
44954             }
44955             
44956                     
44957                 
44958             
44959         });
44960         
44961         
44962         tb.el.on('click', function(e){
44963             e.preventDefault(); // what does this do?
44964         });
44965         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44966         tb.el.hide();
44967         tb.name = nm;
44968         // dont need to disable them... as they will get hidden
44969         return tb;
44970          
44971         
44972     },
44973     buildFooter : function()
44974     {
44975         
44976         var fel = this.editor.wrap.createChild();
44977         this.footer = new Roo.Toolbar(fel);
44978         // toolbar has scrolly on left / right?
44979         var footDisp= new Roo.Toolbar.Fill();
44980         var _t = this;
44981         this.footer.add(
44982             {
44983                 text : '&lt;',
44984                 xtype: 'Button',
44985                 handler : function() {
44986                     _t.footDisp.scrollTo('left',0,true)
44987                 }
44988             }
44989         );
44990         this.footer.add( footDisp );
44991         this.footer.add( 
44992             {
44993                 text : '&gt;',
44994                 xtype: 'Button',
44995                 handler : function() {
44996                     // no animation..
44997                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44998                 }
44999             }
45000         );
45001         var fel = Roo.get(footDisp.el);
45002         fel.addClass('x-editor-context');
45003         this.footDispWrap = fel; 
45004         this.footDispWrap.overflow  = 'hidden';
45005         
45006         this.footDisp = fel.createChild();
45007         this.footDispWrap.on('click', this.onContextClick, this)
45008         
45009         
45010     },
45011     onContextClick : function (ev,dom)
45012     {
45013         ev.preventDefault();
45014         var  cn = dom.className;
45015         //Roo.log(cn);
45016         if (!cn.match(/x-ed-loc-/)) {
45017             return;
45018         }
45019         var n = cn.split('-').pop();
45020         var ans = this.footerEls;
45021         var sel = ans[n];
45022         
45023          // pick
45024         var range = this.editorcore.createRange();
45025         
45026         range.selectNodeContents(sel);
45027         //range.selectNode(sel);
45028         
45029         
45030         var selection = this.editorcore.getSelection();
45031         selection.removeAllRanges();
45032         selection.addRange(range);
45033         
45034         
45035         
45036         this.updateToolbar(null, null, sel);
45037         
45038         
45039     }
45040     
45041     
45042     
45043     
45044     
45045 });
45046
45047
45048
45049
45050
45051 /*
45052  * Based on:
45053  * Ext JS Library 1.1.1
45054  * Copyright(c) 2006-2007, Ext JS, LLC.
45055  *
45056  * Originally Released Under LGPL - original licence link has changed is not relivant.
45057  *
45058  * Fork - LGPL
45059  * <script type="text/javascript">
45060  */
45061  
45062 /**
45063  * @class Roo.form.BasicForm
45064  * @extends Roo.util.Observable
45065  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
45066  * @constructor
45067  * @param {String/HTMLElement/Roo.Element} el The form element or its id
45068  * @param {Object} config Configuration options
45069  */
45070 Roo.form.BasicForm = function(el, config){
45071     this.allItems = [];
45072     this.childForms = [];
45073     Roo.apply(this, config);
45074     /*
45075      * The Roo.form.Field items in this form.
45076      * @type MixedCollection
45077      */
45078      
45079      
45080     this.items = new Roo.util.MixedCollection(false, function(o){
45081         return o.id || (o.id = Roo.id());
45082     });
45083     this.addEvents({
45084         /**
45085          * @event beforeaction
45086          * Fires before any action is performed. Return false to cancel the action.
45087          * @param {Form} this
45088          * @param {Action} action The action to be performed
45089          */
45090         beforeaction: true,
45091         /**
45092          * @event actionfailed
45093          * Fires when an action fails.
45094          * @param {Form} this
45095          * @param {Action} action The action that failed
45096          */
45097         actionfailed : true,
45098         /**
45099          * @event actioncomplete
45100          * Fires when an action is completed.
45101          * @param {Form} this
45102          * @param {Action} action The action that completed
45103          */
45104         actioncomplete : true
45105     });
45106     if(el){
45107         this.initEl(el);
45108     }
45109     Roo.form.BasicForm.superclass.constructor.call(this);
45110 };
45111
45112 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
45113     /**
45114      * @cfg {String} method
45115      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
45116      */
45117     /**
45118      * @cfg {DataReader} reader
45119      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
45120      * This is optional as there is built-in support for processing JSON.
45121      */
45122     /**
45123      * @cfg {DataReader} errorReader
45124      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
45125      * This is completely optional as there is built-in support for processing JSON.
45126      */
45127     /**
45128      * @cfg {String} url
45129      * The URL to use for form actions if one isn't supplied in the action options.
45130      */
45131     /**
45132      * @cfg {Boolean} fileUpload
45133      * Set to true if this form is a file upload.
45134      */
45135      
45136     /**
45137      * @cfg {Object} baseParams
45138      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
45139      */
45140      /**
45141      
45142     /**
45143      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
45144      */
45145     timeout: 30,
45146
45147     // private
45148     activeAction : null,
45149
45150     /**
45151      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
45152      * or setValues() data instead of when the form was first created.
45153      */
45154     trackResetOnLoad : false,
45155     
45156     
45157     /**
45158      * childForms - used for multi-tab forms
45159      * @type {Array}
45160      */
45161     childForms : false,
45162     
45163     /**
45164      * allItems - full list of fields.
45165      * @type {Array}
45166      */
45167     allItems : false,
45168     
45169     /**
45170      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
45171      * element by passing it or its id or mask the form itself by passing in true.
45172      * @type Mixed
45173      */
45174     waitMsgTarget : false,
45175
45176     // private
45177     initEl : function(el){
45178         this.el = Roo.get(el);
45179         this.id = this.el.id || Roo.id();
45180         this.el.on('submit', this.onSubmit, this);
45181         this.el.addClass('x-form');
45182     },
45183
45184     // private
45185     onSubmit : function(e){
45186         e.stopEvent();
45187     },
45188
45189     /**
45190      * Returns true if client-side validation on the form is successful.
45191      * @return Boolean
45192      */
45193     isValid : function(){
45194         var valid = true;
45195         this.items.each(function(f){
45196            if(!f.validate()){
45197                valid = false;
45198            }
45199         });
45200         return valid;
45201     },
45202
45203     /**
45204      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
45205      * @return Boolean
45206      */
45207     isDirty : function(){
45208         var dirty = false;
45209         this.items.each(function(f){
45210            if(f.isDirty()){
45211                dirty = true;
45212                return false;
45213            }
45214         });
45215         return dirty;
45216     },
45217     
45218     /**
45219      * Returns true if any fields in this form have changed since their original load. (New version)
45220      * @return Boolean
45221      */
45222     
45223     hasChanged : function()
45224     {
45225         var dirty = false;
45226         this.items.each(function(f){
45227            if(f.hasChanged()){
45228                dirty = true;
45229                return false;
45230            }
45231         });
45232         return dirty;
45233         
45234     },
45235     /**
45236      * Resets all hasChanged to 'false' -
45237      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
45238      * So hasChanged storage is only to be used for this purpose
45239      * @return Boolean
45240      */
45241     resetHasChanged : function()
45242     {
45243         this.items.each(function(f){
45244            f.resetHasChanged();
45245         });
45246         
45247     },
45248     
45249     
45250     /**
45251      * Performs a predefined action (submit or load) or custom actions you define on this form.
45252      * @param {String} actionName The name of the action type
45253      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
45254      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
45255      * accept other config options):
45256      * <pre>
45257 Property          Type             Description
45258 ----------------  ---------------  ----------------------------------------------------------------------------------
45259 url               String           The url for the action (defaults to the form's url)
45260 method            String           The form method to use (defaults to the form's method, or POST if not defined)
45261 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
45262 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
45263                                    validate the form on the client (defaults to false)
45264      * </pre>
45265      * @return {BasicForm} this
45266      */
45267     doAction : function(action, options){
45268         if(typeof action == 'string'){
45269             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
45270         }
45271         if(this.fireEvent('beforeaction', this, action) !== false){
45272             this.beforeAction(action);
45273             action.run.defer(100, action);
45274         }
45275         return this;
45276     },
45277
45278     /**
45279      * Shortcut to do a submit action.
45280      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45281      * @return {BasicForm} this
45282      */
45283     submit : function(options){
45284         this.doAction('submit', options);
45285         return this;
45286     },
45287
45288     /**
45289      * Shortcut to do a load action.
45290      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45291      * @return {BasicForm} this
45292      */
45293     load : function(options){
45294         this.doAction('load', options);
45295         return this;
45296     },
45297
45298     /**
45299      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
45300      * @param {Record} record The record to edit
45301      * @return {BasicForm} this
45302      */
45303     updateRecord : function(record){
45304         record.beginEdit();
45305         var fs = record.fields;
45306         fs.each(function(f){
45307             var field = this.findField(f.name);
45308             if(field){
45309                 record.set(f.name, field.getValue());
45310             }
45311         }, this);
45312         record.endEdit();
45313         return this;
45314     },
45315
45316     /**
45317      * Loads an Roo.data.Record into this form.
45318      * @param {Record} record The record to load
45319      * @return {BasicForm} this
45320      */
45321     loadRecord : function(record){
45322         this.setValues(record.data);
45323         return this;
45324     },
45325
45326     // private
45327     beforeAction : function(action){
45328         var o = action.options;
45329         
45330        
45331         if(this.waitMsgTarget === true){
45332             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
45333         }else if(this.waitMsgTarget){
45334             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
45335             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
45336         }else {
45337             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
45338         }
45339          
45340     },
45341
45342     // private
45343     afterAction : function(action, success){
45344         this.activeAction = null;
45345         var o = action.options;
45346         
45347         if(this.waitMsgTarget === true){
45348             this.el.unmask();
45349         }else if(this.waitMsgTarget){
45350             this.waitMsgTarget.unmask();
45351         }else{
45352             Roo.MessageBox.updateProgress(1);
45353             Roo.MessageBox.hide();
45354         }
45355          
45356         if(success){
45357             if(o.reset){
45358                 this.reset();
45359             }
45360             Roo.callback(o.success, o.scope, [this, action]);
45361             this.fireEvent('actioncomplete', this, action);
45362             
45363         }else{
45364             
45365             // failure condition..
45366             // we have a scenario where updates need confirming.
45367             // eg. if a locking scenario exists..
45368             // we look for { errors : { needs_confirm : true }} in the response.
45369             if (
45370                 (typeof(action.result) != 'undefined')  &&
45371                 (typeof(action.result.errors) != 'undefined')  &&
45372                 (typeof(action.result.errors.needs_confirm) != 'undefined')
45373            ){
45374                 var _t = this;
45375                 Roo.MessageBox.confirm(
45376                     "Change requires confirmation",
45377                     action.result.errorMsg,
45378                     function(r) {
45379                         if (r != 'yes') {
45380                             return;
45381                         }
45382                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
45383                     }
45384                     
45385                 );
45386                 
45387                 
45388                 
45389                 return;
45390             }
45391             
45392             Roo.callback(o.failure, o.scope, [this, action]);
45393             // show an error message if no failed handler is set..
45394             if (!this.hasListener('actionfailed')) {
45395                 Roo.MessageBox.alert("Error",
45396                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
45397                         action.result.errorMsg :
45398                         "Saving Failed, please check your entries or try again"
45399                 );
45400             }
45401             
45402             this.fireEvent('actionfailed', this, action);
45403         }
45404         
45405     },
45406
45407     /**
45408      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
45409      * @param {String} id The value to search for
45410      * @return Field
45411      */
45412     findField : function(id){
45413         var field = this.items.get(id);
45414         if(!field){
45415             this.items.each(function(f){
45416                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
45417                     field = f;
45418                     return false;
45419                 }
45420             });
45421         }
45422         return field || null;
45423     },
45424
45425     /**
45426      * Add a secondary form to this one, 
45427      * Used to provide tabbed forms. One form is primary, with hidden values 
45428      * which mirror the elements from the other forms.
45429      * 
45430      * @param {Roo.form.Form} form to add.
45431      * 
45432      */
45433     addForm : function(form)
45434     {
45435        
45436         if (this.childForms.indexOf(form) > -1) {
45437             // already added..
45438             return;
45439         }
45440         this.childForms.push(form);
45441         var n = '';
45442         Roo.each(form.allItems, function (fe) {
45443             
45444             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
45445             if (this.findField(n)) { // already added..
45446                 return;
45447             }
45448             var add = new Roo.form.Hidden({
45449                 name : n
45450             });
45451             add.render(this.el);
45452             
45453             this.add( add );
45454         }, this);
45455         
45456     },
45457     /**
45458      * Mark fields in this form invalid in bulk.
45459      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
45460      * @return {BasicForm} this
45461      */
45462     markInvalid : function(errors){
45463         if(errors instanceof Array){
45464             for(var i = 0, len = errors.length; i < len; i++){
45465                 var fieldError = errors[i];
45466                 var f = this.findField(fieldError.id);
45467                 if(f){
45468                     f.markInvalid(fieldError.msg);
45469                 }
45470             }
45471         }else{
45472             var field, id;
45473             for(id in errors){
45474                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
45475                     field.markInvalid(errors[id]);
45476                 }
45477             }
45478         }
45479         Roo.each(this.childForms || [], function (f) {
45480             f.markInvalid(errors);
45481         });
45482         
45483         return this;
45484     },
45485
45486     /**
45487      * Set values for fields in this form in bulk.
45488      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
45489      * @return {BasicForm} this
45490      */
45491     setValues : function(values){
45492         if(values instanceof Array){ // array of objects
45493             for(var i = 0, len = values.length; i < len; i++){
45494                 var v = values[i];
45495                 var f = this.findField(v.id);
45496                 if(f){
45497                     f.setValue(v.value);
45498                     if(this.trackResetOnLoad){
45499                         f.originalValue = f.getValue();
45500                     }
45501                 }
45502             }
45503         }else{ // object hash
45504             var field, id;
45505             for(id in values){
45506                 if(typeof values[id] != 'function' && (field = this.findField(id))){
45507                     
45508                     if (field.setFromData && 
45509                         field.valueField && 
45510                         field.displayField &&
45511                         // combos' with local stores can 
45512                         // be queried via setValue()
45513                         // to set their value..
45514                         (field.store && !field.store.isLocal)
45515                         ) {
45516                         // it's a combo
45517                         var sd = { };
45518                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
45519                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
45520                         field.setFromData(sd);
45521                         
45522                     } else {
45523                         field.setValue(values[id]);
45524                     }
45525                     
45526                     
45527                     if(this.trackResetOnLoad){
45528                         field.originalValue = field.getValue();
45529                     }
45530                 }
45531             }
45532         }
45533         this.resetHasChanged();
45534         
45535         
45536         Roo.each(this.childForms || [], function (f) {
45537             f.setValues(values);
45538             f.resetHasChanged();
45539         });
45540                 
45541         return this;
45542     },
45543
45544     /**
45545      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
45546      * they are returned as an array.
45547      * @param {Boolean} asString
45548      * @return {Object}
45549      */
45550     getValues : function(asString){
45551         if (this.childForms) {
45552             // copy values from the child forms
45553             Roo.each(this.childForms, function (f) {
45554                 this.setValues(f.getValues());
45555             }, this);
45556         }
45557         
45558         
45559         
45560         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
45561         if(asString === true){
45562             return fs;
45563         }
45564         return Roo.urlDecode(fs);
45565     },
45566     
45567     /**
45568      * Returns the fields in this form as an object with key/value pairs. 
45569      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
45570      * @return {Object}
45571      */
45572     getFieldValues : function(with_hidden)
45573     {
45574         if (this.childForms) {
45575             // copy values from the child forms
45576             // should this call getFieldValues - probably not as we do not currently copy
45577             // hidden fields when we generate..
45578             Roo.each(this.childForms, function (f) {
45579                 this.setValues(f.getValues());
45580             }, this);
45581         }
45582         
45583         var ret = {};
45584         this.items.each(function(f){
45585             if (!f.getName()) {
45586                 return;
45587             }
45588             var v = f.getValue();
45589             if (f.inputType =='radio') {
45590                 if (typeof(ret[f.getName()]) == 'undefined') {
45591                     ret[f.getName()] = ''; // empty..
45592                 }
45593                 
45594                 if (!f.el.dom.checked) {
45595                     return;
45596                     
45597                 }
45598                 v = f.el.dom.value;
45599                 
45600             }
45601             
45602             // not sure if this supported any more..
45603             if ((typeof(v) == 'object') && f.getRawValue) {
45604                 v = f.getRawValue() ; // dates..
45605             }
45606             // combo boxes where name != hiddenName...
45607             if (f.name != f.getName()) {
45608                 ret[f.name] = f.getRawValue();
45609             }
45610             ret[f.getName()] = v;
45611         });
45612         
45613         return ret;
45614     },
45615
45616     /**
45617      * Clears all invalid messages in this form.
45618      * @return {BasicForm} this
45619      */
45620     clearInvalid : function(){
45621         this.items.each(function(f){
45622            f.clearInvalid();
45623         });
45624         
45625         Roo.each(this.childForms || [], function (f) {
45626             f.clearInvalid();
45627         });
45628         
45629         
45630         return this;
45631     },
45632
45633     /**
45634      * Resets this form.
45635      * @return {BasicForm} this
45636      */
45637     reset : function(){
45638         this.items.each(function(f){
45639             f.reset();
45640         });
45641         
45642         Roo.each(this.childForms || [], function (f) {
45643             f.reset();
45644         });
45645         this.resetHasChanged();
45646         
45647         return this;
45648     },
45649
45650     /**
45651      * Add Roo.form components to this form.
45652      * @param {Field} field1
45653      * @param {Field} field2 (optional)
45654      * @param {Field} etc (optional)
45655      * @return {BasicForm} this
45656      */
45657     add : function(){
45658         this.items.addAll(Array.prototype.slice.call(arguments, 0));
45659         return this;
45660     },
45661
45662
45663     /**
45664      * Removes a field from the items collection (does NOT remove its markup).
45665      * @param {Field} field
45666      * @return {BasicForm} this
45667      */
45668     remove : function(field){
45669         this.items.remove(field);
45670         return this;
45671     },
45672
45673     /**
45674      * Looks at the fields in this form, checks them for an id attribute,
45675      * and calls applyTo on the existing dom element with that id.
45676      * @return {BasicForm} this
45677      */
45678     render : function(){
45679         this.items.each(function(f){
45680             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
45681                 f.applyTo(f.id);
45682             }
45683         });
45684         return this;
45685     },
45686
45687     /**
45688      * Calls {@link Ext#apply} for all fields in this form with the passed object.
45689      * @param {Object} values
45690      * @return {BasicForm} this
45691      */
45692     applyToFields : function(o){
45693         this.items.each(function(f){
45694            Roo.apply(f, o);
45695         });
45696         return this;
45697     },
45698
45699     /**
45700      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
45701      * @param {Object} values
45702      * @return {BasicForm} this
45703      */
45704     applyIfToFields : function(o){
45705         this.items.each(function(f){
45706            Roo.applyIf(f, o);
45707         });
45708         return this;
45709     }
45710 });
45711
45712 // back compat
45713 Roo.BasicForm = Roo.form.BasicForm;/*
45714  * Based on:
45715  * Ext JS Library 1.1.1
45716  * Copyright(c) 2006-2007, Ext JS, LLC.
45717  *
45718  * Originally Released Under LGPL - original licence link has changed is not relivant.
45719  *
45720  * Fork - LGPL
45721  * <script type="text/javascript">
45722  */
45723
45724 /**
45725  * @class Roo.form.Form
45726  * @extends Roo.form.BasicForm
45727  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
45728  * @constructor
45729  * @param {Object} config Configuration options
45730  */
45731 Roo.form.Form = function(config){
45732     var xitems =  [];
45733     if (config.items) {
45734         xitems = config.items;
45735         delete config.items;
45736     }
45737    
45738     
45739     Roo.form.Form.superclass.constructor.call(this, null, config);
45740     this.url = this.url || this.action;
45741     if(!this.root){
45742         this.root = new Roo.form.Layout(Roo.applyIf({
45743             id: Roo.id()
45744         }, config));
45745     }
45746     this.active = this.root;
45747     /**
45748      * Array of all the buttons that have been added to this form via {@link addButton}
45749      * @type Array
45750      */
45751     this.buttons = [];
45752     this.allItems = [];
45753     this.addEvents({
45754         /**
45755          * @event clientvalidation
45756          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45757          * @param {Form} this
45758          * @param {Boolean} valid true if the form has passed client-side validation
45759          */
45760         clientvalidation: true,
45761         /**
45762          * @event rendered
45763          * Fires when the form is rendered
45764          * @param {Roo.form.Form} form
45765          */
45766         rendered : true
45767     });
45768     
45769     if (this.progressUrl) {
45770             // push a hidden field onto the list of fields..
45771             this.addxtype( {
45772                     xns: Roo.form, 
45773                     xtype : 'Hidden', 
45774                     name : 'UPLOAD_IDENTIFIER' 
45775             });
45776         }
45777         
45778     
45779     Roo.each(xitems, this.addxtype, this);
45780     
45781     
45782     
45783 };
45784
45785 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45786     /**
45787      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45788      */
45789     /**
45790      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45791      */
45792     /**
45793      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45794      */
45795     buttonAlign:'center',
45796
45797     /**
45798      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45799      */
45800     minButtonWidth:75,
45801
45802     /**
45803      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45804      * This property cascades to child containers if not set.
45805      */
45806     labelAlign:'left',
45807
45808     /**
45809      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45810      * fires a looping event with that state. This is required to bind buttons to the valid
45811      * state using the config value formBind:true on the button.
45812      */
45813     monitorValid : false,
45814
45815     /**
45816      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45817      */
45818     monitorPoll : 200,
45819     
45820     /**
45821      * @cfg {String} progressUrl - Url to return progress data 
45822      */
45823     
45824     progressUrl : false,
45825   
45826     /**
45827      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45828      * fields are added and the column is closed. If no fields are passed the column remains open
45829      * until end() is called.
45830      * @param {Object} config The config to pass to the column
45831      * @param {Field} field1 (optional)
45832      * @param {Field} field2 (optional)
45833      * @param {Field} etc (optional)
45834      * @return Column The column container object
45835      */
45836     column : function(c){
45837         var col = new Roo.form.Column(c);
45838         this.start(col);
45839         if(arguments.length > 1){ // duplicate code required because of Opera
45840             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45841             this.end();
45842         }
45843         return col;
45844     },
45845
45846     /**
45847      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45848      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45849      * until end() is called.
45850      * @param {Object} config The config to pass to the fieldset
45851      * @param {Field} field1 (optional)
45852      * @param {Field} field2 (optional)
45853      * @param {Field} etc (optional)
45854      * @return FieldSet The fieldset container object
45855      */
45856     fieldset : function(c){
45857         var fs = new Roo.form.FieldSet(c);
45858         this.start(fs);
45859         if(arguments.length > 1){ // duplicate code required because of Opera
45860             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45861             this.end();
45862         }
45863         return fs;
45864     },
45865
45866     /**
45867      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45868      * fields are added and the container is closed. If no fields are passed the container remains open
45869      * until end() is called.
45870      * @param {Object} config The config to pass to the Layout
45871      * @param {Field} field1 (optional)
45872      * @param {Field} field2 (optional)
45873      * @param {Field} etc (optional)
45874      * @return Layout The container object
45875      */
45876     container : function(c){
45877         var l = new Roo.form.Layout(c);
45878         this.start(l);
45879         if(arguments.length > 1){ // duplicate code required because of Opera
45880             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45881             this.end();
45882         }
45883         return l;
45884     },
45885
45886     /**
45887      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45888      * @param {Object} container A Roo.form.Layout or subclass of Layout
45889      * @return {Form} this
45890      */
45891     start : function(c){
45892         // cascade label info
45893         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45894         this.active.stack.push(c);
45895         c.ownerCt = this.active;
45896         this.active = c;
45897         return this;
45898     },
45899
45900     /**
45901      * Closes the current open container
45902      * @return {Form} this
45903      */
45904     end : function(){
45905         if(this.active == this.root){
45906             return this;
45907         }
45908         this.active = this.active.ownerCt;
45909         return this;
45910     },
45911
45912     /**
45913      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45914      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45915      * as the label of the field.
45916      * @param {Field} field1
45917      * @param {Field} field2 (optional)
45918      * @param {Field} etc. (optional)
45919      * @return {Form} this
45920      */
45921     add : function(){
45922         this.active.stack.push.apply(this.active.stack, arguments);
45923         this.allItems.push.apply(this.allItems,arguments);
45924         var r = [];
45925         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45926             if(a[i].isFormField){
45927                 r.push(a[i]);
45928             }
45929         }
45930         if(r.length > 0){
45931             Roo.form.Form.superclass.add.apply(this, r);
45932         }
45933         return this;
45934     },
45935     
45936
45937     
45938     
45939     
45940      /**
45941      * Find any element that has been added to a form, using it's ID or name
45942      * This can include framesets, columns etc. along with regular fields..
45943      * @param {String} id - id or name to find.
45944      
45945      * @return {Element} e - or false if nothing found.
45946      */
45947     findbyId : function(id)
45948     {
45949         var ret = false;
45950         if (!id) {
45951             return ret;
45952         }
45953         Roo.each(this.allItems, function(f){
45954             if (f.id == id || f.name == id ){
45955                 ret = f;
45956                 return false;
45957             }
45958         });
45959         return ret;
45960     },
45961
45962     
45963     
45964     /**
45965      * Render this form into the passed container. This should only be called once!
45966      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45967      * @return {Form} this
45968      */
45969     render : function(ct)
45970     {
45971         
45972         
45973         
45974         ct = Roo.get(ct);
45975         var o = this.autoCreate || {
45976             tag: 'form',
45977             method : this.method || 'POST',
45978             id : this.id || Roo.id()
45979         };
45980         this.initEl(ct.createChild(o));
45981
45982         this.root.render(this.el);
45983         
45984        
45985              
45986         this.items.each(function(f){
45987             f.render('x-form-el-'+f.id);
45988         });
45989
45990         if(this.buttons.length > 0){
45991             // tables are required to maintain order and for correct IE layout
45992             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45993                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45994                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45995             }}, null, true);
45996             var tr = tb.getElementsByTagName('tr')[0];
45997             for(var i = 0, len = this.buttons.length; i < len; i++) {
45998                 var b = this.buttons[i];
45999                 var td = document.createElement('td');
46000                 td.className = 'x-form-btn-td';
46001                 b.render(tr.appendChild(td));
46002             }
46003         }
46004         if(this.monitorValid){ // initialize after render
46005             this.startMonitoring();
46006         }
46007         this.fireEvent('rendered', this);
46008         return this;
46009     },
46010
46011     /**
46012      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
46013      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
46014      * object or a valid Roo.DomHelper element config
46015      * @param {Function} handler The function called when the button is clicked
46016      * @param {Object} scope (optional) The scope of the handler function
46017      * @return {Roo.Button}
46018      */
46019     addButton : function(config, handler, scope){
46020         var bc = {
46021             handler: handler,
46022             scope: scope,
46023             minWidth: this.minButtonWidth,
46024             hideParent:true
46025         };
46026         if(typeof config == "string"){
46027             bc.text = config;
46028         }else{
46029             Roo.apply(bc, config);
46030         }
46031         var btn = new Roo.Button(null, bc);
46032         this.buttons.push(btn);
46033         return btn;
46034     },
46035
46036      /**
46037      * Adds a series of form elements (using the xtype property as the factory method.
46038      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
46039      * @param {Object} config 
46040      */
46041     
46042     addxtype : function()
46043     {
46044         var ar = Array.prototype.slice.call(arguments, 0);
46045         var ret = false;
46046         for(var i = 0; i < ar.length; i++) {
46047             if (!ar[i]) {
46048                 continue; // skip -- if this happends something invalid got sent, we 
46049                 // should ignore it, as basically that interface element will not show up
46050                 // and that should be pretty obvious!!
46051             }
46052             
46053             if (Roo.form[ar[i].xtype]) {
46054                 ar[i].form = this;
46055                 var fe = Roo.factory(ar[i], Roo.form);
46056                 if (!ret) {
46057                     ret = fe;
46058                 }
46059                 fe.form = this;
46060                 if (fe.store) {
46061                     fe.store.form = this;
46062                 }
46063                 if (fe.isLayout) {  
46064                          
46065                     this.start(fe);
46066                     this.allItems.push(fe);
46067                     if (fe.items && fe.addxtype) {
46068                         fe.addxtype.apply(fe, fe.items);
46069                         delete fe.items;
46070                     }
46071                      this.end();
46072                     continue;
46073                 }
46074                 
46075                 
46076                  
46077                 this.add(fe);
46078               //  console.log('adding ' + ar[i].xtype);
46079             }
46080             if (ar[i].xtype == 'Button') {  
46081                 //console.log('adding button');
46082                 //console.log(ar[i]);
46083                 this.addButton(ar[i]);
46084                 this.allItems.push(fe);
46085                 continue;
46086             }
46087             
46088             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
46089                 alert('end is not supported on xtype any more, use items');
46090             //    this.end();
46091             //    //console.log('adding end');
46092             }
46093             
46094         }
46095         return ret;
46096     },
46097     
46098     /**
46099      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
46100      * option "monitorValid"
46101      */
46102     startMonitoring : function(){
46103         if(!this.bound){
46104             this.bound = true;
46105             Roo.TaskMgr.start({
46106                 run : this.bindHandler,
46107                 interval : this.monitorPoll || 200,
46108                 scope: this
46109             });
46110         }
46111     },
46112
46113     /**
46114      * Stops monitoring of the valid state of this form
46115      */
46116     stopMonitoring : function(){
46117         this.bound = false;
46118     },
46119
46120     // private
46121     bindHandler : function(){
46122         if(!this.bound){
46123             return false; // stops binding
46124         }
46125         var valid = true;
46126         this.items.each(function(f){
46127             if(!f.isValid(true)){
46128                 valid = false;
46129                 return false;
46130             }
46131         });
46132         for(var i = 0, len = this.buttons.length; i < len; i++){
46133             var btn = this.buttons[i];
46134             if(btn.formBind === true && btn.disabled === valid){
46135                 btn.setDisabled(!valid);
46136             }
46137         }
46138         this.fireEvent('clientvalidation', this, valid);
46139     }
46140     
46141     
46142     
46143     
46144     
46145     
46146     
46147     
46148 });
46149
46150
46151 // back compat
46152 Roo.Form = Roo.form.Form;
46153 /*
46154  * Based on:
46155  * Ext JS Library 1.1.1
46156  * Copyright(c) 2006-2007, Ext JS, LLC.
46157  *
46158  * Originally Released Under LGPL - original licence link has changed is not relivant.
46159  *
46160  * Fork - LGPL
46161  * <script type="text/javascript">
46162  */
46163
46164 // as we use this in bootstrap.
46165 Roo.namespace('Roo.form');
46166  /**
46167  * @class Roo.form.Action
46168  * Internal Class used to handle form actions
46169  * @constructor
46170  * @param {Roo.form.BasicForm} el The form element or its id
46171  * @param {Object} config Configuration options
46172  */
46173
46174  
46175  
46176 // define the action interface
46177 Roo.form.Action = function(form, options){
46178     this.form = form;
46179     this.options = options || {};
46180 };
46181 /**
46182  * Client Validation Failed
46183  * @const 
46184  */
46185 Roo.form.Action.CLIENT_INVALID = 'client';
46186 /**
46187  * Server Validation Failed
46188  * @const 
46189  */
46190 Roo.form.Action.SERVER_INVALID = 'server';
46191  /**
46192  * Connect to Server Failed
46193  * @const 
46194  */
46195 Roo.form.Action.CONNECT_FAILURE = 'connect';
46196 /**
46197  * Reading Data from Server Failed
46198  * @const 
46199  */
46200 Roo.form.Action.LOAD_FAILURE = 'load';
46201
46202 Roo.form.Action.prototype = {
46203     type : 'default',
46204     failureType : undefined,
46205     response : undefined,
46206     result : undefined,
46207
46208     // interface method
46209     run : function(options){
46210
46211     },
46212
46213     // interface method
46214     success : function(response){
46215
46216     },
46217
46218     // interface method
46219     handleResponse : function(response){
46220
46221     },
46222
46223     // default connection failure
46224     failure : function(response){
46225         
46226         this.response = response;
46227         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46228         this.form.afterAction(this, false);
46229     },
46230
46231     processResponse : function(response){
46232         this.response = response;
46233         if(!response.responseText){
46234             return true;
46235         }
46236         this.result = this.handleResponse(response);
46237         return this.result;
46238     },
46239
46240     // utility functions used internally
46241     getUrl : function(appendParams){
46242         var url = this.options.url || this.form.url || this.form.el.dom.action;
46243         if(appendParams){
46244             var p = this.getParams();
46245             if(p){
46246                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
46247             }
46248         }
46249         return url;
46250     },
46251
46252     getMethod : function(){
46253         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
46254     },
46255
46256     getParams : function(){
46257         var bp = this.form.baseParams;
46258         var p = this.options.params;
46259         if(p){
46260             if(typeof p == "object"){
46261                 p = Roo.urlEncode(Roo.applyIf(p, bp));
46262             }else if(typeof p == 'string' && bp){
46263                 p += '&' + Roo.urlEncode(bp);
46264             }
46265         }else if(bp){
46266             p = Roo.urlEncode(bp);
46267         }
46268         return p;
46269     },
46270
46271     createCallback : function(){
46272         return {
46273             success: this.success,
46274             failure: this.failure,
46275             scope: this,
46276             timeout: (this.form.timeout*1000),
46277             upload: this.form.fileUpload ? this.success : undefined
46278         };
46279     }
46280 };
46281
46282 Roo.form.Action.Submit = function(form, options){
46283     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
46284 };
46285
46286 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
46287     type : 'submit',
46288
46289     haveProgress : false,
46290     uploadComplete : false,
46291     
46292     // uploadProgress indicator.
46293     uploadProgress : function()
46294     {
46295         if (!this.form.progressUrl) {
46296             return;
46297         }
46298         
46299         if (!this.haveProgress) {
46300             Roo.MessageBox.progress("Uploading", "Uploading");
46301         }
46302         if (this.uploadComplete) {
46303            Roo.MessageBox.hide();
46304            return;
46305         }
46306         
46307         this.haveProgress = true;
46308    
46309         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
46310         
46311         var c = new Roo.data.Connection();
46312         c.request({
46313             url : this.form.progressUrl,
46314             params: {
46315                 id : uid
46316             },
46317             method: 'GET',
46318             success : function(req){
46319                //console.log(data);
46320                 var rdata = false;
46321                 var edata;
46322                 try  {
46323                    rdata = Roo.decode(req.responseText)
46324                 } catch (e) {
46325                     Roo.log("Invalid data from server..");
46326                     Roo.log(edata);
46327                     return;
46328                 }
46329                 if (!rdata || !rdata.success) {
46330                     Roo.log(rdata);
46331                     Roo.MessageBox.alert(Roo.encode(rdata));
46332                     return;
46333                 }
46334                 var data = rdata.data;
46335                 
46336                 if (this.uploadComplete) {
46337                    Roo.MessageBox.hide();
46338                    return;
46339                 }
46340                    
46341                 if (data){
46342                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
46343                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
46344                     );
46345                 }
46346                 this.uploadProgress.defer(2000,this);
46347             },
46348        
46349             failure: function(data) {
46350                 Roo.log('progress url failed ');
46351                 Roo.log(data);
46352             },
46353             scope : this
46354         });
46355            
46356     },
46357     
46358     
46359     run : function()
46360     {
46361         // run get Values on the form, so it syncs any secondary forms.
46362         this.form.getValues();
46363         
46364         var o = this.options;
46365         var method = this.getMethod();
46366         var isPost = method == 'POST';
46367         if(o.clientValidation === false || this.form.isValid()){
46368             
46369             if (this.form.progressUrl) {
46370                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
46371                     (new Date() * 1) + '' + Math.random());
46372                     
46373             } 
46374             
46375             
46376             Roo.Ajax.request(Roo.apply(this.createCallback(), {
46377                 form:this.form.el.dom,
46378                 url:this.getUrl(!isPost),
46379                 method: method,
46380                 params:isPost ? this.getParams() : null,
46381                 isUpload: this.form.fileUpload
46382             }));
46383             
46384             this.uploadProgress();
46385
46386         }else if (o.clientValidation !== false){ // client validation failed
46387             this.failureType = Roo.form.Action.CLIENT_INVALID;
46388             this.form.afterAction(this, false);
46389         }
46390     },
46391
46392     success : function(response)
46393     {
46394         this.uploadComplete= true;
46395         if (this.haveProgress) {
46396             Roo.MessageBox.hide();
46397         }
46398         
46399         
46400         var result = this.processResponse(response);
46401         if(result === true || result.success){
46402             this.form.afterAction(this, true);
46403             return;
46404         }
46405         if(result.errors){
46406             this.form.markInvalid(result.errors);
46407             this.failureType = Roo.form.Action.SERVER_INVALID;
46408         }
46409         this.form.afterAction(this, false);
46410     },
46411     failure : function(response)
46412     {
46413         this.uploadComplete= true;
46414         if (this.haveProgress) {
46415             Roo.MessageBox.hide();
46416         }
46417         
46418         this.response = response;
46419         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46420         this.form.afterAction(this, false);
46421     },
46422     
46423     handleResponse : function(response){
46424         if(this.form.errorReader){
46425             var rs = this.form.errorReader.read(response);
46426             var errors = [];
46427             if(rs.records){
46428                 for(var i = 0, len = rs.records.length; i < len; i++) {
46429                     var r = rs.records[i];
46430                     errors[i] = r.data;
46431                 }
46432             }
46433             if(errors.length < 1){
46434                 errors = null;
46435             }
46436             return {
46437                 success : rs.success,
46438                 errors : errors
46439             };
46440         }
46441         var ret = false;
46442         try {
46443             ret = Roo.decode(response.responseText);
46444         } catch (e) {
46445             ret = {
46446                 success: false,
46447                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
46448                 errors : []
46449             };
46450         }
46451         return ret;
46452         
46453     }
46454 });
46455
46456
46457 Roo.form.Action.Load = function(form, options){
46458     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
46459     this.reader = this.form.reader;
46460 };
46461
46462 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
46463     type : 'load',
46464
46465     run : function(){
46466         
46467         Roo.Ajax.request(Roo.apply(
46468                 this.createCallback(), {
46469                     method:this.getMethod(),
46470                     url:this.getUrl(false),
46471                     params:this.getParams()
46472         }));
46473     },
46474
46475     success : function(response){
46476         
46477         var result = this.processResponse(response);
46478         if(result === true || !result.success || !result.data){
46479             this.failureType = Roo.form.Action.LOAD_FAILURE;
46480             this.form.afterAction(this, false);
46481             return;
46482         }
46483         this.form.clearInvalid();
46484         this.form.setValues(result.data);
46485         this.form.afterAction(this, true);
46486     },
46487
46488     handleResponse : function(response){
46489         if(this.form.reader){
46490             var rs = this.form.reader.read(response);
46491             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
46492             return {
46493                 success : rs.success,
46494                 data : data
46495             };
46496         }
46497         return Roo.decode(response.responseText);
46498     }
46499 });
46500
46501 Roo.form.Action.ACTION_TYPES = {
46502     'load' : Roo.form.Action.Load,
46503     'submit' : Roo.form.Action.Submit
46504 };/*
46505  * Based on:
46506  * Ext JS Library 1.1.1
46507  * Copyright(c) 2006-2007, Ext JS, LLC.
46508  *
46509  * Originally Released Under LGPL - original licence link has changed is not relivant.
46510  *
46511  * Fork - LGPL
46512  * <script type="text/javascript">
46513  */
46514  
46515 /**
46516  * @class Roo.form.Layout
46517  * @extends Roo.Component
46518  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
46519  * @constructor
46520  * @param {Object} config Configuration options
46521  */
46522 Roo.form.Layout = function(config){
46523     var xitems = [];
46524     if (config.items) {
46525         xitems = config.items;
46526         delete config.items;
46527     }
46528     Roo.form.Layout.superclass.constructor.call(this, config);
46529     this.stack = [];
46530     Roo.each(xitems, this.addxtype, this);
46531      
46532 };
46533
46534 Roo.extend(Roo.form.Layout, Roo.Component, {
46535     /**
46536      * @cfg {String/Object} autoCreate
46537      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
46538      */
46539     /**
46540      * @cfg {String/Object/Function} style
46541      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
46542      * a function which returns such a specification.
46543      */
46544     /**
46545      * @cfg {String} labelAlign
46546      * Valid values are "left," "top" and "right" (defaults to "left")
46547      */
46548     /**
46549      * @cfg {Number} labelWidth
46550      * Fixed width in pixels of all field labels (defaults to undefined)
46551      */
46552     /**
46553      * @cfg {Boolean} clear
46554      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
46555      */
46556     clear : true,
46557     /**
46558      * @cfg {String} labelSeparator
46559      * The separator to use after field labels (defaults to ':')
46560      */
46561     labelSeparator : ':',
46562     /**
46563      * @cfg {Boolean} hideLabels
46564      * True to suppress the display of field labels in this layout (defaults to false)
46565      */
46566     hideLabels : false,
46567
46568     // private
46569     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
46570     
46571     isLayout : true,
46572     
46573     // private
46574     onRender : function(ct, position){
46575         if(this.el){ // from markup
46576             this.el = Roo.get(this.el);
46577         }else {  // generate
46578             var cfg = this.getAutoCreate();
46579             this.el = ct.createChild(cfg, position);
46580         }
46581         if(this.style){
46582             this.el.applyStyles(this.style);
46583         }
46584         if(this.labelAlign){
46585             this.el.addClass('x-form-label-'+this.labelAlign);
46586         }
46587         if(this.hideLabels){
46588             this.labelStyle = "display:none";
46589             this.elementStyle = "padding-left:0;";
46590         }else{
46591             if(typeof this.labelWidth == 'number'){
46592                 this.labelStyle = "width:"+this.labelWidth+"px;";
46593                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
46594             }
46595             if(this.labelAlign == 'top'){
46596                 this.labelStyle = "width:auto;";
46597                 this.elementStyle = "padding-left:0;";
46598             }
46599         }
46600         var stack = this.stack;
46601         var slen = stack.length;
46602         if(slen > 0){
46603             if(!this.fieldTpl){
46604                 var t = new Roo.Template(
46605                     '<div class="x-form-item {5}">',
46606                         '<label for="{0}" style="{2}">{1}{4}</label>',
46607                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46608                         '</div>',
46609                     '</div><div class="x-form-clear-left"></div>'
46610                 );
46611                 t.disableFormats = true;
46612                 t.compile();
46613                 Roo.form.Layout.prototype.fieldTpl = t;
46614             }
46615             for(var i = 0; i < slen; i++) {
46616                 if(stack[i].isFormField){
46617                     this.renderField(stack[i]);
46618                 }else{
46619                     this.renderComponent(stack[i]);
46620                 }
46621             }
46622         }
46623         if(this.clear){
46624             this.el.createChild({cls:'x-form-clear'});
46625         }
46626     },
46627
46628     // private
46629     renderField : function(f){
46630         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
46631                f.id, //0
46632                f.fieldLabel, //1
46633                f.labelStyle||this.labelStyle||'', //2
46634                this.elementStyle||'', //3
46635                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
46636                f.itemCls||this.itemCls||''  //5
46637        ], true).getPrevSibling());
46638     },
46639
46640     // private
46641     renderComponent : function(c){
46642         c.render(c.isLayout ? this.el : this.el.createChild());    
46643     },
46644     /**
46645      * Adds a object form elements (using the xtype property as the factory method.)
46646      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
46647      * @param {Object} config 
46648      */
46649     addxtype : function(o)
46650     {
46651         // create the lement.
46652         o.form = this.form;
46653         var fe = Roo.factory(o, Roo.form);
46654         this.form.allItems.push(fe);
46655         this.stack.push(fe);
46656         
46657         if (fe.isFormField) {
46658             this.form.items.add(fe);
46659         }
46660          
46661         return fe;
46662     }
46663 });
46664
46665 /**
46666  * @class Roo.form.Column
46667  * @extends Roo.form.Layout
46668  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
46669  * @constructor
46670  * @param {Object} config Configuration options
46671  */
46672 Roo.form.Column = function(config){
46673     Roo.form.Column.superclass.constructor.call(this, config);
46674 };
46675
46676 Roo.extend(Roo.form.Column, Roo.form.Layout, {
46677     /**
46678      * @cfg {Number/String} width
46679      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46680      */
46681     /**
46682      * @cfg {String/Object} autoCreate
46683      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
46684      */
46685
46686     // private
46687     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
46688
46689     // private
46690     onRender : function(ct, position){
46691         Roo.form.Column.superclass.onRender.call(this, ct, position);
46692         if(this.width){
46693             this.el.setWidth(this.width);
46694         }
46695     }
46696 });
46697
46698
46699 /**
46700  * @class Roo.form.Row
46701  * @extends Roo.form.Layout
46702  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
46703  * @constructor
46704  * @param {Object} config Configuration options
46705  */
46706
46707  
46708 Roo.form.Row = function(config){
46709     Roo.form.Row.superclass.constructor.call(this, config);
46710 };
46711  
46712 Roo.extend(Roo.form.Row, Roo.form.Layout, {
46713       /**
46714      * @cfg {Number/String} width
46715      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46716      */
46717     /**
46718      * @cfg {Number/String} height
46719      * The fixed height of the column in pixels or CSS value (defaults to "auto")
46720      */
46721     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
46722     
46723     padWidth : 20,
46724     // private
46725     onRender : function(ct, position){
46726         //console.log('row render');
46727         if(!this.rowTpl){
46728             var t = new Roo.Template(
46729                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
46730                     '<label for="{0}" style="{2}">{1}{4}</label>',
46731                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46732                     '</div>',
46733                 '</div>'
46734             );
46735             t.disableFormats = true;
46736             t.compile();
46737             Roo.form.Layout.prototype.rowTpl = t;
46738         }
46739         this.fieldTpl = this.rowTpl;
46740         
46741         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46742         var labelWidth = 100;
46743         
46744         if ((this.labelAlign != 'top')) {
46745             if (typeof this.labelWidth == 'number') {
46746                 labelWidth = this.labelWidth
46747             }
46748             this.padWidth =  20 + labelWidth;
46749             
46750         }
46751         
46752         Roo.form.Column.superclass.onRender.call(this, ct, position);
46753         if(this.width){
46754             this.el.setWidth(this.width);
46755         }
46756         if(this.height){
46757             this.el.setHeight(this.height);
46758         }
46759     },
46760     
46761     // private
46762     renderField : function(f){
46763         f.fieldEl = this.fieldTpl.append(this.el, [
46764                f.id, f.fieldLabel,
46765                f.labelStyle||this.labelStyle||'',
46766                this.elementStyle||'',
46767                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46768                f.itemCls||this.itemCls||'',
46769                f.width ? f.width + this.padWidth : 160 + this.padWidth
46770        ],true);
46771     }
46772 });
46773  
46774
46775 /**
46776  * @class Roo.form.FieldSet
46777  * @extends Roo.form.Layout
46778  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46779  * @constructor
46780  * @param {Object} config Configuration options
46781  */
46782 Roo.form.FieldSet = function(config){
46783     Roo.form.FieldSet.superclass.constructor.call(this, config);
46784 };
46785
46786 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46787     /**
46788      * @cfg {String} legend
46789      * The text to display as the legend for the FieldSet (defaults to '')
46790      */
46791     /**
46792      * @cfg {String/Object} autoCreate
46793      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46794      */
46795
46796     // private
46797     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46798
46799     // private
46800     onRender : function(ct, position){
46801         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46802         if(this.legend){
46803             this.setLegend(this.legend);
46804         }
46805     },
46806
46807     // private
46808     setLegend : function(text){
46809         if(this.rendered){
46810             this.el.child('legend').update(text);
46811         }
46812     }
46813 });/*
46814  * Based on:
46815  * Ext JS Library 1.1.1
46816  * Copyright(c) 2006-2007, Ext JS, LLC.
46817  *
46818  * Originally Released Under LGPL - original licence link has changed is not relivant.
46819  *
46820  * Fork - LGPL
46821  * <script type="text/javascript">
46822  */
46823 /**
46824  * @class Roo.form.VTypes
46825  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46826  * @singleton
46827  */
46828 Roo.form.VTypes = function(){
46829     // closure these in so they are only created once.
46830     var alpha = /^[a-zA-Z_]+$/;
46831     var alphanum = /^[a-zA-Z0-9_]+$/;
46832     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
46833     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46834
46835     // All these messages and functions are configurable
46836     return {
46837         /**
46838          * The function used to validate email addresses
46839          * @param {String} value The email address
46840          */
46841         'email' : function(v){
46842             return email.test(v);
46843         },
46844         /**
46845          * The error text to display when the email validation function returns false
46846          * @type String
46847          */
46848         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46849         /**
46850          * The keystroke filter mask to be applied on email input
46851          * @type RegExp
46852          */
46853         'emailMask' : /[a-z0-9_\.\-@]/i,
46854
46855         /**
46856          * The function used to validate URLs
46857          * @param {String} value The URL
46858          */
46859         'url' : function(v){
46860             return url.test(v);
46861         },
46862         /**
46863          * The error text to display when the url validation function returns false
46864          * @type String
46865          */
46866         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46867         
46868         /**
46869          * The function used to validate alpha values
46870          * @param {String} value The value
46871          */
46872         'alpha' : function(v){
46873             return alpha.test(v);
46874         },
46875         /**
46876          * The error text to display when the alpha validation function returns false
46877          * @type String
46878          */
46879         'alphaText' : 'This field should only contain letters and _',
46880         /**
46881          * The keystroke filter mask to be applied on alpha input
46882          * @type RegExp
46883          */
46884         'alphaMask' : /[a-z_]/i,
46885
46886         /**
46887          * The function used to validate alphanumeric values
46888          * @param {String} value The value
46889          */
46890         'alphanum' : function(v){
46891             return alphanum.test(v);
46892         },
46893         /**
46894          * The error text to display when the alphanumeric validation function returns false
46895          * @type String
46896          */
46897         'alphanumText' : 'This field should only contain letters, numbers and _',
46898         /**
46899          * The keystroke filter mask to be applied on alphanumeric input
46900          * @type RegExp
46901          */
46902         'alphanumMask' : /[a-z0-9_]/i
46903     };
46904 }();//<script type="text/javascript">
46905
46906 /**
46907  * @class Roo.form.FCKeditor
46908  * @extends Roo.form.TextArea
46909  * Wrapper around the FCKEditor http://www.fckeditor.net
46910  * @constructor
46911  * Creates a new FCKeditor
46912  * @param {Object} config Configuration options
46913  */
46914 Roo.form.FCKeditor = function(config){
46915     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46916     this.addEvents({
46917          /**
46918          * @event editorinit
46919          * Fired when the editor is initialized - you can add extra handlers here..
46920          * @param {FCKeditor} this
46921          * @param {Object} the FCK object.
46922          */
46923         editorinit : true
46924     });
46925     
46926     
46927 };
46928 Roo.form.FCKeditor.editors = { };
46929 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46930 {
46931     //defaultAutoCreate : {
46932     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46933     //},
46934     // private
46935     /**
46936      * @cfg {Object} fck options - see fck manual for details.
46937      */
46938     fckconfig : false,
46939     
46940     /**
46941      * @cfg {Object} fck toolbar set (Basic or Default)
46942      */
46943     toolbarSet : 'Basic',
46944     /**
46945      * @cfg {Object} fck BasePath
46946      */ 
46947     basePath : '/fckeditor/',
46948     
46949     
46950     frame : false,
46951     
46952     value : '',
46953     
46954    
46955     onRender : function(ct, position)
46956     {
46957         if(!this.el){
46958             this.defaultAutoCreate = {
46959                 tag: "textarea",
46960                 style:"width:300px;height:60px;",
46961                 autocomplete: "new-password"
46962             };
46963         }
46964         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46965         /*
46966         if(this.grow){
46967             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46968             if(this.preventScrollbars){
46969                 this.el.setStyle("overflow", "hidden");
46970             }
46971             this.el.setHeight(this.growMin);
46972         }
46973         */
46974         //console.log('onrender' + this.getId() );
46975         Roo.form.FCKeditor.editors[this.getId()] = this;
46976          
46977
46978         this.replaceTextarea() ;
46979         
46980     },
46981     
46982     getEditor : function() {
46983         return this.fckEditor;
46984     },
46985     /**
46986      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46987      * @param {Mixed} value The value to set
46988      */
46989     
46990     
46991     setValue : function(value)
46992     {
46993         //console.log('setValue: ' + value);
46994         
46995         if(typeof(value) == 'undefined') { // not sure why this is happending...
46996             return;
46997         }
46998         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46999         
47000         //if(!this.el || !this.getEditor()) {
47001         //    this.value = value;
47002             //this.setValue.defer(100,this,[value]);    
47003         //    return;
47004         //} 
47005         
47006         if(!this.getEditor()) {
47007             return;
47008         }
47009         
47010         this.getEditor().SetData(value);
47011         
47012         //
47013
47014     },
47015
47016     /**
47017      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
47018      * @return {Mixed} value The field value
47019      */
47020     getValue : function()
47021     {
47022         
47023         if (this.frame && this.frame.dom.style.display == 'none') {
47024             return Roo.form.FCKeditor.superclass.getValue.call(this);
47025         }
47026         
47027         if(!this.el || !this.getEditor()) {
47028            
47029            // this.getValue.defer(100,this); 
47030             return this.value;
47031         }
47032        
47033         
47034         var value=this.getEditor().GetData();
47035         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
47036         return Roo.form.FCKeditor.superclass.getValue.call(this);
47037         
47038
47039     },
47040
47041     /**
47042      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
47043      * @return {Mixed} value The field value
47044      */
47045     getRawValue : function()
47046     {
47047         if (this.frame && this.frame.dom.style.display == 'none') {
47048             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
47049         }
47050         
47051         if(!this.el || !this.getEditor()) {
47052             //this.getRawValue.defer(100,this); 
47053             return this.value;
47054             return;
47055         }
47056         
47057         
47058         
47059         var value=this.getEditor().GetData();
47060         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
47061         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
47062          
47063     },
47064     
47065     setSize : function(w,h) {
47066         
47067         
47068         
47069         //if (this.frame && this.frame.dom.style.display == 'none') {
47070         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
47071         //    return;
47072         //}
47073         //if(!this.el || !this.getEditor()) {
47074         //    this.setSize.defer(100,this, [w,h]); 
47075         //    return;
47076         //}
47077         
47078         
47079         
47080         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
47081         
47082         this.frame.dom.setAttribute('width', w);
47083         this.frame.dom.setAttribute('height', h);
47084         this.frame.setSize(w,h);
47085         
47086     },
47087     
47088     toggleSourceEdit : function(value) {
47089         
47090       
47091          
47092         this.el.dom.style.display = value ? '' : 'none';
47093         this.frame.dom.style.display = value ?  'none' : '';
47094         
47095     },
47096     
47097     
47098     focus: function(tag)
47099     {
47100         if (this.frame.dom.style.display == 'none') {
47101             return Roo.form.FCKeditor.superclass.focus.call(this);
47102         }
47103         if(!this.el || !this.getEditor()) {
47104             this.focus.defer(100,this, [tag]); 
47105             return;
47106         }
47107         
47108         
47109         
47110         
47111         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
47112         this.getEditor().Focus();
47113         if (tgs.length) {
47114             if (!this.getEditor().Selection.GetSelection()) {
47115                 this.focus.defer(100,this, [tag]); 
47116                 return;
47117             }
47118             
47119             
47120             var r = this.getEditor().EditorDocument.createRange();
47121             r.setStart(tgs[0],0);
47122             r.setEnd(tgs[0],0);
47123             this.getEditor().Selection.GetSelection().removeAllRanges();
47124             this.getEditor().Selection.GetSelection().addRange(r);
47125             this.getEditor().Focus();
47126         }
47127         
47128     },
47129     
47130     
47131     
47132     replaceTextarea : function()
47133     {
47134         if ( document.getElementById( this.getId() + '___Frame' ) ) {
47135             return ;
47136         }
47137         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
47138         //{
47139             // We must check the elements firstly using the Id and then the name.
47140         var oTextarea = document.getElementById( this.getId() );
47141         
47142         var colElementsByName = document.getElementsByName( this.getId() ) ;
47143          
47144         oTextarea.style.display = 'none' ;
47145
47146         if ( oTextarea.tabIndex ) {            
47147             this.TabIndex = oTextarea.tabIndex ;
47148         }
47149         
47150         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
47151         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
47152         this.frame = Roo.get(this.getId() + '___Frame')
47153     },
47154     
47155     _getConfigHtml : function()
47156     {
47157         var sConfig = '' ;
47158
47159         for ( var o in this.fckconfig ) {
47160             sConfig += sConfig.length > 0  ? '&amp;' : '';
47161             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
47162         }
47163
47164         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
47165     },
47166     
47167     
47168     _getIFrameHtml : function()
47169     {
47170         var sFile = 'fckeditor.html' ;
47171         /* no idea what this is about..
47172         try
47173         {
47174             if ( (/fcksource=true/i).test( window.top.location.search ) )
47175                 sFile = 'fckeditor.original.html' ;
47176         }
47177         catch (e) { 
47178         */
47179
47180         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
47181         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
47182         
47183         
47184         var html = '<iframe id="' + this.getId() +
47185             '___Frame" src="' + sLink +
47186             '" width="' + this.width +
47187             '" height="' + this.height + '"' +
47188             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
47189             ' frameborder="0" scrolling="no"></iframe>' ;
47190
47191         return html ;
47192     },
47193     
47194     _insertHtmlBefore : function( html, element )
47195     {
47196         if ( element.insertAdjacentHTML )       {
47197             // IE
47198             element.insertAdjacentHTML( 'beforeBegin', html ) ;
47199         } else { // Gecko
47200             var oRange = document.createRange() ;
47201             oRange.setStartBefore( element ) ;
47202             var oFragment = oRange.createContextualFragment( html );
47203             element.parentNode.insertBefore( oFragment, element ) ;
47204         }
47205     }
47206     
47207     
47208   
47209     
47210     
47211     
47212     
47213
47214 });
47215
47216 //Roo.reg('fckeditor', Roo.form.FCKeditor);
47217
47218 function FCKeditor_OnComplete(editorInstance){
47219     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
47220     f.fckEditor = editorInstance;
47221     //console.log("loaded");
47222     f.fireEvent('editorinit', f, editorInstance);
47223
47224   
47225
47226  
47227
47228
47229
47230
47231
47232
47233
47234
47235
47236
47237
47238
47239
47240
47241
47242 //<script type="text/javascript">
47243 /**
47244  * @class Roo.form.GridField
47245  * @extends Roo.form.Field
47246  * Embed a grid (or editable grid into a form)
47247  * STATUS ALPHA
47248  * 
47249  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
47250  * it needs 
47251  * xgrid.store = Roo.data.Store
47252  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
47253  * xgrid.store.reader = Roo.data.JsonReader 
47254  * 
47255  * 
47256  * @constructor
47257  * Creates a new GridField
47258  * @param {Object} config Configuration options
47259  */
47260 Roo.form.GridField = function(config){
47261     Roo.form.GridField.superclass.constructor.call(this, config);
47262      
47263 };
47264
47265 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
47266     /**
47267      * @cfg {Number} width  - used to restrict width of grid..
47268      */
47269     width : 100,
47270     /**
47271      * @cfg {Number} height - used to restrict height of grid..
47272      */
47273     height : 50,
47274      /**
47275      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
47276          * 
47277          *}
47278      */
47279     xgrid : false, 
47280     /**
47281      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47282      * {tag: "input", type: "checkbox", autocomplete: "off"})
47283      */
47284    // defaultAutoCreate : { tag: 'div' },
47285     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
47286     /**
47287      * @cfg {String} addTitle Text to include for adding a title.
47288      */
47289     addTitle : false,
47290     //
47291     onResize : function(){
47292         Roo.form.Field.superclass.onResize.apply(this, arguments);
47293     },
47294
47295     initEvents : function(){
47296         // Roo.form.Checkbox.superclass.initEvents.call(this);
47297         // has no events...
47298        
47299     },
47300
47301
47302     getResizeEl : function(){
47303         return this.wrap;
47304     },
47305
47306     getPositionEl : function(){
47307         return this.wrap;
47308     },
47309
47310     // private
47311     onRender : function(ct, position){
47312         
47313         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
47314         var style = this.style;
47315         delete this.style;
47316         
47317         Roo.form.GridField.superclass.onRender.call(this, ct, position);
47318         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
47319         this.viewEl = this.wrap.createChild({ tag: 'div' });
47320         if (style) {
47321             this.viewEl.applyStyles(style);
47322         }
47323         if (this.width) {
47324             this.viewEl.setWidth(this.width);
47325         }
47326         if (this.height) {
47327             this.viewEl.setHeight(this.height);
47328         }
47329         //if(this.inputValue !== undefined){
47330         //this.setValue(this.value);
47331         
47332         
47333         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
47334         
47335         
47336         this.grid.render();
47337         this.grid.getDataSource().on('remove', this.refreshValue, this);
47338         this.grid.getDataSource().on('update', this.refreshValue, this);
47339         this.grid.on('afteredit', this.refreshValue, this);
47340  
47341     },
47342      
47343     
47344     /**
47345      * Sets the value of the item. 
47346      * @param {String} either an object  or a string..
47347      */
47348     setValue : function(v){
47349         //this.value = v;
47350         v = v || []; // empty set..
47351         // this does not seem smart - it really only affects memoryproxy grids..
47352         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
47353             var ds = this.grid.getDataSource();
47354             // assumes a json reader..
47355             var data = {}
47356             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
47357             ds.loadData( data);
47358         }
47359         // clear selection so it does not get stale.
47360         if (this.grid.sm) { 
47361             this.grid.sm.clearSelections();
47362         }
47363         
47364         Roo.form.GridField.superclass.setValue.call(this, v);
47365         this.refreshValue();
47366         // should load data in the grid really....
47367     },
47368     
47369     // private
47370     refreshValue: function() {
47371          var val = [];
47372         this.grid.getDataSource().each(function(r) {
47373             val.push(r.data);
47374         });
47375         this.el.dom.value = Roo.encode(val);
47376     }
47377     
47378      
47379     
47380     
47381 });/*
47382  * Based on:
47383  * Ext JS Library 1.1.1
47384  * Copyright(c) 2006-2007, Ext JS, LLC.
47385  *
47386  * Originally Released Under LGPL - original licence link has changed is not relivant.
47387  *
47388  * Fork - LGPL
47389  * <script type="text/javascript">
47390  */
47391 /**
47392  * @class Roo.form.DisplayField
47393  * @extends Roo.form.Field
47394  * A generic Field to display non-editable data.
47395  * @cfg {Boolean} closable (true|false) default false
47396  * @constructor
47397  * Creates a new Display Field item.
47398  * @param {Object} config Configuration options
47399  */
47400 Roo.form.DisplayField = function(config){
47401     Roo.form.DisplayField.superclass.constructor.call(this, config);
47402     
47403     this.addEvents({
47404         /**
47405          * @event close
47406          * Fires after the click the close btn
47407              * @param {Roo.form.DisplayField} this
47408              */
47409         close : true
47410     });
47411 };
47412
47413 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
47414     inputType:      'hidden',
47415     allowBlank:     true,
47416     readOnly:         true,
47417     
47418  
47419     /**
47420      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47421      */
47422     focusClass : undefined,
47423     /**
47424      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47425      */
47426     fieldClass: 'x-form-field',
47427     
47428      /**
47429      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
47430      */
47431     valueRenderer: undefined,
47432     
47433     width: 100,
47434     /**
47435      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47436      * {tag: "input", type: "checkbox", autocomplete: "off"})
47437      */
47438      
47439  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
47440  
47441     closable : false,
47442     
47443     onResize : function(){
47444         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
47445         
47446     },
47447
47448     initEvents : function(){
47449         // Roo.form.Checkbox.superclass.initEvents.call(this);
47450         // has no events...
47451         
47452         if(this.closable){
47453             this.closeEl.on('click', this.onClose, this);
47454         }
47455        
47456     },
47457
47458
47459     getResizeEl : function(){
47460         return this.wrap;
47461     },
47462
47463     getPositionEl : function(){
47464         return this.wrap;
47465     },
47466
47467     // private
47468     onRender : function(ct, position){
47469         
47470         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
47471         //if(this.inputValue !== undefined){
47472         this.wrap = this.el.wrap();
47473         
47474         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
47475         
47476         if(this.closable){
47477             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
47478         }
47479         
47480         if (this.bodyStyle) {
47481             this.viewEl.applyStyles(this.bodyStyle);
47482         }
47483         //this.viewEl.setStyle('padding', '2px');
47484         
47485         this.setValue(this.value);
47486         
47487     },
47488 /*
47489     // private
47490     initValue : Roo.emptyFn,
47491
47492   */
47493
47494         // private
47495     onClick : function(){
47496         
47497     },
47498
47499     /**
47500      * Sets the checked state of the checkbox.
47501      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
47502      */
47503     setValue : function(v){
47504         this.value = v;
47505         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
47506         // this might be called before we have a dom element..
47507         if (!this.viewEl) {
47508             return;
47509         }
47510         this.viewEl.dom.innerHTML = html;
47511         Roo.form.DisplayField.superclass.setValue.call(this, v);
47512
47513     },
47514     
47515     onClose : function(e)
47516     {
47517         e.preventDefault();
47518         
47519         this.fireEvent('close', this);
47520     }
47521 });/*
47522  * 
47523  * Licence- LGPL
47524  * 
47525  */
47526
47527 /**
47528  * @class Roo.form.DayPicker
47529  * @extends Roo.form.Field
47530  * A Day picker show [M] [T] [W] ....
47531  * @constructor
47532  * Creates a new Day Picker
47533  * @param {Object} config Configuration options
47534  */
47535 Roo.form.DayPicker= function(config){
47536     Roo.form.DayPicker.superclass.constructor.call(this, config);
47537      
47538 };
47539
47540 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
47541     /**
47542      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47543      */
47544     focusClass : undefined,
47545     /**
47546      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47547      */
47548     fieldClass: "x-form-field",
47549    
47550     /**
47551      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47552      * {tag: "input", type: "checkbox", autocomplete: "off"})
47553      */
47554     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
47555     
47556    
47557     actionMode : 'viewEl', 
47558     //
47559     // private
47560  
47561     inputType : 'hidden',
47562     
47563      
47564     inputElement: false, // real input element?
47565     basedOn: false, // ????
47566     
47567     isFormField: true, // not sure where this is needed!!!!
47568
47569     onResize : function(){
47570         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
47571         if(!this.boxLabel){
47572             this.el.alignTo(this.wrap, 'c-c');
47573         }
47574     },
47575
47576     initEvents : function(){
47577         Roo.form.Checkbox.superclass.initEvents.call(this);
47578         this.el.on("click", this.onClick,  this);
47579         this.el.on("change", this.onClick,  this);
47580     },
47581
47582
47583     getResizeEl : function(){
47584         return this.wrap;
47585     },
47586
47587     getPositionEl : function(){
47588         return this.wrap;
47589     },
47590
47591     
47592     // private
47593     onRender : function(ct, position){
47594         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
47595        
47596         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
47597         
47598         var r1 = '<table><tr>';
47599         var r2 = '<tr class="x-form-daypick-icons">';
47600         for (var i=0; i < 7; i++) {
47601             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
47602             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
47603         }
47604         
47605         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
47606         viewEl.select('img').on('click', this.onClick, this);
47607         this.viewEl = viewEl;   
47608         
47609         
47610         // this will not work on Chrome!!!
47611         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
47612         this.el.on('propertychange', this.setFromHidden,  this);  //ie
47613         
47614         
47615           
47616
47617     },
47618
47619     // private
47620     initValue : Roo.emptyFn,
47621
47622     /**
47623      * Returns the checked state of the checkbox.
47624      * @return {Boolean} True if checked, else false
47625      */
47626     getValue : function(){
47627         return this.el.dom.value;
47628         
47629     },
47630
47631         // private
47632     onClick : function(e){ 
47633         //this.setChecked(!this.checked);
47634         Roo.get(e.target).toggleClass('x-menu-item-checked');
47635         this.refreshValue();
47636         //if(this.el.dom.checked != this.checked){
47637         //    this.setValue(this.el.dom.checked);
47638        // }
47639     },
47640     
47641     // private
47642     refreshValue : function()
47643     {
47644         var val = '';
47645         this.viewEl.select('img',true).each(function(e,i,n)  {
47646             val += e.is(".x-menu-item-checked") ? String(n) : '';
47647         });
47648         this.setValue(val, true);
47649     },
47650
47651     /**
47652      * Sets the checked state of the checkbox.
47653      * On is always based on a string comparison between inputValue and the param.
47654      * @param {Boolean/String} value - the value to set 
47655      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47656      */
47657     setValue : function(v,suppressEvent){
47658         if (!this.el.dom) {
47659             return;
47660         }
47661         var old = this.el.dom.value ;
47662         this.el.dom.value = v;
47663         if (suppressEvent) {
47664             return ;
47665         }
47666          
47667         // update display..
47668         this.viewEl.select('img',true).each(function(e,i,n)  {
47669             
47670             var on = e.is(".x-menu-item-checked");
47671             var newv = v.indexOf(String(n)) > -1;
47672             if (on != newv) {
47673                 e.toggleClass('x-menu-item-checked');
47674             }
47675             
47676         });
47677         
47678         
47679         this.fireEvent('change', this, v, old);
47680         
47681         
47682     },
47683    
47684     // handle setting of hidden value by some other method!!?!?
47685     setFromHidden: function()
47686     {
47687         if(!this.el){
47688             return;
47689         }
47690         //console.log("SET FROM HIDDEN");
47691         //alert('setFrom hidden');
47692         this.setValue(this.el.dom.value);
47693     },
47694     
47695     onDestroy : function()
47696     {
47697         if(this.viewEl){
47698             Roo.get(this.viewEl).remove();
47699         }
47700          
47701         Roo.form.DayPicker.superclass.onDestroy.call(this);
47702     }
47703
47704 });/*
47705  * RooJS Library 1.1.1
47706  * Copyright(c) 2008-2011  Alan Knowles
47707  *
47708  * License - LGPL
47709  */
47710  
47711
47712 /**
47713  * @class Roo.form.ComboCheck
47714  * @extends Roo.form.ComboBox
47715  * A combobox for multiple select items.
47716  *
47717  * FIXME - could do with a reset button..
47718  * 
47719  * @constructor
47720  * Create a new ComboCheck
47721  * @param {Object} config Configuration options
47722  */
47723 Roo.form.ComboCheck = function(config){
47724     Roo.form.ComboCheck.superclass.constructor.call(this, config);
47725     // should verify some data...
47726     // like
47727     // hiddenName = required..
47728     // displayField = required
47729     // valudField == required
47730     var req= [ 'hiddenName', 'displayField', 'valueField' ];
47731     var _t = this;
47732     Roo.each(req, function(e) {
47733         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
47734             throw "Roo.form.ComboCheck : missing value for: " + e;
47735         }
47736     });
47737     
47738     
47739 };
47740
47741 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
47742      
47743      
47744     editable : false,
47745      
47746     selectedClass: 'x-menu-item-checked', 
47747     
47748     // private
47749     onRender : function(ct, position){
47750         var _t = this;
47751         
47752         
47753         
47754         if(!this.tpl){
47755             var cls = 'x-combo-list';
47756
47757             
47758             this.tpl =  new Roo.Template({
47759                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
47760                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47761                    '<span>{' + this.displayField + '}</span>' +
47762                     '</div>' 
47763                 
47764             });
47765         }
47766  
47767         
47768         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47769         this.view.singleSelect = false;
47770         this.view.multiSelect = true;
47771         this.view.toggleSelect = true;
47772         this.pageTb.add(new Roo.Toolbar.Fill(), {
47773             
47774             text: 'Done',
47775             handler: function()
47776             {
47777                 _t.collapse();
47778             }
47779         });
47780     },
47781     
47782     onViewOver : function(e, t){
47783         // do nothing...
47784         return;
47785         
47786     },
47787     
47788     onViewClick : function(doFocus,index){
47789         return;
47790         
47791     },
47792     select: function () {
47793         //Roo.log("SELECT CALLED");
47794     },
47795      
47796     selectByValue : function(xv, scrollIntoView){
47797         var ar = this.getValueArray();
47798         var sels = [];
47799         
47800         Roo.each(ar, function(v) {
47801             if(v === undefined || v === null){
47802                 return;
47803             }
47804             var r = this.findRecord(this.valueField, v);
47805             if(r){
47806                 sels.push(this.store.indexOf(r))
47807                 
47808             }
47809         },this);
47810         this.view.select(sels);
47811         return false;
47812     },
47813     
47814     
47815     
47816     onSelect : function(record, index){
47817        // Roo.log("onselect Called");
47818        // this is only called by the clear button now..
47819         this.view.clearSelections();
47820         this.setValue('[]');
47821         if (this.value != this.valueBefore) {
47822             this.fireEvent('change', this, this.value, this.valueBefore);
47823             this.valueBefore = this.value;
47824         }
47825     },
47826     getValueArray : function()
47827     {
47828         var ar = [] ;
47829         
47830         try {
47831             //Roo.log(this.value);
47832             if (typeof(this.value) == 'undefined') {
47833                 return [];
47834             }
47835             var ar = Roo.decode(this.value);
47836             return  ar instanceof Array ? ar : []; //?? valid?
47837             
47838         } catch(e) {
47839             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47840             return [];
47841         }
47842          
47843     },
47844     expand : function ()
47845     {
47846         
47847         Roo.form.ComboCheck.superclass.expand.call(this);
47848         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47849         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47850         
47851
47852     },
47853     
47854     collapse : function(){
47855         Roo.form.ComboCheck.superclass.collapse.call(this);
47856         var sl = this.view.getSelectedIndexes();
47857         var st = this.store;
47858         var nv = [];
47859         var tv = [];
47860         var r;
47861         Roo.each(sl, function(i) {
47862             r = st.getAt(i);
47863             nv.push(r.get(this.valueField));
47864         },this);
47865         this.setValue(Roo.encode(nv));
47866         if (this.value != this.valueBefore) {
47867
47868             this.fireEvent('change', this, this.value, this.valueBefore);
47869             this.valueBefore = this.value;
47870         }
47871         
47872     },
47873     
47874     setValue : function(v){
47875         // Roo.log(v);
47876         this.value = v;
47877         
47878         var vals = this.getValueArray();
47879         var tv = [];
47880         Roo.each(vals, function(k) {
47881             var r = this.findRecord(this.valueField, k);
47882             if(r){
47883                 tv.push(r.data[this.displayField]);
47884             }else if(this.valueNotFoundText !== undefined){
47885                 tv.push( this.valueNotFoundText );
47886             }
47887         },this);
47888        // Roo.log(tv);
47889         
47890         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47891         this.hiddenField.value = v;
47892         this.value = v;
47893     }
47894     
47895 });/*
47896  * Based on:
47897  * Ext JS Library 1.1.1
47898  * Copyright(c) 2006-2007, Ext JS, LLC.
47899  *
47900  * Originally Released Under LGPL - original licence link has changed is not relivant.
47901  *
47902  * Fork - LGPL
47903  * <script type="text/javascript">
47904  */
47905  
47906 /**
47907  * @class Roo.form.Signature
47908  * @extends Roo.form.Field
47909  * Signature field.  
47910  * @constructor
47911  * 
47912  * @param {Object} config Configuration options
47913  */
47914
47915 Roo.form.Signature = function(config){
47916     Roo.form.Signature.superclass.constructor.call(this, config);
47917     
47918     this.addEvents({// not in used??
47919          /**
47920          * @event confirm
47921          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47922              * @param {Roo.form.Signature} combo This combo box
47923              */
47924         'confirm' : true,
47925         /**
47926          * @event reset
47927          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47928              * @param {Roo.form.ComboBox} combo This combo box
47929              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47930              */
47931         'reset' : true
47932     });
47933 };
47934
47935 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47936     /**
47937      * @cfg {Object} labels Label to use when rendering a form.
47938      * defaults to 
47939      * labels : { 
47940      *      clear : "Clear",
47941      *      confirm : "Confirm"
47942      *  }
47943      */
47944     labels : { 
47945         clear : "Clear",
47946         confirm : "Confirm"
47947     },
47948     /**
47949      * @cfg {Number} width The signature panel width (defaults to 300)
47950      */
47951     width: 300,
47952     /**
47953      * @cfg {Number} height The signature panel height (defaults to 100)
47954      */
47955     height : 100,
47956     /**
47957      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47958      */
47959     allowBlank : false,
47960     
47961     //private
47962     // {Object} signPanel The signature SVG panel element (defaults to {})
47963     signPanel : {},
47964     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47965     isMouseDown : false,
47966     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47967     isConfirmed : false,
47968     // {String} signatureTmp SVG mapping string (defaults to empty string)
47969     signatureTmp : '',
47970     
47971     
47972     defaultAutoCreate : { // modified by initCompnoent..
47973         tag: "input",
47974         type:"hidden"
47975     },
47976
47977     // private
47978     onRender : function(ct, position){
47979         
47980         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47981         
47982         this.wrap = this.el.wrap({
47983             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47984         });
47985         
47986         this.createToolbar(this);
47987         this.signPanel = this.wrap.createChild({
47988                 tag: 'div',
47989                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47990             }, this.el
47991         );
47992             
47993         this.svgID = Roo.id();
47994         this.svgEl = this.signPanel.createChild({
47995               xmlns : 'http://www.w3.org/2000/svg',
47996               tag : 'svg',
47997               id : this.svgID + "-svg",
47998               width: this.width,
47999               height: this.height,
48000               viewBox: '0 0 '+this.width+' '+this.height,
48001               cn : [
48002                 {
48003                     tag: "rect",
48004                     id: this.svgID + "-svg-r",
48005                     width: this.width,
48006                     height: this.height,
48007                     fill: "#ffa"
48008                 },
48009                 {
48010                     tag: "line",
48011                     id: this.svgID + "-svg-l",
48012                     x1: "0", // start
48013                     y1: (this.height*0.8), // start set the line in 80% of height
48014                     x2: this.width, // end
48015                     y2: (this.height*0.8), // end set the line in 80% of height
48016                     'stroke': "#666",
48017                     'stroke-width': "1",
48018                     'stroke-dasharray': "3",
48019                     'shape-rendering': "crispEdges",
48020                     'pointer-events': "none"
48021                 },
48022                 {
48023                     tag: "path",
48024                     id: this.svgID + "-svg-p",
48025                     'stroke': "navy",
48026                     'stroke-width': "3",
48027                     'fill': "none",
48028                     'pointer-events': 'none'
48029                 }
48030               ]
48031         });
48032         this.createSVG();
48033         this.svgBox = this.svgEl.dom.getScreenCTM();
48034     },
48035     createSVG : function(){ 
48036         var svg = this.signPanel;
48037         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
48038         var t = this;
48039
48040         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
48041         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
48042         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
48043         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
48044         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
48045         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
48046         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
48047         
48048     },
48049     isTouchEvent : function(e){
48050         return e.type.match(/^touch/);
48051     },
48052     getCoords : function (e) {
48053         var pt    = this.svgEl.dom.createSVGPoint();
48054         pt.x = e.clientX; 
48055         pt.y = e.clientY;
48056         if (this.isTouchEvent(e)) {
48057             pt.x =  e.targetTouches[0].clientX;
48058             pt.y = e.targetTouches[0].clientY;
48059         }
48060         var a = this.svgEl.dom.getScreenCTM();
48061         var b = a.inverse();
48062         var mx = pt.matrixTransform(b);
48063         return mx.x + ',' + mx.y;
48064     },
48065     //mouse event headler 
48066     down : function (e) {
48067         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
48068         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
48069         
48070         this.isMouseDown = true;
48071         
48072         e.preventDefault();
48073     },
48074     move : function (e) {
48075         if (this.isMouseDown) {
48076             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
48077             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
48078         }
48079         
48080         e.preventDefault();
48081     },
48082     up : function (e) {
48083         this.isMouseDown = false;
48084         var sp = this.signatureTmp.split(' ');
48085         
48086         if(sp.length > 1){
48087             if(!sp[sp.length-2].match(/^L/)){
48088                 sp.pop();
48089                 sp.pop();
48090                 sp.push("");
48091                 this.signatureTmp = sp.join(" ");
48092             }
48093         }
48094         if(this.getValue() != this.signatureTmp){
48095             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48096             this.isConfirmed = false;
48097         }
48098         e.preventDefault();
48099     },
48100     
48101     /**
48102      * Protected method that will not generally be called directly. It
48103      * is called when the editor creates its toolbar. Override this method if you need to
48104      * add custom toolbar buttons.
48105      * @param {HtmlEditor} editor
48106      */
48107     createToolbar : function(editor){
48108          function btn(id, toggle, handler){
48109             var xid = fid + '-'+ id ;
48110             return {
48111                 id : xid,
48112                 cmd : id,
48113                 cls : 'x-btn-icon x-edit-'+id,
48114                 enableToggle:toggle !== false,
48115                 scope: editor, // was editor...
48116                 handler:handler||editor.relayBtnCmd,
48117                 clickEvent:'mousedown',
48118                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
48119                 tabIndex:-1
48120             };
48121         }
48122         
48123         
48124         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
48125         this.tb = tb;
48126         this.tb.add(
48127            {
48128                 cls : ' x-signature-btn x-signature-'+id,
48129                 scope: editor, // was editor...
48130                 handler: this.reset,
48131                 clickEvent:'mousedown',
48132                 text: this.labels.clear
48133             },
48134             {
48135                  xtype : 'Fill',
48136                  xns: Roo.Toolbar
48137             }, 
48138             {
48139                 cls : '  x-signature-btn x-signature-'+id,
48140                 scope: editor, // was editor...
48141                 handler: this.confirmHandler,
48142                 clickEvent:'mousedown',
48143                 text: this.labels.confirm
48144             }
48145         );
48146     
48147     },
48148     //public
48149     /**
48150      * when user is clicked confirm then show this image.....
48151      * 
48152      * @return {String} Image Data URI
48153      */
48154     getImageDataURI : function(){
48155         var svg = this.svgEl.dom.parentNode.innerHTML;
48156         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
48157         return src; 
48158     },
48159     /**
48160      * 
48161      * @return {Boolean} this.isConfirmed
48162      */
48163     getConfirmed : function(){
48164         return this.isConfirmed;
48165     },
48166     /**
48167      * 
48168      * @return {Number} this.width
48169      */
48170     getWidth : function(){
48171         return this.width;
48172     },
48173     /**
48174      * 
48175      * @return {Number} this.height
48176      */
48177     getHeight : function(){
48178         return this.height;
48179     },
48180     // private
48181     getSignature : function(){
48182         return this.signatureTmp;
48183     },
48184     // private
48185     reset : function(){
48186         this.signatureTmp = '';
48187         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48188         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
48189         this.isConfirmed = false;
48190         Roo.form.Signature.superclass.reset.call(this);
48191     },
48192     setSignature : function(s){
48193         this.signatureTmp = s;
48194         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48195         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
48196         this.setValue(s);
48197         this.isConfirmed = false;
48198         Roo.form.Signature.superclass.reset.call(this);
48199     }, 
48200     test : function(){
48201 //        Roo.log(this.signPanel.dom.contentWindow.up())
48202     },
48203     //private
48204     setConfirmed : function(){
48205         
48206         
48207         
48208 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
48209     },
48210     // private
48211     confirmHandler : function(){
48212         if(!this.getSignature()){
48213             return;
48214         }
48215         
48216         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
48217         this.setValue(this.getSignature());
48218         this.isConfirmed = true;
48219         
48220         this.fireEvent('confirm', this);
48221     },
48222     // private
48223     // Subclasses should provide the validation implementation by overriding this
48224     validateValue : function(value){
48225         if(this.allowBlank){
48226             return true;
48227         }
48228         
48229         if(this.isConfirmed){
48230             return true;
48231         }
48232         return false;
48233     }
48234 });/*
48235  * Based on:
48236  * Ext JS Library 1.1.1
48237  * Copyright(c) 2006-2007, Ext JS, LLC.
48238  *
48239  * Originally Released Under LGPL - original licence link has changed is not relivant.
48240  *
48241  * Fork - LGPL
48242  * <script type="text/javascript">
48243  */
48244  
48245
48246 /**
48247  * @class Roo.form.ComboBox
48248  * @extends Roo.form.TriggerField
48249  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
48250  * @constructor
48251  * Create a new ComboBox.
48252  * @param {Object} config Configuration options
48253  */
48254 Roo.form.Select = function(config){
48255     Roo.form.Select.superclass.constructor.call(this, config);
48256      
48257 };
48258
48259 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
48260     /**
48261      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
48262      */
48263     /**
48264      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
48265      * rendering into an Roo.Editor, defaults to false)
48266      */
48267     /**
48268      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
48269      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
48270      */
48271     /**
48272      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
48273      */
48274     /**
48275      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
48276      * the dropdown list (defaults to undefined, with no header element)
48277      */
48278
48279      /**
48280      * @cfg {String/Roo.Template} tpl The template to use to render the output
48281      */
48282      
48283     // private
48284     defaultAutoCreate : {tag: "select"  },
48285     /**
48286      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
48287      */
48288     listWidth: undefined,
48289     /**
48290      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
48291      * mode = 'remote' or 'text' if mode = 'local')
48292      */
48293     displayField: undefined,
48294     /**
48295      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
48296      * mode = 'remote' or 'value' if mode = 'local'). 
48297      * Note: use of a valueField requires the user make a selection
48298      * in order for a value to be mapped.
48299      */
48300     valueField: undefined,
48301     
48302     
48303     /**
48304      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
48305      * field's data value (defaults to the underlying DOM element's name)
48306      */
48307     hiddenName: undefined,
48308     /**
48309      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
48310      */
48311     listClass: '',
48312     /**
48313      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
48314      */
48315     selectedClass: 'x-combo-selected',
48316     /**
48317      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
48318      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
48319      * which displays a downward arrow icon).
48320      */
48321     triggerClass : 'x-form-arrow-trigger',
48322     /**
48323      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
48324      */
48325     shadow:'sides',
48326     /**
48327      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
48328      * anchor positions (defaults to 'tl-bl')
48329      */
48330     listAlign: 'tl-bl?',
48331     /**
48332      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
48333      */
48334     maxHeight: 300,
48335     /**
48336      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
48337      * query specified by the allQuery config option (defaults to 'query')
48338      */
48339     triggerAction: 'query',
48340     /**
48341      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
48342      * (defaults to 4, does not apply if editable = false)
48343      */
48344     minChars : 4,
48345     /**
48346      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
48347      * delay (typeAheadDelay) if it matches a known value (defaults to false)
48348      */
48349     typeAhead: false,
48350     /**
48351      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
48352      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
48353      */
48354     queryDelay: 500,
48355     /**
48356      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
48357      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
48358      */
48359     pageSize: 0,
48360     /**
48361      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
48362      * when editable = true (defaults to false)
48363      */
48364     selectOnFocus:false,
48365     /**
48366      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
48367      */
48368     queryParam: 'query',
48369     /**
48370      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
48371      * when mode = 'remote' (defaults to 'Loading...')
48372      */
48373     loadingText: 'Loading...',
48374     /**
48375      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
48376      */
48377     resizable: false,
48378     /**
48379      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
48380      */
48381     handleHeight : 8,
48382     /**
48383      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
48384      * traditional select (defaults to true)
48385      */
48386     editable: true,
48387     /**
48388      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
48389      */
48390     allQuery: '',
48391     /**
48392      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
48393      */
48394     mode: 'remote',
48395     /**
48396      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
48397      * listWidth has a higher value)
48398      */
48399     minListWidth : 70,
48400     /**
48401      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
48402      * allow the user to set arbitrary text into the field (defaults to false)
48403      */
48404     forceSelection:false,
48405     /**
48406      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
48407      * if typeAhead = true (defaults to 250)
48408      */
48409     typeAheadDelay : 250,
48410     /**
48411      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
48412      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
48413      */
48414     valueNotFoundText : undefined,
48415     
48416     /**
48417      * @cfg {String} defaultValue The value displayed after loading the store.
48418      */
48419     defaultValue: '',
48420     
48421     /**
48422      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
48423      */
48424     blockFocus : false,
48425     
48426     /**
48427      * @cfg {Boolean} disableClear Disable showing of clear button.
48428      */
48429     disableClear : false,
48430     /**
48431      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
48432      */
48433     alwaysQuery : false,
48434     
48435     //private
48436     addicon : false,
48437     editicon: false,
48438     
48439     // element that contains real text value.. (when hidden is used..)
48440      
48441     // private
48442     onRender : function(ct, position){
48443         Roo.form.Field.prototype.onRender.call(this, ct, position);
48444         
48445         if(this.store){
48446             this.store.on('beforeload', this.onBeforeLoad, this);
48447             this.store.on('load', this.onLoad, this);
48448             this.store.on('loadexception', this.onLoadException, this);
48449             this.store.load({});
48450         }
48451         
48452         
48453         
48454     },
48455
48456     // private
48457     initEvents : function(){
48458         //Roo.form.ComboBox.superclass.initEvents.call(this);
48459  
48460     },
48461
48462     onDestroy : function(){
48463        
48464         if(this.store){
48465             this.store.un('beforeload', this.onBeforeLoad, this);
48466             this.store.un('load', this.onLoad, this);
48467             this.store.un('loadexception', this.onLoadException, this);
48468         }
48469         //Roo.form.ComboBox.superclass.onDestroy.call(this);
48470     },
48471
48472     // private
48473     fireKey : function(e){
48474         if(e.isNavKeyPress() && !this.list.isVisible()){
48475             this.fireEvent("specialkey", this, e);
48476         }
48477     },
48478
48479     // private
48480     onResize: function(w, h){
48481         
48482         return; 
48483     
48484         
48485     },
48486
48487     /**
48488      * Allow or prevent the user from directly editing the field text.  If false is passed,
48489      * the user will only be able to select from the items defined in the dropdown list.  This method
48490      * is the runtime equivalent of setting the 'editable' config option at config time.
48491      * @param {Boolean} value True to allow the user to directly edit the field text
48492      */
48493     setEditable : function(value){
48494          
48495     },
48496
48497     // private
48498     onBeforeLoad : function(){
48499         
48500         Roo.log("Select before load");
48501         return;
48502     
48503         this.innerList.update(this.loadingText ?
48504                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
48505         //this.restrictHeight();
48506         this.selectedIndex = -1;
48507     },
48508
48509     // private
48510     onLoad : function(){
48511
48512     
48513         var dom = this.el.dom;
48514         dom.innerHTML = '';
48515          var od = dom.ownerDocument;
48516          
48517         if (this.emptyText) {
48518             var op = od.createElement('option');
48519             op.setAttribute('value', '');
48520             op.innerHTML = String.format('{0}', this.emptyText);
48521             dom.appendChild(op);
48522         }
48523         if(this.store.getCount() > 0){
48524            
48525             var vf = this.valueField;
48526             var df = this.displayField;
48527             this.store.data.each(function(r) {
48528                 // which colmsn to use... testing - cdoe / title..
48529                 var op = od.createElement('option');
48530                 op.setAttribute('value', r.data[vf]);
48531                 op.innerHTML = String.format('{0}', r.data[df]);
48532                 dom.appendChild(op);
48533             });
48534             if (typeof(this.defaultValue != 'undefined')) {
48535                 this.setValue(this.defaultValue);
48536             }
48537             
48538              
48539         }else{
48540             //this.onEmptyResults();
48541         }
48542         //this.el.focus();
48543     },
48544     // private
48545     onLoadException : function()
48546     {
48547         dom.innerHTML = '';
48548             
48549         Roo.log("Select on load exception");
48550         return;
48551     
48552         this.collapse();
48553         Roo.log(this.store.reader.jsonData);
48554         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
48555             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
48556         }
48557         
48558         
48559     },
48560     // private
48561     onTypeAhead : function(){
48562          
48563     },
48564
48565     // private
48566     onSelect : function(record, index){
48567         Roo.log('on select?');
48568         return;
48569         if(this.fireEvent('beforeselect', this, record, index) !== false){
48570             this.setFromData(index > -1 ? record.data : false);
48571             this.collapse();
48572             this.fireEvent('select', this, record, index);
48573         }
48574     },
48575
48576     /**
48577      * Returns the currently selected field value or empty string if no value is set.
48578      * @return {String} value The selected value
48579      */
48580     getValue : function(){
48581         var dom = this.el.dom;
48582         this.value = dom.options[dom.selectedIndex].value;
48583         return this.value;
48584         
48585     },
48586
48587     /**
48588      * Clears any text/value currently set in the field
48589      */
48590     clearValue : function(){
48591         this.value = '';
48592         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
48593         
48594     },
48595
48596     /**
48597      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
48598      * will be displayed in the field.  If the value does not match the data value of an existing item,
48599      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
48600      * Otherwise the field will be blank (although the value will still be set).
48601      * @param {String} value The value to match
48602      */
48603     setValue : function(v){
48604         var d = this.el.dom;
48605         for (var i =0; i < d.options.length;i++) {
48606             if (v == d.options[i].value) {
48607                 d.selectedIndex = i;
48608                 this.value = v;
48609                 return;
48610             }
48611         }
48612         this.clearValue();
48613     },
48614     /**
48615      * @property {Object} the last set data for the element
48616      */
48617     
48618     lastData : false,
48619     /**
48620      * Sets the value of the field based on a object which is related to the record format for the store.
48621      * @param {Object} value the value to set as. or false on reset?
48622      */
48623     setFromData : function(o){
48624         Roo.log('setfrom data?');
48625          
48626         
48627         
48628     },
48629     // private
48630     reset : function(){
48631         this.clearValue();
48632     },
48633     // private
48634     findRecord : function(prop, value){
48635         
48636         return false;
48637     
48638         var record;
48639         if(this.store.getCount() > 0){
48640             this.store.each(function(r){
48641                 if(r.data[prop] == value){
48642                     record = r;
48643                     return false;
48644                 }
48645                 return true;
48646             });
48647         }
48648         return record;
48649     },
48650     
48651     getName: function()
48652     {
48653         // returns hidden if it's set..
48654         if (!this.rendered) {return ''};
48655         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
48656         
48657     },
48658      
48659
48660     
48661
48662     // private
48663     onEmptyResults : function(){
48664         Roo.log('empty results');
48665         //this.collapse();
48666     },
48667
48668     /**
48669      * Returns true if the dropdown list is expanded, else false.
48670      */
48671     isExpanded : function(){
48672         return false;
48673     },
48674
48675     /**
48676      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
48677      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48678      * @param {String} value The data value of the item to select
48679      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48680      * selected item if it is not currently in view (defaults to true)
48681      * @return {Boolean} True if the value matched an item in the list, else false
48682      */
48683     selectByValue : function(v, scrollIntoView){
48684         Roo.log('select By Value');
48685         return false;
48686     
48687         if(v !== undefined && v !== null){
48688             var r = this.findRecord(this.valueField || this.displayField, v);
48689             if(r){
48690                 this.select(this.store.indexOf(r), scrollIntoView);
48691                 return true;
48692             }
48693         }
48694         return false;
48695     },
48696
48697     /**
48698      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
48699      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48700      * @param {Number} index The zero-based index of the list item to select
48701      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48702      * selected item if it is not currently in view (defaults to true)
48703      */
48704     select : function(index, scrollIntoView){
48705         Roo.log('select ');
48706         return  ;
48707         
48708         this.selectedIndex = index;
48709         this.view.select(index);
48710         if(scrollIntoView !== false){
48711             var el = this.view.getNode(index);
48712             if(el){
48713                 this.innerList.scrollChildIntoView(el, false);
48714             }
48715         }
48716     },
48717
48718       
48719
48720     // private
48721     validateBlur : function(){
48722         
48723         return;
48724         
48725     },
48726
48727     // private
48728     initQuery : function(){
48729         this.doQuery(this.getRawValue());
48730     },
48731
48732     // private
48733     doForce : function(){
48734         if(this.el.dom.value.length > 0){
48735             this.el.dom.value =
48736                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
48737              
48738         }
48739     },
48740
48741     /**
48742      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
48743      * query allowing the query action to be canceled if needed.
48744      * @param {String} query The SQL query to execute
48745      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
48746      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
48747      * saved in the current store (defaults to false)
48748      */
48749     doQuery : function(q, forceAll){
48750         
48751         Roo.log('doQuery?');
48752         if(q === undefined || q === null){
48753             q = '';
48754         }
48755         var qe = {
48756             query: q,
48757             forceAll: forceAll,
48758             combo: this,
48759             cancel:false
48760         };
48761         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48762             return false;
48763         }
48764         q = qe.query;
48765         forceAll = qe.forceAll;
48766         if(forceAll === true || (q.length >= this.minChars)){
48767             if(this.lastQuery != q || this.alwaysQuery){
48768                 this.lastQuery = q;
48769                 if(this.mode == 'local'){
48770                     this.selectedIndex = -1;
48771                     if(forceAll){
48772                         this.store.clearFilter();
48773                     }else{
48774                         this.store.filter(this.displayField, q);
48775                     }
48776                     this.onLoad();
48777                 }else{
48778                     this.store.baseParams[this.queryParam] = q;
48779                     this.store.load({
48780                         params: this.getParams(q)
48781                     });
48782                     this.expand();
48783                 }
48784             }else{
48785                 this.selectedIndex = -1;
48786                 this.onLoad();   
48787             }
48788         }
48789     },
48790
48791     // private
48792     getParams : function(q){
48793         var p = {};
48794         //p[this.queryParam] = q;
48795         if(this.pageSize){
48796             p.start = 0;
48797             p.limit = this.pageSize;
48798         }
48799         return p;
48800     },
48801
48802     /**
48803      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48804      */
48805     collapse : function(){
48806         
48807     },
48808
48809     // private
48810     collapseIf : function(e){
48811         
48812     },
48813
48814     /**
48815      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48816      */
48817     expand : function(){
48818         
48819     } ,
48820
48821     // private
48822      
48823
48824     /** 
48825     * @cfg {Boolean} grow 
48826     * @hide 
48827     */
48828     /** 
48829     * @cfg {Number} growMin 
48830     * @hide 
48831     */
48832     /** 
48833     * @cfg {Number} growMax 
48834     * @hide 
48835     */
48836     /**
48837      * @hide
48838      * @method autoSize
48839      */
48840     
48841     setWidth : function()
48842     {
48843         
48844     },
48845     getResizeEl : function(){
48846         return this.el;
48847     }
48848 });//<script type="text/javasscript">
48849  
48850
48851 /**
48852  * @class Roo.DDView
48853  * A DnD enabled version of Roo.View.
48854  * @param {Element/String} container The Element in which to create the View.
48855  * @param {String} tpl The template string used to create the markup for each element of the View
48856  * @param {Object} config The configuration properties. These include all the config options of
48857  * {@link Roo.View} plus some specific to this class.<br>
48858  * <p>
48859  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48860  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48861  * <p>
48862  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48863 .x-view-drag-insert-above {
48864         border-top:1px dotted #3366cc;
48865 }
48866 .x-view-drag-insert-below {
48867         border-bottom:1px dotted #3366cc;
48868 }
48869 </code></pre>
48870  * 
48871  */
48872  
48873 Roo.DDView = function(container, tpl, config) {
48874     Roo.DDView.superclass.constructor.apply(this, arguments);
48875     this.getEl().setStyle("outline", "0px none");
48876     this.getEl().unselectable();
48877     if (this.dragGroup) {
48878                 this.setDraggable(this.dragGroup.split(","));
48879     }
48880     if (this.dropGroup) {
48881                 this.setDroppable(this.dropGroup.split(","));
48882     }
48883     if (this.deletable) {
48884         this.setDeletable();
48885     }
48886     this.isDirtyFlag = false;
48887         this.addEvents({
48888                 "drop" : true
48889         });
48890 };
48891
48892 Roo.extend(Roo.DDView, Roo.View, {
48893 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48894 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48895 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48896 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48897
48898         isFormField: true,
48899
48900         reset: Roo.emptyFn,
48901         
48902         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48903
48904         validate: function() {
48905                 return true;
48906         },
48907         
48908         destroy: function() {
48909                 this.purgeListeners();
48910                 this.getEl.removeAllListeners();
48911                 this.getEl().remove();
48912                 if (this.dragZone) {
48913                         if (this.dragZone.destroy) {
48914                                 this.dragZone.destroy();
48915                         }
48916                 }
48917                 if (this.dropZone) {
48918                         if (this.dropZone.destroy) {
48919                                 this.dropZone.destroy();
48920                         }
48921                 }
48922         },
48923
48924 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48925         getName: function() {
48926                 return this.name;
48927         },
48928
48929 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48930         setValue: function(v) {
48931                 if (!this.store) {
48932                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48933                 }
48934                 var data = {};
48935                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48936                 this.store.proxy = new Roo.data.MemoryProxy(data);
48937                 this.store.load();
48938         },
48939
48940 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48941         getValue: function() {
48942                 var result = '(';
48943                 this.store.each(function(rec) {
48944                         result += rec.id + ',';
48945                 });
48946                 return result.substr(0, result.length - 1) + ')';
48947         },
48948         
48949         getIds: function() {
48950                 var i = 0, result = new Array(this.store.getCount());
48951                 this.store.each(function(rec) {
48952                         result[i++] = rec.id;
48953                 });
48954                 return result;
48955         },
48956         
48957         isDirty: function() {
48958                 return this.isDirtyFlag;
48959         },
48960
48961 /**
48962  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48963  *      whole Element becomes the target, and this causes the drop gesture to append.
48964  */
48965     getTargetFromEvent : function(e) {
48966                 var target = e.getTarget();
48967                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48968                 target = target.parentNode;
48969                 }
48970                 if (!target) {
48971                         target = this.el.dom.lastChild || this.el.dom;
48972                 }
48973                 return target;
48974     },
48975
48976 /**
48977  *      Create the drag data which consists of an object which has the property "ddel" as
48978  *      the drag proxy element. 
48979  */
48980     getDragData : function(e) {
48981         var target = this.findItemFromChild(e.getTarget());
48982                 if(target) {
48983                         this.handleSelection(e);
48984                         var selNodes = this.getSelectedNodes();
48985             var dragData = {
48986                 source: this,
48987                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48988                 nodes: selNodes,
48989                 records: []
48990                         };
48991                         var selectedIndices = this.getSelectedIndexes();
48992                         for (var i = 0; i < selectedIndices.length; i++) {
48993                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48994                         }
48995                         if (selNodes.length == 1) {
48996                                 dragData.ddel = target.cloneNode(true); // the div element
48997                         } else {
48998                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48999                                 div.className = 'multi-proxy';
49000                                 for (var i = 0, len = selNodes.length; i < len; i++) {
49001                                         div.appendChild(selNodes[i].cloneNode(true));
49002                                 }
49003                                 dragData.ddel = div;
49004                         }
49005             //console.log(dragData)
49006             //console.log(dragData.ddel.innerHTML)
49007                         return dragData;
49008                 }
49009         //console.log('nodragData')
49010                 return false;
49011     },
49012     
49013 /**     Specify to which ddGroup items in this DDView may be dragged. */
49014     setDraggable: function(ddGroup) {
49015         if (ddGroup instanceof Array) {
49016                 Roo.each(ddGroup, this.setDraggable, this);
49017                 return;
49018         }
49019         if (this.dragZone) {
49020                 this.dragZone.addToGroup(ddGroup);
49021         } else {
49022                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
49023                                 containerScroll: true,
49024                                 ddGroup: ddGroup 
49025
49026                         });
49027 //                      Draggability implies selection. DragZone's mousedown selects the element.
49028                         if (!this.multiSelect) { this.singleSelect = true; }
49029
49030 //                      Wire the DragZone's handlers up to methods in *this*
49031                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
49032                 }
49033     },
49034
49035 /**     Specify from which ddGroup this DDView accepts drops. */
49036     setDroppable: function(ddGroup) {
49037         if (ddGroup instanceof Array) {
49038                 Roo.each(ddGroup, this.setDroppable, this);
49039                 return;
49040         }
49041         if (this.dropZone) {
49042                 this.dropZone.addToGroup(ddGroup);
49043         } else {
49044                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
49045                                 containerScroll: true,
49046                                 ddGroup: ddGroup
49047                         });
49048
49049 //                      Wire the DropZone's handlers up to methods in *this*
49050                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
49051                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
49052                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
49053                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
49054                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
49055                 }
49056     },
49057
49058 /**     Decide whether to drop above or below a View node. */
49059     getDropPoint : function(e, n, dd){
49060         if (n == this.el.dom) { return "above"; }
49061                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
49062                 var c = t + (b - t) / 2;
49063                 var y = Roo.lib.Event.getPageY(e);
49064                 if(y <= c) {
49065                         return "above";
49066                 }else{
49067                         return "below";
49068                 }
49069     },
49070
49071     onNodeEnter : function(n, dd, e, data){
49072                 return false;
49073     },
49074     
49075     onNodeOver : function(n, dd, e, data){
49076                 var pt = this.getDropPoint(e, n, dd);
49077                 // set the insert point style on the target node
49078                 var dragElClass = this.dropNotAllowed;
49079                 if (pt) {
49080                         var targetElClass;
49081                         if (pt == "above"){
49082                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
49083                                 targetElClass = "x-view-drag-insert-above";
49084                         } else {
49085                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
49086                                 targetElClass = "x-view-drag-insert-below";
49087                         }
49088                         if (this.lastInsertClass != targetElClass){
49089                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
49090                                 this.lastInsertClass = targetElClass;
49091                         }
49092                 }
49093                 return dragElClass;
49094         },
49095
49096     onNodeOut : function(n, dd, e, data){
49097                 this.removeDropIndicators(n);
49098     },
49099
49100     onNodeDrop : function(n, dd, e, data){
49101         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
49102                 return false;
49103         }
49104         var pt = this.getDropPoint(e, n, dd);
49105                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
49106                 if (pt == "below") { insertAt++; }
49107                 for (var i = 0; i < data.records.length; i++) {
49108                         var r = data.records[i];
49109                         var dup = this.store.getById(r.id);
49110                         if (dup && (dd != this.dragZone)) {
49111                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
49112                         } else {
49113                                 if (data.copy) {
49114                                         this.store.insert(insertAt++, r.copy());
49115                                 } else {
49116                                         data.source.isDirtyFlag = true;
49117                                         r.store.remove(r);
49118                                         this.store.insert(insertAt++, r);
49119                                 }
49120                                 this.isDirtyFlag = true;
49121                         }
49122                 }
49123                 this.dragZone.cachedTarget = null;
49124                 return true;
49125     },
49126
49127     removeDropIndicators : function(n){
49128                 if(n){
49129                         Roo.fly(n).removeClass([
49130                                 "x-view-drag-insert-above",
49131                                 "x-view-drag-insert-below"]);
49132                         this.lastInsertClass = "_noclass";
49133                 }
49134     },
49135
49136 /**
49137  *      Utility method. Add a delete option to the DDView's context menu.
49138  *      @param {String} imageUrl The URL of the "delete" icon image.
49139  */
49140         setDeletable: function(imageUrl) {
49141                 if (!this.singleSelect && !this.multiSelect) {
49142                         this.singleSelect = true;
49143                 }
49144                 var c = this.getContextMenu();
49145                 this.contextMenu.on("itemclick", function(item) {
49146                         switch (item.id) {
49147                                 case "delete":
49148                                         this.remove(this.getSelectedIndexes());
49149                                         break;
49150                         }
49151                 }, this);
49152                 this.contextMenu.add({
49153                         icon: imageUrl,
49154                         id: "delete",
49155                         text: 'Delete'
49156                 });
49157         },
49158         
49159 /**     Return the context menu for this DDView. */
49160         getContextMenu: function() {
49161                 if (!this.contextMenu) {
49162 //                      Create the View's context menu
49163                         this.contextMenu = new Roo.menu.Menu({
49164                                 id: this.id + "-contextmenu"
49165                         });
49166                         this.el.on("contextmenu", this.showContextMenu, this);
49167                 }
49168                 return this.contextMenu;
49169         },
49170         
49171         disableContextMenu: function() {
49172                 if (this.contextMenu) {
49173                         this.el.un("contextmenu", this.showContextMenu, this);
49174                 }
49175         },
49176
49177         showContextMenu: function(e, item) {
49178         item = this.findItemFromChild(e.getTarget());
49179                 if (item) {
49180                         e.stopEvent();
49181                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
49182                         this.contextMenu.showAt(e.getXY());
49183             }
49184     },
49185
49186 /**
49187  *      Remove {@link Roo.data.Record}s at the specified indices.
49188  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
49189  */
49190     remove: function(selectedIndices) {
49191                 selectedIndices = [].concat(selectedIndices);
49192                 for (var i = 0; i < selectedIndices.length; i++) {
49193                         var rec = this.store.getAt(selectedIndices[i]);
49194                         this.store.remove(rec);
49195                 }
49196     },
49197
49198 /**
49199  *      Double click fires the event, but also, if this is draggable, and there is only one other
49200  *      related DropZone, it transfers the selected node.
49201  */
49202     onDblClick : function(e){
49203         var item = this.findItemFromChild(e.getTarget());
49204         if(item){
49205             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
49206                 return false;
49207             }
49208             if (this.dragGroup) {
49209                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
49210                     while (targets.indexOf(this.dropZone) > -1) {
49211                             targets.remove(this.dropZone);
49212                                 }
49213                     if (targets.length == 1) {
49214                                         this.dragZone.cachedTarget = null;
49215                         var el = Roo.get(targets[0].getEl());
49216                         var box = el.getBox(true);
49217                         targets[0].onNodeDrop(el.dom, {
49218                                 target: el.dom,
49219                                 xy: [box.x, box.y + box.height - 1]
49220                         }, null, this.getDragData(e));
49221                     }
49222                 }
49223         }
49224     },
49225     
49226     handleSelection: function(e) {
49227                 this.dragZone.cachedTarget = null;
49228         var item = this.findItemFromChild(e.getTarget());
49229         if (!item) {
49230                 this.clearSelections(true);
49231                 return;
49232         }
49233                 if (item && (this.multiSelect || this.singleSelect)){
49234                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
49235                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
49236                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
49237                                 this.unselect(item);
49238                         } else {
49239                                 this.select(item, this.multiSelect && e.ctrlKey);
49240                                 this.lastSelection = item;
49241                         }
49242                 }
49243     },
49244
49245     onItemClick : function(item, index, e){
49246                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
49247                         return false;
49248                 }
49249                 return true;
49250     },
49251
49252     unselect : function(nodeInfo, suppressEvent){
49253                 var node = this.getNode(nodeInfo);
49254                 if(node && this.isSelected(node)){
49255                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
49256                                 Roo.fly(node).removeClass(this.selectedClass);
49257                                 this.selections.remove(node);
49258                                 if(!suppressEvent){
49259                                         this.fireEvent("selectionchange", this, this.selections);
49260                                 }
49261                         }
49262                 }
49263     }
49264 });
49265 /*
49266  * Based on:
49267  * Ext JS Library 1.1.1
49268  * Copyright(c) 2006-2007, Ext JS, LLC.
49269  *
49270  * Originally Released Under LGPL - original licence link has changed is not relivant.
49271  *
49272  * Fork - LGPL
49273  * <script type="text/javascript">
49274  */
49275  
49276 /**
49277  * @class Roo.LayoutManager
49278  * @extends Roo.util.Observable
49279  * Base class for layout managers.
49280  */
49281 Roo.LayoutManager = function(container, config){
49282     Roo.LayoutManager.superclass.constructor.call(this);
49283     this.el = Roo.get(container);
49284     // ie scrollbar fix
49285     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
49286         document.body.scroll = "no";
49287     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
49288         this.el.position('relative');
49289     }
49290     this.id = this.el.id;
49291     this.el.addClass("x-layout-container");
49292     /** false to disable window resize monitoring @type Boolean */
49293     this.monitorWindowResize = true;
49294     this.regions = {};
49295     this.addEvents({
49296         /**
49297          * @event layout
49298          * Fires when a layout is performed. 
49299          * @param {Roo.LayoutManager} this
49300          */
49301         "layout" : true,
49302         /**
49303          * @event regionresized
49304          * Fires when the user resizes a region. 
49305          * @param {Roo.LayoutRegion} region The resized region
49306          * @param {Number} newSize The new size (width for east/west, height for north/south)
49307          */
49308         "regionresized" : true,
49309         /**
49310          * @event regioncollapsed
49311          * Fires when a region is collapsed. 
49312          * @param {Roo.LayoutRegion} region The collapsed region
49313          */
49314         "regioncollapsed" : true,
49315         /**
49316          * @event regionexpanded
49317          * Fires when a region is expanded.  
49318          * @param {Roo.LayoutRegion} region The expanded region
49319          */
49320         "regionexpanded" : true
49321     });
49322     this.updating = false;
49323     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49324 };
49325
49326 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
49327     /**
49328      * Returns true if this layout is currently being updated
49329      * @return {Boolean}
49330      */
49331     isUpdating : function(){
49332         return this.updating; 
49333     },
49334     
49335     /**
49336      * Suspend the LayoutManager from doing auto-layouts while
49337      * making multiple add or remove calls
49338      */
49339     beginUpdate : function(){
49340         this.updating = true;    
49341     },
49342     
49343     /**
49344      * Restore auto-layouts and optionally disable the manager from performing a layout
49345      * @param {Boolean} noLayout true to disable a layout update 
49346      */
49347     endUpdate : function(noLayout){
49348         this.updating = false;
49349         if(!noLayout){
49350             this.layout();
49351         }    
49352     },
49353     
49354     layout: function(){
49355         
49356     },
49357     
49358     onRegionResized : function(region, newSize){
49359         this.fireEvent("regionresized", region, newSize);
49360         this.layout();
49361     },
49362     
49363     onRegionCollapsed : function(region){
49364         this.fireEvent("regioncollapsed", region);
49365     },
49366     
49367     onRegionExpanded : function(region){
49368         this.fireEvent("regionexpanded", region);
49369     },
49370         
49371     /**
49372      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
49373      * performs box-model adjustments.
49374      * @return {Object} The size as an object {width: (the width), height: (the height)}
49375      */
49376     getViewSize : function(){
49377         var size;
49378         if(this.el.dom != document.body){
49379             size = this.el.getSize();
49380         }else{
49381             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
49382         }
49383         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
49384         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
49385         return size;
49386     },
49387     
49388     /**
49389      * Returns the Element this layout is bound to.
49390      * @return {Roo.Element}
49391      */
49392     getEl : function(){
49393         return this.el;
49394     },
49395     
49396     /**
49397      * Returns the specified region.
49398      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
49399      * @return {Roo.LayoutRegion}
49400      */
49401     getRegion : function(target){
49402         return this.regions[target.toLowerCase()];
49403     },
49404     
49405     onWindowResize : function(){
49406         if(this.monitorWindowResize){
49407             this.layout();
49408         }
49409     }
49410 });/*
49411  * Based on:
49412  * Ext JS Library 1.1.1
49413  * Copyright(c) 2006-2007, Ext JS, LLC.
49414  *
49415  * Originally Released Under LGPL - original licence link has changed is not relivant.
49416  *
49417  * Fork - LGPL
49418  * <script type="text/javascript">
49419  */
49420 /**
49421  * @class Roo.BorderLayout
49422  * @extends Roo.LayoutManager
49423  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
49424  * please see: <br><br>
49425  * <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>
49426  * <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>
49427  * Example:
49428  <pre><code>
49429  var layout = new Roo.BorderLayout(document.body, {
49430     north: {
49431         initialSize: 25,
49432         titlebar: false
49433     },
49434     west: {
49435         split:true,
49436         initialSize: 200,
49437         minSize: 175,
49438         maxSize: 400,
49439         titlebar: true,
49440         collapsible: true
49441     },
49442     east: {
49443         split:true,
49444         initialSize: 202,
49445         minSize: 175,
49446         maxSize: 400,
49447         titlebar: true,
49448         collapsible: true
49449     },
49450     south: {
49451         split:true,
49452         initialSize: 100,
49453         minSize: 100,
49454         maxSize: 200,
49455         titlebar: true,
49456         collapsible: true
49457     },
49458     center: {
49459         titlebar: true,
49460         autoScroll:true,
49461         resizeTabs: true,
49462         minTabWidth: 50,
49463         preferredTabWidth: 150
49464     }
49465 });
49466
49467 // shorthand
49468 var CP = Roo.ContentPanel;
49469
49470 layout.beginUpdate();
49471 layout.add("north", new CP("north", "North"));
49472 layout.add("south", new CP("south", {title: "South", closable: true}));
49473 layout.add("west", new CP("west", {title: "West"}));
49474 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
49475 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
49476 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
49477 layout.getRegion("center").showPanel("center1");
49478 layout.endUpdate();
49479 </code></pre>
49480
49481 <b>The container the layout is rendered into can be either the body element or any other element.
49482 If it is not the body element, the container needs to either be an absolute positioned element,
49483 or you will need to add "position:relative" to the css of the container.  You will also need to specify
49484 the container size if it is not the body element.</b>
49485
49486 * @constructor
49487 * Create a new BorderLayout
49488 * @param {String/HTMLElement/Element} container The container this layout is bound to
49489 * @param {Object} config Configuration options
49490  */
49491 Roo.BorderLayout = function(container, config){
49492     config = config || {};
49493     Roo.BorderLayout.superclass.constructor.call(this, container, config);
49494     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
49495     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
49496         var target = this.factory.validRegions[i];
49497         if(config[target]){
49498             this.addRegion(target, config[target]);
49499         }
49500     }
49501 };
49502
49503 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
49504     /**
49505      * Creates and adds a new region if it doesn't already exist.
49506      * @param {String} target The target region key (north, south, east, west or center).
49507      * @param {Object} config The regions config object
49508      * @return {BorderLayoutRegion} The new region
49509      */
49510     addRegion : function(target, config){
49511         if(!this.regions[target]){
49512             var r = this.factory.create(target, this, config);
49513             this.bindRegion(target, r);
49514         }
49515         return this.regions[target];
49516     },
49517
49518     // private (kinda)
49519     bindRegion : function(name, r){
49520         this.regions[name] = r;
49521         r.on("visibilitychange", this.layout, this);
49522         r.on("paneladded", this.layout, this);
49523         r.on("panelremoved", this.layout, this);
49524         r.on("invalidated", this.layout, this);
49525         r.on("resized", this.onRegionResized, this);
49526         r.on("collapsed", this.onRegionCollapsed, this);
49527         r.on("expanded", this.onRegionExpanded, this);
49528     },
49529
49530     /**
49531      * Performs a layout update.
49532      */
49533     layout : function(){
49534         if(this.updating) {
49535             return;
49536         }
49537         var size = this.getViewSize();
49538         var w = size.width;
49539         var h = size.height;
49540         var centerW = w;
49541         var centerH = h;
49542         var centerY = 0;
49543         var centerX = 0;
49544         //var x = 0, y = 0;
49545
49546         var rs = this.regions;
49547         var north = rs["north"];
49548         var south = rs["south"]; 
49549         var west = rs["west"];
49550         var east = rs["east"];
49551         var center = rs["center"];
49552         //if(this.hideOnLayout){ // not supported anymore
49553             //c.el.setStyle("display", "none");
49554         //}
49555         if(north && north.isVisible()){
49556             var b = north.getBox();
49557             var m = north.getMargins();
49558             b.width = w - (m.left+m.right);
49559             b.x = m.left;
49560             b.y = m.top;
49561             centerY = b.height + b.y + m.bottom;
49562             centerH -= centerY;
49563             north.updateBox(this.safeBox(b));
49564         }
49565         if(south && south.isVisible()){
49566             var b = south.getBox();
49567             var m = south.getMargins();
49568             b.width = w - (m.left+m.right);
49569             b.x = m.left;
49570             var totalHeight = (b.height + m.top + m.bottom);
49571             b.y = h - totalHeight + m.top;
49572             centerH -= totalHeight;
49573             south.updateBox(this.safeBox(b));
49574         }
49575         if(west && west.isVisible()){
49576             var b = west.getBox();
49577             var m = west.getMargins();
49578             b.height = centerH - (m.top+m.bottom);
49579             b.x = m.left;
49580             b.y = centerY + m.top;
49581             var totalWidth = (b.width + m.left + m.right);
49582             centerX += totalWidth;
49583             centerW -= totalWidth;
49584             west.updateBox(this.safeBox(b));
49585         }
49586         if(east && east.isVisible()){
49587             var b = east.getBox();
49588             var m = east.getMargins();
49589             b.height = centerH - (m.top+m.bottom);
49590             var totalWidth = (b.width + m.left + m.right);
49591             b.x = w - totalWidth + m.left;
49592             b.y = centerY + m.top;
49593             centerW -= totalWidth;
49594             east.updateBox(this.safeBox(b));
49595         }
49596         if(center){
49597             var m = center.getMargins();
49598             var centerBox = {
49599                 x: centerX + m.left,
49600                 y: centerY + m.top,
49601                 width: centerW - (m.left+m.right),
49602                 height: centerH - (m.top+m.bottom)
49603             };
49604             //if(this.hideOnLayout){
49605                 //center.el.setStyle("display", "block");
49606             //}
49607             center.updateBox(this.safeBox(centerBox));
49608         }
49609         this.el.repaint();
49610         this.fireEvent("layout", this);
49611     },
49612
49613     // private
49614     safeBox : function(box){
49615         box.width = Math.max(0, box.width);
49616         box.height = Math.max(0, box.height);
49617         return box;
49618     },
49619
49620     /**
49621      * Adds a ContentPanel (or subclass) to this layout.
49622      * @param {String} target The target region key (north, south, east, west or center).
49623      * @param {Roo.ContentPanel} panel The panel to add
49624      * @return {Roo.ContentPanel} The added panel
49625      */
49626     add : function(target, panel){
49627          
49628         target = target.toLowerCase();
49629         return this.regions[target].add(panel);
49630     },
49631
49632     /**
49633      * Remove a ContentPanel (or subclass) to this layout.
49634      * @param {String} target The target region key (north, south, east, west or center).
49635      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
49636      * @return {Roo.ContentPanel} The removed panel
49637      */
49638     remove : function(target, panel){
49639         target = target.toLowerCase();
49640         return this.regions[target].remove(panel);
49641     },
49642
49643     /**
49644      * Searches all regions for a panel with the specified id
49645      * @param {String} panelId
49646      * @return {Roo.ContentPanel} The panel or null if it wasn't found
49647      */
49648     findPanel : function(panelId){
49649         var rs = this.regions;
49650         for(var target in rs){
49651             if(typeof rs[target] != "function"){
49652                 var p = rs[target].getPanel(panelId);
49653                 if(p){
49654                     return p;
49655                 }
49656             }
49657         }
49658         return null;
49659     },
49660
49661     /**
49662      * Searches all regions for a panel with the specified id and activates (shows) it.
49663      * @param {String/ContentPanel} panelId The panels id or the panel itself
49664      * @return {Roo.ContentPanel} The shown panel or null
49665      */
49666     showPanel : function(panelId) {
49667       var rs = this.regions;
49668       for(var target in rs){
49669          var r = rs[target];
49670          if(typeof r != "function"){
49671             if(r.hasPanel(panelId)){
49672                return r.showPanel(panelId);
49673             }
49674          }
49675       }
49676       return null;
49677    },
49678
49679    /**
49680      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
49681      * @param {Roo.state.Provider} provider (optional) An alternate state provider
49682      */
49683     restoreState : function(provider){
49684         if(!provider){
49685             provider = Roo.state.Manager;
49686         }
49687         var sm = new Roo.LayoutStateManager();
49688         sm.init(this, provider);
49689     },
49690
49691     /**
49692      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
49693      * object should contain properties for each region to add ContentPanels to, and each property's value should be
49694      * a valid ContentPanel config object.  Example:
49695      * <pre><code>
49696 // Create the main layout
49697 var layout = new Roo.BorderLayout('main-ct', {
49698     west: {
49699         split:true,
49700         minSize: 175,
49701         titlebar: true
49702     },
49703     center: {
49704         title:'Components'
49705     }
49706 }, 'main-ct');
49707
49708 // Create and add multiple ContentPanels at once via configs
49709 layout.batchAdd({
49710    west: {
49711        id: 'source-files',
49712        autoCreate:true,
49713        title:'Ext Source Files',
49714        autoScroll:true,
49715        fitToFrame:true
49716    },
49717    center : {
49718        el: cview,
49719        autoScroll:true,
49720        fitToFrame:true,
49721        toolbar: tb,
49722        resizeEl:'cbody'
49723    }
49724 });
49725 </code></pre>
49726      * @param {Object} regions An object containing ContentPanel configs by region name
49727      */
49728     batchAdd : function(regions){
49729         this.beginUpdate();
49730         for(var rname in regions){
49731             var lr = this.regions[rname];
49732             if(lr){
49733                 this.addTypedPanels(lr, regions[rname]);
49734             }
49735         }
49736         this.endUpdate();
49737     },
49738
49739     // private
49740     addTypedPanels : function(lr, ps){
49741         if(typeof ps == 'string'){
49742             lr.add(new Roo.ContentPanel(ps));
49743         }
49744         else if(ps instanceof Array){
49745             for(var i =0, len = ps.length; i < len; i++){
49746                 this.addTypedPanels(lr, ps[i]);
49747             }
49748         }
49749         else if(!ps.events){ // raw config?
49750             var el = ps.el;
49751             delete ps.el; // prevent conflict
49752             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
49753         }
49754         else {  // panel object assumed!
49755             lr.add(ps);
49756         }
49757     },
49758     /**
49759      * Adds a xtype elements to the layout.
49760      * <pre><code>
49761
49762 layout.addxtype({
49763        xtype : 'ContentPanel',
49764        region: 'west',
49765        items: [ .... ]
49766    }
49767 );
49768
49769 layout.addxtype({
49770         xtype : 'NestedLayoutPanel',
49771         region: 'west',
49772         layout: {
49773            center: { },
49774            west: { }   
49775         },
49776         items : [ ... list of content panels or nested layout panels.. ]
49777    }
49778 );
49779 </code></pre>
49780      * @param {Object} cfg Xtype definition of item to add.
49781      */
49782     addxtype : function(cfg)
49783     {
49784         // basically accepts a pannel...
49785         // can accept a layout region..!?!?
49786         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49787         
49788         if (!cfg.xtype.match(/Panel$/)) {
49789             return false;
49790         }
49791         var ret = false;
49792         
49793         if (typeof(cfg.region) == 'undefined') {
49794             Roo.log("Failed to add Panel, region was not set");
49795             Roo.log(cfg);
49796             return false;
49797         }
49798         var region = cfg.region;
49799         delete cfg.region;
49800         
49801           
49802         var xitems = [];
49803         if (cfg.items) {
49804             xitems = cfg.items;
49805             delete cfg.items;
49806         }
49807         var nb = false;
49808         
49809         switch(cfg.xtype) 
49810         {
49811             case 'ContentPanel':  // ContentPanel (el, cfg)
49812             case 'ScrollPanel':  // ContentPanel (el, cfg)
49813             case 'ViewPanel': 
49814                 if(cfg.autoCreate) {
49815                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49816                 } else {
49817                     var el = this.el.createChild();
49818                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49819                 }
49820                 
49821                 this.add(region, ret);
49822                 break;
49823             
49824             
49825             case 'TreePanel': // our new panel!
49826                 cfg.el = this.el.createChild();
49827                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49828                 this.add(region, ret);
49829                 break;
49830             
49831             case 'NestedLayoutPanel': 
49832                 // create a new Layout (which is  a Border Layout...
49833                 var el = this.el.createChild();
49834                 var clayout = cfg.layout;
49835                 delete cfg.layout;
49836                 clayout.items   = clayout.items  || [];
49837                 // replace this exitems with the clayout ones..
49838                 xitems = clayout.items;
49839                  
49840                 
49841                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49842                     cfg.background = false;
49843                 }
49844                 var layout = new Roo.BorderLayout(el, clayout);
49845                 
49846                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49847                 //console.log('adding nested layout panel '  + cfg.toSource());
49848                 this.add(region, ret);
49849                 nb = {}; /// find first...
49850                 break;
49851                 
49852             case 'GridPanel': 
49853             
49854                 // needs grid and region
49855                 
49856                 //var el = this.getRegion(region).el.createChild();
49857                 var el = this.el.createChild();
49858                 // create the grid first...
49859                 
49860                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49861                 delete cfg.grid;
49862                 if (region == 'center' && this.active ) {
49863                     cfg.background = false;
49864                 }
49865                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49866                 
49867                 this.add(region, ret);
49868                 if (cfg.background) {
49869                     ret.on('activate', function(gp) {
49870                         if (!gp.grid.rendered) {
49871                             gp.grid.render();
49872                         }
49873                     });
49874                 } else {
49875                     grid.render();
49876                 }
49877                 break;
49878            
49879            
49880            
49881                 
49882                 
49883                 
49884             default:
49885                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49886                     
49887                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49888                     this.add(region, ret);
49889                 } else {
49890                 
49891                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49892                     return null;
49893                 }
49894                 
49895              // GridPanel (grid, cfg)
49896             
49897         }
49898         this.beginUpdate();
49899         // add children..
49900         var region = '';
49901         var abn = {};
49902         Roo.each(xitems, function(i)  {
49903             region = nb && i.region ? i.region : false;
49904             
49905             var add = ret.addxtype(i);
49906            
49907             if (region) {
49908                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49909                 if (!i.background) {
49910                     abn[region] = nb[region] ;
49911                 }
49912             }
49913             
49914         });
49915         this.endUpdate();
49916
49917         // make the last non-background panel active..
49918         //if (nb) { Roo.log(abn); }
49919         if (nb) {
49920             
49921             for(var r in abn) {
49922                 region = this.getRegion(r);
49923                 if (region) {
49924                     // tried using nb[r], but it does not work..
49925                      
49926                     region.showPanel(abn[r]);
49927                    
49928                 }
49929             }
49930         }
49931         return ret;
49932         
49933     }
49934 });
49935
49936 /**
49937  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49938  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49939  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49940  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49941  * <pre><code>
49942 // shorthand
49943 var CP = Roo.ContentPanel;
49944
49945 var layout = Roo.BorderLayout.create({
49946     north: {
49947         initialSize: 25,
49948         titlebar: false,
49949         panels: [new CP("north", "North")]
49950     },
49951     west: {
49952         split:true,
49953         initialSize: 200,
49954         minSize: 175,
49955         maxSize: 400,
49956         titlebar: true,
49957         collapsible: true,
49958         panels: [new CP("west", {title: "West"})]
49959     },
49960     east: {
49961         split:true,
49962         initialSize: 202,
49963         minSize: 175,
49964         maxSize: 400,
49965         titlebar: true,
49966         collapsible: true,
49967         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49968     },
49969     south: {
49970         split:true,
49971         initialSize: 100,
49972         minSize: 100,
49973         maxSize: 200,
49974         titlebar: true,
49975         collapsible: true,
49976         panels: [new CP("south", {title: "South", closable: true})]
49977     },
49978     center: {
49979         titlebar: true,
49980         autoScroll:true,
49981         resizeTabs: true,
49982         minTabWidth: 50,
49983         preferredTabWidth: 150,
49984         panels: [
49985             new CP("center1", {title: "Close Me", closable: true}),
49986             new CP("center2", {title: "Center Panel", closable: false})
49987         ]
49988     }
49989 }, document.body);
49990
49991 layout.getRegion("center").showPanel("center1");
49992 </code></pre>
49993  * @param config
49994  * @param targetEl
49995  */
49996 Roo.BorderLayout.create = function(config, targetEl){
49997     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49998     layout.beginUpdate();
49999     var regions = Roo.BorderLayout.RegionFactory.validRegions;
50000     for(var j = 0, jlen = regions.length; j < jlen; j++){
50001         var lr = regions[j];
50002         if(layout.regions[lr] && config[lr].panels){
50003             var r = layout.regions[lr];
50004             var ps = config[lr].panels;
50005             layout.addTypedPanels(r, ps);
50006         }
50007     }
50008     layout.endUpdate();
50009     return layout;
50010 };
50011
50012 // private
50013 Roo.BorderLayout.RegionFactory = {
50014     // private
50015     validRegions : ["north","south","east","west","center"],
50016
50017     // private
50018     create : function(target, mgr, config){
50019         target = target.toLowerCase();
50020         if(config.lightweight || config.basic){
50021             return new Roo.BasicLayoutRegion(mgr, config, target);
50022         }
50023         switch(target){
50024             case "north":
50025                 return new Roo.NorthLayoutRegion(mgr, config);
50026             case "south":
50027                 return new Roo.SouthLayoutRegion(mgr, config);
50028             case "east":
50029                 return new Roo.EastLayoutRegion(mgr, config);
50030             case "west":
50031                 return new Roo.WestLayoutRegion(mgr, config);
50032             case "center":
50033                 return new Roo.CenterLayoutRegion(mgr, config);
50034         }
50035         throw 'Layout region "'+target+'" not supported.';
50036     }
50037 };/*
50038  * Based on:
50039  * Ext JS Library 1.1.1
50040  * Copyright(c) 2006-2007, Ext JS, LLC.
50041  *
50042  * Originally Released Under LGPL - original licence link has changed is not relivant.
50043  *
50044  * Fork - LGPL
50045  * <script type="text/javascript">
50046  */
50047  
50048 /**
50049  * @class Roo.BasicLayoutRegion
50050  * @extends Roo.util.Observable
50051  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
50052  * and does not have a titlebar, tabs or any other features. All it does is size and position 
50053  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
50054  */
50055 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
50056     this.mgr = mgr;
50057     this.position  = pos;
50058     this.events = {
50059         /**
50060          * @scope Roo.BasicLayoutRegion
50061          */
50062         
50063         /**
50064          * @event beforeremove
50065          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
50066          * @param {Roo.LayoutRegion} this
50067          * @param {Roo.ContentPanel} panel The panel
50068          * @param {Object} e The cancel event object
50069          */
50070         "beforeremove" : true,
50071         /**
50072          * @event invalidated
50073          * Fires when the layout for this region is changed.
50074          * @param {Roo.LayoutRegion} this
50075          */
50076         "invalidated" : true,
50077         /**
50078          * @event visibilitychange
50079          * Fires when this region is shown or hidden 
50080          * @param {Roo.LayoutRegion} this
50081          * @param {Boolean} visibility true or false
50082          */
50083         "visibilitychange" : true,
50084         /**
50085          * @event paneladded
50086          * Fires when a panel is added. 
50087          * @param {Roo.LayoutRegion} this
50088          * @param {Roo.ContentPanel} panel The panel
50089          */
50090         "paneladded" : true,
50091         /**
50092          * @event panelremoved
50093          * Fires when a panel is removed. 
50094          * @param {Roo.LayoutRegion} this
50095          * @param {Roo.ContentPanel} panel The panel
50096          */
50097         "panelremoved" : true,
50098         /**
50099          * @event collapsed
50100          * Fires when this region is collapsed.
50101          * @param {Roo.LayoutRegion} this
50102          */
50103         "collapsed" : true,
50104         /**
50105          * @event expanded
50106          * Fires when this region is expanded.
50107          * @param {Roo.LayoutRegion} this
50108          */
50109         "expanded" : true,
50110         /**
50111          * @event slideshow
50112          * Fires when this region is slid into view.
50113          * @param {Roo.LayoutRegion} this
50114          */
50115         "slideshow" : true,
50116         /**
50117          * @event slidehide
50118          * Fires when this region slides out of view. 
50119          * @param {Roo.LayoutRegion} this
50120          */
50121         "slidehide" : true,
50122         /**
50123          * @event panelactivated
50124          * Fires when a panel is activated. 
50125          * @param {Roo.LayoutRegion} this
50126          * @param {Roo.ContentPanel} panel The activated panel
50127          */
50128         "panelactivated" : true,
50129         /**
50130          * @event resized
50131          * Fires when the user resizes this region. 
50132          * @param {Roo.LayoutRegion} this
50133          * @param {Number} newSize The new size (width for east/west, height for north/south)
50134          */
50135         "resized" : true
50136     };
50137     /** A collection of panels in this region. @type Roo.util.MixedCollection */
50138     this.panels = new Roo.util.MixedCollection();
50139     this.panels.getKey = this.getPanelId.createDelegate(this);
50140     this.box = null;
50141     this.activePanel = null;
50142     // ensure listeners are added...
50143     
50144     if (config.listeners || config.events) {
50145         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
50146             listeners : config.listeners || {},
50147             events : config.events || {}
50148         });
50149     }
50150     
50151     if(skipConfig !== true){
50152         this.applyConfig(config);
50153     }
50154 };
50155
50156 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
50157     getPanelId : function(p){
50158         return p.getId();
50159     },
50160     
50161     applyConfig : function(config){
50162         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50163         this.config = config;
50164         
50165     },
50166     
50167     /**
50168      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
50169      * the width, for horizontal (north, south) the height.
50170      * @param {Number} newSize The new width or height
50171      */
50172     resizeTo : function(newSize){
50173         var el = this.el ? this.el :
50174                  (this.activePanel ? this.activePanel.getEl() : null);
50175         if(el){
50176             switch(this.position){
50177                 case "east":
50178                 case "west":
50179                     el.setWidth(newSize);
50180                     this.fireEvent("resized", this, newSize);
50181                 break;
50182                 case "north":
50183                 case "south":
50184                     el.setHeight(newSize);
50185                     this.fireEvent("resized", this, newSize);
50186                 break;                
50187             }
50188         }
50189     },
50190     
50191     getBox : function(){
50192         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
50193     },
50194     
50195     getMargins : function(){
50196         return this.margins;
50197     },
50198     
50199     updateBox : function(box){
50200         this.box = box;
50201         var el = this.activePanel.getEl();
50202         el.dom.style.left = box.x + "px";
50203         el.dom.style.top = box.y + "px";
50204         this.activePanel.setSize(box.width, box.height);
50205     },
50206     
50207     /**
50208      * Returns the container element for this region.
50209      * @return {Roo.Element}
50210      */
50211     getEl : function(){
50212         return this.activePanel;
50213     },
50214     
50215     /**
50216      * Returns true if this region is currently visible.
50217      * @return {Boolean}
50218      */
50219     isVisible : function(){
50220         return this.activePanel ? true : false;
50221     },
50222     
50223     setActivePanel : function(panel){
50224         panel = this.getPanel(panel);
50225         if(this.activePanel && this.activePanel != panel){
50226             this.activePanel.setActiveState(false);
50227             this.activePanel.getEl().setLeftTop(-10000,-10000);
50228         }
50229         this.activePanel = panel;
50230         panel.setActiveState(true);
50231         if(this.box){
50232             panel.setSize(this.box.width, this.box.height);
50233         }
50234         this.fireEvent("panelactivated", this, panel);
50235         this.fireEvent("invalidated");
50236     },
50237     
50238     /**
50239      * Show the specified panel.
50240      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
50241      * @return {Roo.ContentPanel} The shown panel or null
50242      */
50243     showPanel : function(panel){
50244         if(panel = this.getPanel(panel)){
50245             this.setActivePanel(panel);
50246         }
50247         return panel;
50248     },
50249     
50250     /**
50251      * Get the active panel for this region.
50252      * @return {Roo.ContentPanel} The active panel or null
50253      */
50254     getActivePanel : function(){
50255         return this.activePanel;
50256     },
50257     
50258     /**
50259      * Add the passed ContentPanel(s)
50260      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50261      * @return {Roo.ContentPanel} The panel added (if only one was added)
50262      */
50263     add : function(panel){
50264         if(arguments.length > 1){
50265             for(var i = 0, len = arguments.length; i < len; i++) {
50266                 this.add(arguments[i]);
50267             }
50268             return null;
50269         }
50270         if(this.hasPanel(panel)){
50271             this.showPanel(panel);
50272             return panel;
50273         }
50274         var el = panel.getEl();
50275         if(el.dom.parentNode != this.mgr.el.dom){
50276             this.mgr.el.dom.appendChild(el.dom);
50277         }
50278         if(panel.setRegion){
50279             panel.setRegion(this);
50280         }
50281         this.panels.add(panel);
50282         el.setStyle("position", "absolute");
50283         if(!panel.background){
50284             this.setActivePanel(panel);
50285             if(this.config.initialSize && this.panels.getCount()==1){
50286                 this.resizeTo(this.config.initialSize);
50287             }
50288         }
50289         this.fireEvent("paneladded", this, panel);
50290         return panel;
50291     },
50292     
50293     /**
50294      * Returns true if the panel is in this region.
50295      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50296      * @return {Boolean}
50297      */
50298     hasPanel : function(panel){
50299         if(typeof panel == "object"){ // must be panel obj
50300             panel = panel.getId();
50301         }
50302         return this.getPanel(panel) ? true : false;
50303     },
50304     
50305     /**
50306      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50307      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50308      * @param {Boolean} preservePanel Overrides the config preservePanel option
50309      * @return {Roo.ContentPanel} The panel that was removed
50310      */
50311     remove : function(panel, preservePanel){
50312         panel = this.getPanel(panel);
50313         if(!panel){
50314             return null;
50315         }
50316         var e = {};
50317         this.fireEvent("beforeremove", this, panel, e);
50318         if(e.cancel === true){
50319             return null;
50320         }
50321         var panelId = panel.getId();
50322         this.panels.removeKey(panelId);
50323         return panel;
50324     },
50325     
50326     /**
50327      * Returns the panel specified or null if it's not in this region.
50328      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50329      * @return {Roo.ContentPanel}
50330      */
50331     getPanel : function(id){
50332         if(typeof id == "object"){ // must be panel obj
50333             return id;
50334         }
50335         return this.panels.get(id);
50336     },
50337     
50338     /**
50339      * Returns this regions position (north/south/east/west/center).
50340      * @return {String} 
50341      */
50342     getPosition: function(){
50343         return this.position;    
50344     }
50345 });/*
50346  * Based on:
50347  * Ext JS Library 1.1.1
50348  * Copyright(c) 2006-2007, Ext JS, LLC.
50349  *
50350  * Originally Released Under LGPL - original licence link has changed is not relivant.
50351  *
50352  * Fork - LGPL
50353  * <script type="text/javascript">
50354  */
50355  
50356 /**
50357  * @class Roo.LayoutRegion
50358  * @extends Roo.BasicLayoutRegion
50359  * This class represents a region in a layout manager.
50360  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
50361  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
50362  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
50363  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
50364  * @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})
50365  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
50366  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
50367  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
50368  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
50369  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
50370  * @cfg {String}    title           The title for the region (overrides panel titles)
50371  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
50372  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
50373  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
50374  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
50375  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
50376  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
50377  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
50378  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
50379  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
50380  * @cfg {Boolean}   showPin         True to show a pin button
50381  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
50382  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
50383  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
50384  * @cfg {Number}    width           For East/West panels
50385  * @cfg {Number}    height          For North/South panels
50386  * @cfg {Boolean}   split           To show the splitter
50387  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
50388  */
50389 Roo.LayoutRegion = function(mgr, config, pos){
50390     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
50391     var dh = Roo.DomHelper;
50392     /** This region's container element 
50393     * @type Roo.Element */
50394     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
50395     /** This region's title element 
50396     * @type Roo.Element */
50397
50398     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
50399         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
50400         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
50401     ]}, true);
50402     this.titleEl.enableDisplayMode();
50403     /** This region's title text element 
50404     * @type HTMLElement */
50405     this.titleTextEl = this.titleEl.dom.firstChild;
50406     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
50407     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
50408     this.closeBtn.enableDisplayMode();
50409     this.closeBtn.on("click", this.closeClicked, this);
50410     this.closeBtn.hide();
50411
50412     this.createBody(config);
50413     this.visible = true;
50414     this.collapsed = false;
50415
50416     if(config.hideWhenEmpty){
50417         this.hide();
50418         this.on("paneladded", this.validateVisibility, this);
50419         this.on("panelremoved", this.validateVisibility, this);
50420     }
50421     this.applyConfig(config);
50422 };
50423
50424 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
50425
50426     createBody : function(){
50427         /** This region's body element 
50428         * @type Roo.Element */
50429         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
50430     },
50431
50432     applyConfig : function(c){
50433         if(c.collapsible && this.position != "center" && !this.collapsedEl){
50434             var dh = Roo.DomHelper;
50435             if(c.titlebar !== false){
50436                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
50437                 this.collapseBtn.on("click", this.collapse, this);
50438                 this.collapseBtn.enableDisplayMode();
50439
50440                 if(c.showPin === true || this.showPin){
50441                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
50442                     this.stickBtn.enableDisplayMode();
50443                     this.stickBtn.on("click", this.expand, this);
50444                     this.stickBtn.hide();
50445                 }
50446             }
50447             /** This region's collapsed element
50448             * @type Roo.Element */
50449             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
50450                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
50451             ]}, true);
50452             if(c.floatable !== false){
50453                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
50454                this.collapsedEl.on("click", this.collapseClick, this);
50455             }
50456
50457             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
50458                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
50459                    id: "message", unselectable: "on", style:{"float":"left"}});
50460                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
50461              }
50462             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
50463             this.expandBtn.on("click", this.expand, this);
50464         }
50465         if(this.collapseBtn){
50466             this.collapseBtn.setVisible(c.collapsible == true);
50467         }
50468         this.cmargins = c.cmargins || this.cmargins ||
50469                          (this.position == "west" || this.position == "east" ?
50470                              {top: 0, left: 2, right:2, bottom: 0} :
50471                              {top: 2, left: 0, right:0, bottom: 2});
50472         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50473         this.bottomTabs = c.tabPosition != "top";
50474         this.autoScroll = c.autoScroll || false;
50475         if(this.autoScroll){
50476             this.bodyEl.setStyle("overflow", "auto");
50477         }else{
50478             this.bodyEl.setStyle("overflow", "hidden");
50479         }
50480         //if(c.titlebar !== false){
50481             if((!c.titlebar && !c.title) || c.titlebar === false){
50482                 this.titleEl.hide();
50483             }else{
50484                 this.titleEl.show();
50485                 if(c.title){
50486                     this.titleTextEl.innerHTML = c.title;
50487                 }
50488             }
50489         //}
50490         this.duration = c.duration || .30;
50491         this.slideDuration = c.slideDuration || .45;
50492         this.config = c;
50493         if(c.collapsed){
50494             this.collapse(true);
50495         }
50496         if(c.hidden){
50497             this.hide();
50498         }
50499     },
50500     /**
50501      * Returns true if this region is currently visible.
50502      * @return {Boolean}
50503      */
50504     isVisible : function(){
50505         return this.visible;
50506     },
50507
50508     /**
50509      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
50510      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
50511      */
50512     setCollapsedTitle : function(title){
50513         title = title || "&#160;";
50514         if(this.collapsedTitleTextEl){
50515             this.collapsedTitleTextEl.innerHTML = title;
50516         }
50517     },
50518
50519     getBox : function(){
50520         var b;
50521         if(!this.collapsed){
50522             b = this.el.getBox(false, true);
50523         }else{
50524             b = this.collapsedEl.getBox(false, true);
50525         }
50526         return b;
50527     },
50528
50529     getMargins : function(){
50530         return this.collapsed ? this.cmargins : this.margins;
50531     },
50532
50533     highlight : function(){
50534         this.el.addClass("x-layout-panel-dragover");
50535     },
50536
50537     unhighlight : function(){
50538         this.el.removeClass("x-layout-panel-dragover");
50539     },
50540
50541     updateBox : function(box){
50542         this.box = box;
50543         if(!this.collapsed){
50544             this.el.dom.style.left = box.x + "px";
50545             this.el.dom.style.top = box.y + "px";
50546             this.updateBody(box.width, box.height);
50547         }else{
50548             this.collapsedEl.dom.style.left = box.x + "px";
50549             this.collapsedEl.dom.style.top = box.y + "px";
50550             this.collapsedEl.setSize(box.width, box.height);
50551         }
50552         if(this.tabs){
50553             this.tabs.autoSizeTabs();
50554         }
50555     },
50556
50557     updateBody : function(w, h){
50558         if(w !== null){
50559             this.el.setWidth(w);
50560             w -= this.el.getBorderWidth("rl");
50561             if(this.config.adjustments){
50562                 w += this.config.adjustments[0];
50563             }
50564         }
50565         if(h !== null){
50566             this.el.setHeight(h);
50567             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
50568             h -= this.el.getBorderWidth("tb");
50569             if(this.config.adjustments){
50570                 h += this.config.adjustments[1];
50571             }
50572             this.bodyEl.setHeight(h);
50573             if(this.tabs){
50574                 h = this.tabs.syncHeight(h);
50575             }
50576         }
50577         if(this.panelSize){
50578             w = w !== null ? w : this.panelSize.width;
50579             h = h !== null ? h : this.panelSize.height;
50580         }
50581         if(this.activePanel){
50582             var el = this.activePanel.getEl();
50583             w = w !== null ? w : el.getWidth();
50584             h = h !== null ? h : el.getHeight();
50585             this.panelSize = {width: w, height: h};
50586             this.activePanel.setSize(w, h);
50587         }
50588         if(Roo.isIE && this.tabs){
50589             this.tabs.el.repaint();
50590         }
50591     },
50592
50593     /**
50594      * Returns the container element for this region.
50595      * @return {Roo.Element}
50596      */
50597     getEl : function(){
50598         return this.el;
50599     },
50600
50601     /**
50602      * Hides this region.
50603      */
50604     hide : function(){
50605         if(!this.collapsed){
50606             this.el.dom.style.left = "-2000px";
50607             this.el.hide();
50608         }else{
50609             this.collapsedEl.dom.style.left = "-2000px";
50610             this.collapsedEl.hide();
50611         }
50612         this.visible = false;
50613         this.fireEvent("visibilitychange", this, false);
50614     },
50615
50616     /**
50617      * Shows this region if it was previously hidden.
50618      */
50619     show : function(){
50620         if(!this.collapsed){
50621             this.el.show();
50622         }else{
50623             this.collapsedEl.show();
50624         }
50625         this.visible = true;
50626         this.fireEvent("visibilitychange", this, true);
50627     },
50628
50629     closeClicked : function(){
50630         if(this.activePanel){
50631             this.remove(this.activePanel);
50632         }
50633     },
50634
50635     collapseClick : function(e){
50636         if(this.isSlid){
50637            e.stopPropagation();
50638            this.slideIn();
50639         }else{
50640            e.stopPropagation();
50641            this.slideOut();
50642         }
50643     },
50644
50645     /**
50646      * Collapses this region.
50647      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
50648      */
50649     collapse : function(skipAnim){
50650         if(this.collapsed) {
50651             return;
50652         }
50653         this.collapsed = true;
50654         if(this.split){
50655             this.split.el.hide();
50656         }
50657         if(this.config.animate && skipAnim !== true){
50658             this.fireEvent("invalidated", this);
50659             this.animateCollapse();
50660         }else{
50661             this.el.setLocation(-20000,-20000);
50662             this.el.hide();
50663             this.collapsedEl.show();
50664             this.fireEvent("collapsed", this);
50665             this.fireEvent("invalidated", this);
50666         }
50667     },
50668
50669     animateCollapse : function(){
50670         // overridden
50671     },
50672
50673     /**
50674      * Expands this region if it was previously collapsed.
50675      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
50676      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
50677      */
50678     expand : function(e, skipAnim){
50679         if(e) {
50680             e.stopPropagation();
50681         }
50682         if(!this.collapsed || this.el.hasActiveFx()) {
50683             return;
50684         }
50685         if(this.isSlid){
50686             this.afterSlideIn();
50687             skipAnim = true;
50688         }
50689         this.collapsed = false;
50690         if(this.config.animate && skipAnim !== true){
50691             this.animateExpand();
50692         }else{
50693             this.el.show();
50694             if(this.split){
50695                 this.split.el.show();
50696             }
50697             this.collapsedEl.setLocation(-2000,-2000);
50698             this.collapsedEl.hide();
50699             this.fireEvent("invalidated", this);
50700             this.fireEvent("expanded", this);
50701         }
50702     },
50703
50704     animateExpand : function(){
50705         // overridden
50706     },
50707
50708     initTabs : function()
50709     {
50710         this.bodyEl.setStyle("overflow", "hidden");
50711         var ts = new Roo.TabPanel(
50712                 this.bodyEl.dom,
50713                 {
50714                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
50715                     disableTooltips: this.config.disableTabTips,
50716                     toolbar : this.config.toolbar
50717                 }
50718         );
50719         if(this.config.hideTabs){
50720             ts.stripWrap.setDisplayed(false);
50721         }
50722         this.tabs = ts;
50723         ts.resizeTabs = this.config.resizeTabs === true;
50724         ts.minTabWidth = this.config.minTabWidth || 40;
50725         ts.maxTabWidth = this.config.maxTabWidth || 250;
50726         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
50727         ts.monitorResize = false;
50728         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50729         ts.bodyEl.addClass('x-layout-tabs-body');
50730         this.panels.each(this.initPanelAsTab, this);
50731     },
50732
50733     initPanelAsTab : function(panel){
50734         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
50735                     this.config.closeOnTab && panel.isClosable());
50736         if(panel.tabTip !== undefined){
50737             ti.setTooltip(panel.tabTip);
50738         }
50739         ti.on("activate", function(){
50740               this.setActivePanel(panel);
50741         }, this);
50742         if(this.config.closeOnTab){
50743             ti.on("beforeclose", function(t, e){
50744                 e.cancel = true;
50745                 this.remove(panel);
50746             }, this);
50747         }
50748         return ti;
50749     },
50750
50751     updatePanelTitle : function(panel, title){
50752         if(this.activePanel == panel){
50753             this.updateTitle(title);
50754         }
50755         if(this.tabs){
50756             var ti = this.tabs.getTab(panel.getEl().id);
50757             ti.setText(title);
50758             if(panel.tabTip !== undefined){
50759                 ti.setTooltip(panel.tabTip);
50760             }
50761         }
50762     },
50763
50764     updateTitle : function(title){
50765         if(this.titleTextEl && !this.config.title){
50766             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
50767         }
50768     },
50769
50770     setActivePanel : function(panel){
50771         panel = this.getPanel(panel);
50772         if(this.activePanel && this.activePanel != panel){
50773             this.activePanel.setActiveState(false);
50774         }
50775         this.activePanel = panel;
50776         panel.setActiveState(true);
50777         if(this.panelSize){
50778             panel.setSize(this.panelSize.width, this.panelSize.height);
50779         }
50780         if(this.closeBtn){
50781             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50782         }
50783         this.updateTitle(panel.getTitle());
50784         if(this.tabs){
50785             this.fireEvent("invalidated", this);
50786         }
50787         this.fireEvent("panelactivated", this, panel);
50788     },
50789
50790     /**
50791      * Shows the specified panel.
50792      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50793      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50794      */
50795     showPanel : function(panel)
50796     {
50797         panel = this.getPanel(panel);
50798         if(panel){
50799             if(this.tabs){
50800                 var tab = this.tabs.getTab(panel.getEl().id);
50801                 if(tab.isHidden()){
50802                     this.tabs.unhideTab(tab.id);
50803                 }
50804                 tab.activate();
50805             }else{
50806                 this.setActivePanel(panel);
50807             }
50808         }
50809         return panel;
50810     },
50811
50812     /**
50813      * Get the active panel for this region.
50814      * @return {Roo.ContentPanel} The active panel or null
50815      */
50816     getActivePanel : function(){
50817         return this.activePanel;
50818     },
50819
50820     validateVisibility : function(){
50821         if(this.panels.getCount() < 1){
50822             this.updateTitle("&#160;");
50823             this.closeBtn.hide();
50824             this.hide();
50825         }else{
50826             if(!this.isVisible()){
50827                 this.show();
50828             }
50829         }
50830     },
50831
50832     /**
50833      * Adds the passed ContentPanel(s) to this region.
50834      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50835      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50836      */
50837     add : function(panel){
50838         if(arguments.length > 1){
50839             for(var i = 0, len = arguments.length; i < len; i++) {
50840                 this.add(arguments[i]);
50841             }
50842             return null;
50843         }
50844         if(this.hasPanel(panel)){
50845             this.showPanel(panel);
50846             return panel;
50847         }
50848         panel.setRegion(this);
50849         this.panels.add(panel);
50850         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50851             this.bodyEl.dom.appendChild(panel.getEl().dom);
50852             if(panel.background !== true){
50853                 this.setActivePanel(panel);
50854             }
50855             this.fireEvent("paneladded", this, panel);
50856             return panel;
50857         }
50858         if(!this.tabs){
50859             this.initTabs();
50860         }else{
50861             this.initPanelAsTab(panel);
50862         }
50863         if(panel.background !== true){
50864             this.tabs.activate(panel.getEl().id);
50865         }
50866         this.fireEvent("paneladded", this, panel);
50867         return panel;
50868     },
50869
50870     /**
50871      * Hides the tab for the specified panel.
50872      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50873      */
50874     hidePanel : function(panel){
50875         if(this.tabs && (panel = this.getPanel(panel))){
50876             this.tabs.hideTab(panel.getEl().id);
50877         }
50878     },
50879
50880     /**
50881      * Unhides the tab for a previously hidden panel.
50882      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50883      */
50884     unhidePanel : function(panel){
50885         if(this.tabs && (panel = this.getPanel(panel))){
50886             this.tabs.unhideTab(panel.getEl().id);
50887         }
50888     },
50889
50890     clearPanels : function(){
50891         while(this.panels.getCount() > 0){
50892              this.remove(this.panels.first());
50893         }
50894     },
50895
50896     /**
50897      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50898      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50899      * @param {Boolean} preservePanel Overrides the config preservePanel option
50900      * @return {Roo.ContentPanel} The panel that was removed
50901      */
50902     remove : function(panel, preservePanel){
50903         panel = this.getPanel(panel);
50904         if(!panel){
50905             return null;
50906         }
50907         var e = {};
50908         this.fireEvent("beforeremove", this, panel, e);
50909         if(e.cancel === true){
50910             return null;
50911         }
50912         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50913         var panelId = panel.getId();
50914         this.panels.removeKey(panelId);
50915         if(preservePanel){
50916             document.body.appendChild(panel.getEl().dom);
50917         }
50918         if(this.tabs){
50919             this.tabs.removeTab(panel.getEl().id);
50920         }else if (!preservePanel){
50921             this.bodyEl.dom.removeChild(panel.getEl().dom);
50922         }
50923         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50924             var p = this.panels.first();
50925             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50926             tempEl.appendChild(p.getEl().dom);
50927             this.bodyEl.update("");
50928             this.bodyEl.dom.appendChild(p.getEl().dom);
50929             tempEl = null;
50930             this.updateTitle(p.getTitle());
50931             this.tabs = null;
50932             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50933             this.setActivePanel(p);
50934         }
50935         panel.setRegion(null);
50936         if(this.activePanel == panel){
50937             this.activePanel = null;
50938         }
50939         if(this.config.autoDestroy !== false && preservePanel !== true){
50940             try{panel.destroy();}catch(e){}
50941         }
50942         this.fireEvent("panelremoved", this, panel);
50943         return panel;
50944     },
50945
50946     /**
50947      * Returns the TabPanel component used by this region
50948      * @return {Roo.TabPanel}
50949      */
50950     getTabs : function(){
50951         return this.tabs;
50952     },
50953
50954     createTool : function(parentEl, className){
50955         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50956             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50957         btn.addClassOnOver("x-layout-tools-button-over");
50958         return btn;
50959     }
50960 });/*
50961  * Based on:
50962  * Ext JS Library 1.1.1
50963  * Copyright(c) 2006-2007, Ext JS, LLC.
50964  *
50965  * Originally Released Under LGPL - original licence link has changed is not relivant.
50966  *
50967  * Fork - LGPL
50968  * <script type="text/javascript">
50969  */
50970  
50971
50972
50973 /**
50974  * @class Roo.SplitLayoutRegion
50975  * @extends Roo.LayoutRegion
50976  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50977  */
50978 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50979     this.cursor = cursor;
50980     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50981 };
50982
50983 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50984     splitTip : "Drag to resize.",
50985     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50986     useSplitTips : false,
50987
50988     applyConfig : function(config){
50989         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50990         if(config.split){
50991             if(!this.split){
50992                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50993                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50994                 /** The SplitBar for this region 
50995                 * @type Roo.SplitBar */
50996                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50997                 this.split.on("moved", this.onSplitMove, this);
50998                 this.split.useShim = config.useShim === true;
50999                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
51000                 if(this.useSplitTips){
51001                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
51002                 }
51003                 if(config.collapsible){
51004                     this.split.el.on("dblclick", this.collapse,  this);
51005                 }
51006             }
51007             if(typeof config.minSize != "undefined"){
51008                 this.split.minSize = config.minSize;
51009             }
51010             if(typeof config.maxSize != "undefined"){
51011                 this.split.maxSize = config.maxSize;
51012             }
51013             if(config.hideWhenEmpty || config.hidden || config.collapsed){
51014                 this.hideSplitter();
51015             }
51016         }
51017     },
51018
51019     getHMaxSize : function(){
51020          var cmax = this.config.maxSize || 10000;
51021          var center = this.mgr.getRegion("center");
51022          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
51023     },
51024
51025     getVMaxSize : function(){
51026          var cmax = this.config.maxSize || 10000;
51027          var center = this.mgr.getRegion("center");
51028          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
51029     },
51030
51031     onSplitMove : function(split, newSize){
51032         this.fireEvent("resized", this, newSize);
51033     },
51034     
51035     /** 
51036      * Returns the {@link Roo.SplitBar} for this region.
51037      * @return {Roo.SplitBar}
51038      */
51039     getSplitBar : function(){
51040         return this.split;
51041     },
51042     
51043     hide : function(){
51044         this.hideSplitter();
51045         Roo.SplitLayoutRegion.superclass.hide.call(this);
51046     },
51047
51048     hideSplitter : function(){
51049         if(this.split){
51050             this.split.el.setLocation(-2000,-2000);
51051             this.split.el.hide();
51052         }
51053     },
51054
51055     show : function(){
51056         if(this.split){
51057             this.split.el.show();
51058         }
51059         Roo.SplitLayoutRegion.superclass.show.call(this);
51060     },
51061     
51062     beforeSlide: function(){
51063         if(Roo.isGecko){// firefox overflow auto bug workaround
51064             this.bodyEl.clip();
51065             if(this.tabs) {
51066                 this.tabs.bodyEl.clip();
51067             }
51068             if(this.activePanel){
51069                 this.activePanel.getEl().clip();
51070                 
51071                 if(this.activePanel.beforeSlide){
51072                     this.activePanel.beforeSlide();
51073                 }
51074             }
51075         }
51076     },
51077     
51078     afterSlide : function(){
51079         if(Roo.isGecko){// firefox overflow auto bug workaround
51080             this.bodyEl.unclip();
51081             if(this.tabs) {
51082                 this.tabs.bodyEl.unclip();
51083             }
51084             if(this.activePanel){
51085                 this.activePanel.getEl().unclip();
51086                 if(this.activePanel.afterSlide){
51087                     this.activePanel.afterSlide();
51088                 }
51089             }
51090         }
51091     },
51092
51093     initAutoHide : function(){
51094         if(this.autoHide !== false){
51095             if(!this.autoHideHd){
51096                 var st = new Roo.util.DelayedTask(this.slideIn, this);
51097                 this.autoHideHd = {
51098                     "mouseout": function(e){
51099                         if(!e.within(this.el, true)){
51100                             st.delay(500);
51101                         }
51102                     },
51103                     "mouseover" : function(e){
51104                         st.cancel();
51105                     },
51106                     scope : this
51107                 };
51108             }
51109             this.el.on(this.autoHideHd);
51110         }
51111     },
51112
51113     clearAutoHide : function(){
51114         if(this.autoHide !== false){
51115             this.el.un("mouseout", this.autoHideHd.mouseout);
51116             this.el.un("mouseover", this.autoHideHd.mouseover);
51117         }
51118     },
51119
51120     clearMonitor : function(){
51121         Roo.get(document).un("click", this.slideInIf, this);
51122     },
51123
51124     // these names are backwards but not changed for compat
51125     slideOut : function(){
51126         if(this.isSlid || this.el.hasActiveFx()){
51127             return;
51128         }
51129         this.isSlid = true;
51130         if(this.collapseBtn){
51131             this.collapseBtn.hide();
51132         }
51133         this.closeBtnState = this.closeBtn.getStyle('display');
51134         this.closeBtn.hide();
51135         if(this.stickBtn){
51136             this.stickBtn.show();
51137         }
51138         this.el.show();
51139         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
51140         this.beforeSlide();
51141         this.el.setStyle("z-index", 10001);
51142         this.el.slideIn(this.getSlideAnchor(), {
51143             callback: function(){
51144                 this.afterSlide();
51145                 this.initAutoHide();
51146                 Roo.get(document).on("click", this.slideInIf, this);
51147                 this.fireEvent("slideshow", this);
51148             },
51149             scope: this,
51150             block: true
51151         });
51152     },
51153
51154     afterSlideIn : function(){
51155         this.clearAutoHide();
51156         this.isSlid = false;
51157         this.clearMonitor();
51158         this.el.setStyle("z-index", "");
51159         if(this.collapseBtn){
51160             this.collapseBtn.show();
51161         }
51162         this.closeBtn.setStyle('display', this.closeBtnState);
51163         if(this.stickBtn){
51164             this.stickBtn.hide();
51165         }
51166         this.fireEvent("slidehide", this);
51167     },
51168
51169     slideIn : function(cb){
51170         if(!this.isSlid || this.el.hasActiveFx()){
51171             Roo.callback(cb);
51172             return;
51173         }
51174         this.isSlid = false;
51175         this.beforeSlide();
51176         this.el.slideOut(this.getSlideAnchor(), {
51177             callback: function(){
51178                 this.el.setLeftTop(-10000, -10000);
51179                 this.afterSlide();
51180                 this.afterSlideIn();
51181                 Roo.callback(cb);
51182             },
51183             scope: this,
51184             block: true
51185         });
51186     },
51187     
51188     slideInIf : function(e){
51189         if(!e.within(this.el)){
51190             this.slideIn();
51191         }
51192     },
51193
51194     animateCollapse : function(){
51195         this.beforeSlide();
51196         this.el.setStyle("z-index", 20000);
51197         var anchor = this.getSlideAnchor();
51198         this.el.slideOut(anchor, {
51199             callback : function(){
51200                 this.el.setStyle("z-index", "");
51201                 this.collapsedEl.slideIn(anchor, {duration:.3});
51202                 this.afterSlide();
51203                 this.el.setLocation(-10000,-10000);
51204                 this.el.hide();
51205                 this.fireEvent("collapsed", this);
51206             },
51207             scope: this,
51208             block: true
51209         });
51210     },
51211
51212     animateExpand : function(){
51213         this.beforeSlide();
51214         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
51215         this.el.setStyle("z-index", 20000);
51216         this.collapsedEl.hide({
51217             duration:.1
51218         });
51219         this.el.slideIn(this.getSlideAnchor(), {
51220             callback : function(){
51221                 this.el.setStyle("z-index", "");
51222                 this.afterSlide();
51223                 if(this.split){
51224                     this.split.el.show();
51225                 }
51226                 this.fireEvent("invalidated", this);
51227                 this.fireEvent("expanded", this);
51228             },
51229             scope: this,
51230             block: true
51231         });
51232     },
51233
51234     anchors : {
51235         "west" : "left",
51236         "east" : "right",
51237         "north" : "top",
51238         "south" : "bottom"
51239     },
51240
51241     sanchors : {
51242         "west" : "l",
51243         "east" : "r",
51244         "north" : "t",
51245         "south" : "b"
51246     },
51247
51248     canchors : {
51249         "west" : "tl-tr",
51250         "east" : "tr-tl",
51251         "north" : "tl-bl",
51252         "south" : "bl-tl"
51253     },
51254
51255     getAnchor : function(){
51256         return this.anchors[this.position];
51257     },
51258
51259     getCollapseAnchor : function(){
51260         return this.canchors[this.position];
51261     },
51262
51263     getSlideAnchor : function(){
51264         return this.sanchors[this.position];
51265     },
51266
51267     getAlignAdj : function(){
51268         var cm = this.cmargins;
51269         switch(this.position){
51270             case "west":
51271                 return [0, 0];
51272             break;
51273             case "east":
51274                 return [0, 0];
51275             break;
51276             case "north":
51277                 return [0, 0];
51278             break;
51279             case "south":
51280                 return [0, 0];
51281             break;
51282         }
51283     },
51284
51285     getExpandAdj : function(){
51286         var c = this.collapsedEl, cm = this.cmargins;
51287         switch(this.position){
51288             case "west":
51289                 return [-(cm.right+c.getWidth()+cm.left), 0];
51290             break;
51291             case "east":
51292                 return [cm.right+c.getWidth()+cm.left, 0];
51293             break;
51294             case "north":
51295                 return [0, -(cm.top+cm.bottom+c.getHeight())];
51296             break;
51297             case "south":
51298                 return [0, cm.top+cm.bottom+c.getHeight()];
51299             break;
51300         }
51301     }
51302 });/*
51303  * Based on:
51304  * Ext JS Library 1.1.1
51305  * Copyright(c) 2006-2007, Ext JS, LLC.
51306  *
51307  * Originally Released Under LGPL - original licence link has changed is not relivant.
51308  *
51309  * Fork - LGPL
51310  * <script type="text/javascript">
51311  */
51312 /*
51313  * These classes are private internal classes
51314  */
51315 Roo.CenterLayoutRegion = function(mgr, config){
51316     Roo.LayoutRegion.call(this, mgr, config, "center");
51317     this.visible = true;
51318     this.minWidth = config.minWidth || 20;
51319     this.minHeight = config.minHeight || 20;
51320 };
51321
51322 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
51323     hide : function(){
51324         // center panel can't be hidden
51325     },
51326     
51327     show : function(){
51328         // center panel can't be hidden
51329     },
51330     
51331     getMinWidth: function(){
51332         return this.minWidth;
51333     },
51334     
51335     getMinHeight: function(){
51336         return this.minHeight;
51337     }
51338 });
51339
51340
51341 Roo.NorthLayoutRegion = function(mgr, config){
51342     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
51343     if(this.split){
51344         this.split.placement = Roo.SplitBar.TOP;
51345         this.split.orientation = Roo.SplitBar.VERTICAL;
51346         this.split.el.addClass("x-layout-split-v");
51347     }
51348     var size = config.initialSize || config.height;
51349     if(typeof size != "undefined"){
51350         this.el.setHeight(size);
51351     }
51352 };
51353 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
51354     orientation: Roo.SplitBar.VERTICAL,
51355     getBox : function(){
51356         if(this.collapsed){
51357             return this.collapsedEl.getBox();
51358         }
51359         var box = this.el.getBox();
51360         if(this.split){
51361             box.height += this.split.el.getHeight();
51362         }
51363         return box;
51364     },
51365     
51366     updateBox : function(box){
51367         if(this.split && !this.collapsed){
51368             box.height -= this.split.el.getHeight();
51369             this.split.el.setLeft(box.x);
51370             this.split.el.setTop(box.y+box.height);
51371             this.split.el.setWidth(box.width);
51372         }
51373         if(this.collapsed){
51374             this.updateBody(box.width, null);
51375         }
51376         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51377     }
51378 });
51379
51380 Roo.SouthLayoutRegion = function(mgr, config){
51381     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
51382     if(this.split){
51383         this.split.placement = Roo.SplitBar.BOTTOM;
51384         this.split.orientation = Roo.SplitBar.VERTICAL;
51385         this.split.el.addClass("x-layout-split-v");
51386     }
51387     var size = config.initialSize || config.height;
51388     if(typeof size != "undefined"){
51389         this.el.setHeight(size);
51390     }
51391 };
51392 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
51393     orientation: Roo.SplitBar.VERTICAL,
51394     getBox : function(){
51395         if(this.collapsed){
51396             return this.collapsedEl.getBox();
51397         }
51398         var box = this.el.getBox();
51399         if(this.split){
51400             var sh = this.split.el.getHeight();
51401             box.height += sh;
51402             box.y -= sh;
51403         }
51404         return box;
51405     },
51406     
51407     updateBox : function(box){
51408         if(this.split && !this.collapsed){
51409             var sh = this.split.el.getHeight();
51410             box.height -= sh;
51411             box.y += sh;
51412             this.split.el.setLeft(box.x);
51413             this.split.el.setTop(box.y-sh);
51414             this.split.el.setWidth(box.width);
51415         }
51416         if(this.collapsed){
51417             this.updateBody(box.width, null);
51418         }
51419         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51420     }
51421 });
51422
51423 Roo.EastLayoutRegion = function(mgr, config){
51424     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
51425     if(this.split){
51426         this.split.placement = Roo.SplitBar.RIGHT;
51427         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51428         this.split.el.addClass("x-layout-split-h");
51429     }
51430     var size = config.initialSize || config.width;
51431     if(typeof size != "undefined"){
51432         this.el.setWidth(size);
51433     }
51434 };
51435 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
51436     orientation: Roo.SplitBar.HORIZONTAL,
51437     getBox : function(){
51438         if(this.collapsed){
51439             return this.collapsedEl.getBox();
51440         }
51441         var box = this.el.getBox();
51442         if(this.split){
51443             var sw = this.split.el.getWidth();
51444             box.width += sw;
51445             box.x -= sw;
51446         }
51447         return box;
51448     },
51449
51450     updateBox : function(box){
51451         if(this.split && !this.collapsed){
51452             var sw = this.split.el.getWidth();
51453             box.width -= sw;
51454             this.split.el.setLeft(box.x);
51455             this.split.el.setTop(box.y);
51456             this.split.el.setHeight(box.height);
51457             box.x += sw;
51458         }
51459         if(this.collapsed){
51460             this.updateBody(null, box.height);
51461         }
51462         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51463     }
51464 });
51465
51466 Roo.WestLayoutRegion = function(mgr, config){
51467     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
51468     if(this.split){
51469         this.split.placement = Roo.SplitBar.LEFT;
51470         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51471         this.split.el.addClass("x-layout-split-h");
51472     }
51473     var size = config.initialSize || config.width;
51474     if(typeof size != "undefined"){
51475         this.el.setWidth(size);
51476     }
51477 };
51478 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
51479     orientation: Roo.SplitBar.HORIZONTAL,
51480     getBox : function(){
51481         if(this.collapsed){
51482             return this.collapsedEl.getBox();
51483         }
51484         var box = this.el.getBox();
51485         if(this.split){
51486             box.width += this.split.el.getWidth();
51487         }
51488         return box;
51489     },
51490     
51491     updateBox : function(box){
51492         if(this.split && !this.collapsed){
51493             var sw = this.split.el.getWidth();
51494             box.width -= sw;
51495             this.split.el.setLeft(box.x+box.width);
51496             this.split.el.setTop(box.y);
51497             this.split.el.setHeight(box.height);
51498         }
51499         if(this.collapsed){
51500             this.updateBody(null, box.height);
51501         }
51502         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51503     }
51504 });
51505 /*
51506  * Based on:
51507  * Ext JS Library 1.1.1
51508  * Copyright(c) 2006-2007, Ext JS, LLC.
51509  *
51510  * Originally Released Under LGPL - original licence link has changed is not relivant.
51511  *
51512  * Fork - LGPL
51513  * <script type="text/javascript">
51514  */
51515  
51516  
51517 /*
51518  * Private internal class for reading and applying state
51519  */
51520 Roo.LayoutStateManager = function(layout){
51521      // default empty state
51522      this.state = {
51523         north: {},
51524         south: {},
51525         east: {},
51526         west: {}       
51527     };
51528 };
51529
51530 Roo.LayoutStateManager.prototype = {
51531     init : function(layout, provider){
51532         this.provider = provider;
51533         var state = provider.get(layout.id+"-layout-state");
51534         if(state){
51535             var wasUpdating = layout.isUpdating();
51536             if(!wasUpdating){
51537                 layout.beginUpdate();
51538             }
51539             for(var key in state){
51540                 if(typeof state[key] != "function"){
51541                     var rstate = state[key];
51542                     var r = layout.getRegion(key);
51543                     if(r && rstate){
51544                         if(rstate.size){
51545                             r.resizeTo(rstate.size);
51546                         }
51547                         if(rstate.collapsed == true){
51548                             r.collapse(true);
51549                         }else{
51550                             r.expand(null, true);
51551                         }
51552                     }
51553                 }
51554             }
51555             if(!wasUpdating){
51556                 layout.endUpdate();
51557             }
51558             this.state = state; 
51559         }
51560         this.layout = layout;
51561         layout.on("regionresized", this.onRegionResized, this);
51562         layout.on("regioncollapsed", this.onRegionCollapsed, this);
51563         layout.on("regionexpanded", this.onRegionExpanded, this);
51564     },
51565     
51566     storeState : function(){
51567         this.provider.set(this.layout.id+"-layout-state", this.state);
51568     },
51569     
51570     onRegionResized : function(region, newSize){
51571         this.state[region.getPosition()].size = newSize;
51572         this.storeState();
51573     },
51574     
51575     onRegionCollapsed : function(region){
51576         this.state[region.getPosition()].collapsed = true;
51577         this.storeState();
51578     },
51579     
51580     onRegionExpanded : function(region){
51581         this.state[region.getPosition()].collapsed = false;
51582         this.storeState();
51583     }
51584 };/*
51585  * Based on:
51586  * Ext JS Library 1.1.1
51587  * Copyright(c) 2006-2007, Ext JS, LLC.
51588  *
51589  * Originally Released Under LGPL - original licence link has changed is not relivant.
51590  *
51591  * Fork - LGPL
51592  * <script type="text/javascript">
51593  */
51594 /**
51595  * @class Roo.ContentPanel
51596  * @extends Roo.util.Observable
51597  * A basic ContentPanel element.
51598  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
51599  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
51600  * @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
51601  * @cfg {Boolean}   closable      True if the panel can be closed/removed
51602  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
51603  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
51604  * @cfg {Toolbar}   toolbar       A toolbar for this panel
51605  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
51606  * @cfg {String} title          The title for this panel
51607  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
51608  * @cfg {String} url            Calls {@link #setUrl} with this value
51609  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
51610  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
51611  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
51612  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
51613
51614  * @constructor
51615  * Create a new ContentPanel.
51616  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
51617  * @param {String/Object} config A string to set only the title or a config object
51618  * @param {String} content (optional) Set the HTML content for this panel
51619  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
51620  */
51621 Roo.ContentPanel = function(el, config, content){
51622     
51623      
51624     /*
51625     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
51626         config = el;
51627         el = Roo.id();
51628     }
51629     if (config && config.parentLayout) { 
51630         el = config.parentLayout.el.createChild(); 
51631     }
51632     */
51633     if(el.autoCreate){ // xtype is available if this is called from factory
51634         config = el;
51635         el = Roo.id();
51636     }
51637     this.el = Roo.get(el);
51638     if(!this.el && config && config.autoCreate){
51639         if(typeof config.autoCreate == "object"){
51640             if(!config.autoCreate.id){
51641                 config.autoCreate.id = config.id||el;
51642             }
51643             this.el = Roo.DomHelper.append(document.body,
51644                         config.autoCreate, true);
51645         }else{
51646             this.el = Roo.DomHelper.append(document.body,
51647                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
51648         }
51649     }
51650     this.closable = false;
51651     this.loaded = false;
51652     this.active = false;
51653     if(typeof config == "string"){
51654         this.title = config;
51655     }else{
51656         Roo.apply(this, config);
51657     }
51658     
51659     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
51660         this.wrapEl = this.el.wrap();
51661         this.toolbar.container = this.el.insertSibling(false, 'before');
51662         this.toolbar = new Roo.Toolbar(this.toolbar);
51663     }
51664     
51665     // xtype created footer. - not sure if will work as we normally have to render first..
51666     if (this.footer && !this.footer.el && this.footer.xtype) {
51667         if (!this.wrapEl) {
51668             this.wrapEl = this.el.wrap();
51669         }
51670     
51671         this.footer.container = this.wrapEl.createChild();
51672          
51673         this.footer = Roo.factory(this.footer, Roo);
51674         
51675     }
51676     
51677     if(this.resizeEl){
51678         this.resizeEl = Roo.get(this.resizeEl, true);
51679     }else{
51680         this.resizeEl = this.el;
51681     }
51682     // handle view.xtype
51683     
51684  
51685     
51686     
51687     this.addEvents({
51688         /**
51689          * @event activate
51690          * Fires when this panel is activated. 
51691          * @param {Roo.ContentPanel} this
51692          */
51693         "activate" : true,
51694         /**
51695          * @event deactivate
51696          * Fires when this panel is activated. 
51697          * @param {Roo.ContentPanel} this
51698          */
51699         "deactivate" : true,
51700
51701         /**
51702          * @event resize
51703          * Fires when this panel is resized if fitToFrame is true.
51704          * @param {Roo.ContentPanel} this
51705          * @param {Number} width The width after any component adjustments
51706          * @param {Number} height The height after any component adjustments
51707          */
51708         "resize" : true,
51709         
51710          /**
51711          * @event render
51712          * Fires when this tab is created
51713          * @param {Roo.ContentPanel} this
51714          */
51715         "render" : true
51716         
51717         
51718         
51719     });
51720     
51721
51722     
51723     
51724     if(this.autoScroll){
51725         this.resizeEl.setStyle("overflow", "auto");
51726     } else {
51727         // fix randome scrolling
51728         this.el.on('scroll', function() {
51729             Roo.log('fix random scolling');
51730             this.scrollTo('top',0); 
51731         });
51732     }
51733     content = content || this.content;
51734     if(content){
51735         this.setContent(content);
51736     }
51737     if(config && config.url){
51738         this.setUrl(this.url, this.params, this.loadOnce);
51739     }
51740     
51741     
51742     
51743     Roo.ContentPanel.superclass.constructor.call(this);
51744     
51745     if (this.view && typeof(this.view.xtype) != 'undefined') {
51746         this.view.el = this.el.appendChild(document.createElement("div"));
51747         this.view = Roo.factory(this.view); 
51748         this.view.render  &&  this.view.render(false, '');  
51749     }
51750     
51751     
51752     this.fireEvent('render', this);
51753 };
51754
51755 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
51756     tabTip:'',
51757     setRegion : function(region){
51758         this.region = region;
51759         if(region){
51760            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
51761         }else{
51762            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
51763         } 
51764     },
51765     
51766     /**
51767      * Returns the toolbar for this Panel if one was configured. 
51768      * @return {Roo.Toolbar} 
51769      */
51770     getToolbar : function(){
51771         return this.toolbar;
51772     },
51773     
51774     setActiveState : function(active){
51775         this.active = active;
51776         if(!active){
51777             this.fireEvent("deactivate", this);
51778         }else{
51779             this.fireEvent("activate", this);
51780         }
51781     },
51782     /**
51783      * Updates this panel's element
51784      * @param {String} content The new content
51785      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51786     */
51787     setContent : function(content, loadScripts){
51788         this.el.update(content, loadScripts);
51789     },
51790
51791     ignoreResize : function(w, h){
51792         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51793             return true;
51794         }else{
51795             this.lastSize = {width: w, height: h};
51796             return false;
51797         }
51798     },
51799     /**
51800      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51801      * @return {Roo.UpdateManager} The UpdateManager
51802      */
51803     getUpdateManager : function(){
51804         return this.el.getUpdateManager();
51805     },
51806      /**
51807      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51808      * @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:
51809 <pre><code>
51810 panel.load({
51811     url: "your-url.php",
51812     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51813     callback: yourFunction,
51814     scope: yourObject, //(optional scope)
51815     discardUrl: false,
51816     nocache: false,
51817     text: "Loading...",
51818     timeout: 30,
51819     scripts: false
51820 });
51821 </code></pre>
51822      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51823      * 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.
51824      * @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}
51825      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51826      * @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.
51827      * @return {Roo.ContentPanel} this
51828      */
51829     load : function(){
51830         var um = this.el.getUpdateManager();
51831         um.update.apply(um, arguments);
51832         return this;
51833     },
51834
51835
51836     /**
51837      * 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.
51838      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51839      * @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)
51840      * @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)
51841      * @return {Roo.UpdateManager} The UpdateManager
51842      */
51843     setUrl : function(url, params, loadOnce){
51844         if(this.refreshDelegate){
51845             this.removeListener("activate", this.refreshDelegate);
51846         }
51847         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51848         this.on("activate", this.refreshDelegate);
51849         return this.el.getUpdateManager();
51850     },
51851     
51852     _handleRefresh : function(url, params, loadOnce){
51853         if(!loadOnce || !this.loaded){
51854             var updater = this.el.getUpdateManager();
51855             updater.update(url, params, this._setLoaded.createDelegate(this));
51856         }
51857     },
51858     
51859     _setLoaded : function(){
51860         this.loaded = true;
51861     }, 
51862     
51863     /**
51864      * Returns this panel's id
51865      * @return {String} 
51866      */
51867     getId : function(){
51868         return this.el.id;
51869     },
51870     
51871     /** 
51872      * Returns this panel's element - used by regiosn to add.
51873      * @return {Roo.Element} 
51874      */
51875     getEl : function(){
51876         return this.wrapEl || this.el;
51877     },
51878     
51879     adjustForComponents : function(width, height)
51880     {
51881         //Roo.log('adjustForComponents ');
51882         if(this.resizeEl != this.el){
51883             width -= this.el.getFrameWidth('lr');
51884             height -= this.el.getFrameWidth('tb');
51885         }
51886         if(this.toolbar){
51887             var te = this.toolbar.getEl();
51888             height -= te.getHeight();
51889             te.setWidth(width);
51890         }
51891         if(this.footer){
51892             var te = this.footer.getEl();
51893             Roo.log("footer:" + te.getHeight());
51894             
51895             height -= te.getHeight();
51896             te.setWidth(width);
51897         }
51898         
51899         
51900         if(this.adjustments){
51901             width += this.adjustments[0];
51902             height += this.adjustments[1];
51903         }
51904         return {"width": width, "height": height};
51905     },
51906     
51907     setSize : function(width, height){
51908         if(this.fitToFrame && !this.ignoreResize(width, height)){
51909             if(this.fitContainer && this.resizeEl != this.el){
51910                 this.el.setSize(width, height);
51911             }
51912             var size = this.adjustForComponents(width, height);
51913             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51914             this.fireEvent('resize', this, size.width, size.height);
51915         }
51916     },
51917     
51918     /**
51919      * Returns this panel's title
51920      * @return {String} 
51921      */
51922     getTitle : function(){
51923         return this.title;
51924     },
51925     
51926     /**
51927      * Set this panel's title
51928      * @param {String} title
51929      */
51930     setTitle : function(title){
51931         this.title = title;
51932         if(this.region){
51933             this.region.updatePanelTitle(this, title);
51934         }
51935     },
51936     
51937     /**
51938      * Returns true is this panel was configured to be closable
51939      * @return {Boolean} 
51940      */
51941     isClosable : function(){
51942         return this.closable;
51943     },
51944     
51945     beforeSlide : function(){
51946         this.el.clip();
51947         this.resizeEl.clip();
51948     },
51949     
51950     afterSlide : function(){
51951         this.el.unclip();
51952         this.resizeEl.unclip();
51953     },
51954     
51955     /**
51956      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51957      *   Will fail silently if the {@link #setUrl} method has not been called.
51958      *   This does not activate the panel, just updates its content.
51959      */
51960     refresh : function(){
51961         if(this.refreshDelegate){
51962            this.loaded = false;
51963            this.refreshDelegate();
51964         }
51965     },
51966     
51967     /**
51968      * Destroys this panel
51969      */
51970     destroy : function(){
51971         this.el.removeAllListeners();
51972         var tempEl = document.createElement("span");
51973         tempEl.appendChild(this.el.dom);
51974         tempEl.innerHTML = "";
51975         this.el.remove();
51976         this.el = null;
51977     },
51978     
51979     /**
51980      * form - if the content panel contains a form - this is a reference to it.
51981      * @type {Roo.form.Form}
51982      */
51983     form : false,
51984     /**
51985      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51986      *    This contains a reference to it.
51987      * @type {Roo.View}
51988      */
51989     view : false,
51990     
51991       /**
51992      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51993      * <pre><code>
51994
51995 layout.addxtype({
51996        xtype : 'Form',
51997        items: [ .... ]
51998    }
51999 );
52000
52001 </code></pre>
52002      * @param {Object} cfg Xtype definition of item to add.
52003      */
52004     
52005     addxtype : function(cfg) {
52006         // add form..
52007         if (cfg.xtype.match(/^Form$/)) {
52008             
52009             var el;
52010             //if (this.footer) {
52011             //    el = this.footer.container.insertSibling(false, 'before');
52012             //} else {
52013                 el = this.el.createChild();
52014             //}
52015
52016             this.form = new  Roo.form.Form(cfg);
52017             
52018             
52019             if ( this.form.allItems.length) {
52020                 this.form.render(el.dom);
52021             }
52022             return this.form;
52023         }
52024         // should only have one of theses..
52025         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
52026             // views.. should not be just added - used named prop 'view''
52027             
52028             cfg.el = this.el.appendChild(document.createElement("div"));
52029             // factory?
52030             
52031             var ret = new Roo.factory(cfg);
52032              
52033              ret.render && ret.render(false, ''); // render blank..
52034             this.view = ret;
52035             return ret;
52036         }
52037         return false;
52038     }
52039 });
52040
52041 /**
52042  * @class Roo.GridPanel
52043  * @extends Roo.ContentPanel
52044  * @constructor
52045  * Create a new GridPanel.
52046  * @param {Roo.grid.Grid} grid The grid for this panel
52047  * @param {String/Object} config A string to set only the panel's title, or a config object
52048  */
52049 Roo.GridPanel = function(grid, config){
52050     
52051   
52052     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
52053         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
52054         
52055     this.wrapper.dom.appendChild(grid.getGridEl().dom);
52056     
52057     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
52058     
52059     if(this.toolbar){
52060         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
52061     }
52062     // xtype created footer. - not sure if will work as we normally have to render first..
52063     if (this.footer && !this.footer.el && this.footer.xtype) {
52064         
52065         this.footer.container = this.grid.getView().getFooterPanel(true);
52066         this.footer.dataSource = this.grid.dataSource;
52067         this.footer = Roo.factory(this.footer, Roo);
52068         
52069     }
52070     
52071     grid.monitorWindowResize = false; // turn off autosizing
52072     grid.autoHeight = false;
52073     grid.autoWidth = false;
52074     this.grid = grid;
52075     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
52076 };
52077
52078 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
52079     getId : function(){
52080         return this.grid.id;
52081     },
52082     
52083     /**
52084      * Returns the grid for this panel
52085      * @return {Roo.grid.Grid} 
52086      */
52087     getGrid : function(){
52088         return this.grid;    
52089     },
52090     
52091     setSize : function(width, height){
52092         if(!this.ignoreResize(width, height)){
52093             var grid = this.grid;
52094             var size = this.adjustForComponents(width, height);
52095             grid.getGridEl().setSize(size.width, size.height);
52096             grid.autoSize();
52097         }
52098     },
52099     
52100     beforeSlide : function(){
52101         this.grid.getView().scroller.clip();
52102     },
52103     
52104     afterSlide : function(){
52105         this.grid.getView().scroller.unclip();
52106     },
52107     
52108     destroy : function(){
52109         this.grid.destroy();
52110         delete this.grid;
52111         Roo.GridPanel.superclass.destroy.call(this); 
52112     }
52113 });
52114
52115
52116 /**
52117  * @class Roo.NestedLayoutPanel
52118  * @extends Roo.ContentPanel
52119  * @constructor
52120  * Create a new NestedLayoutPanel.
52121  * 
52122  * 
52123  * @param {Roo.BorderLayout} layout The layout for this panel
52124  * @param {String/Object} config A string to set only the title or a config object
52125  */
52126 Roo.NestedLayoutPanel = function(layout, config)
52127 {
52128     // construct with only one argument..
52129     /* FIXME - implement nicer consturctors
52130     if (layout.layout) {
52131         config = layout;
52132         layout = config.layout;
52133         delete config.layout;
52134     }
52135     if (layout.xtype && !layout.getEl) {
52136         // then layout needs constructing..
52137         layout = Roo.factory(layout, Roo);
52138     }
52139     */
52140     
52141     
52142     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
52143     
52144     layout.monitorWindowResize = false; // turn off autosizing
52145     this.layout = layout;
52146     this.layout.getEl().addClass("x-layout-nested-layout");
52147     
52148     
52149     
52150     
52151 };
52152
52153 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
52154
52155     setSize : function(width, height){
52156         if(!this.ignoreResize(width, height)){
52157             var size = this.adjustForComponents(width, height);
52158             var el = this.layout.getEl();
52159             el.setSize(size.width, size.height);
52160             var touch = el.dom.offsetWidth;
52161             this.layout.layout();
52162             // ie requires a double layout on the first pass
52163             if(Roo.isIE && !this.initialized){
52164                 this.initialized = true;
52165                 this.layout.layout();
52166             }
52167         }
52168     },
52169     
52170     // activate all subpanels if not currently active..
52171     
52172     setActiveState : function(active){
52173         this.active = active;
52174         if(!active){
52175             this.fireEvent("deactivate", this);
52176             return;
52177         }
52178         
52179         this.fireEvent("activate", this);
52180         // not sure if this should happen before or after..
52181         if (!this.layout) {
52182             return; // should not happen..
52183         }
52184         var reg = false;
52185         for (var r in this.layout.regions) {
52186             reg = this.layout.getRegion(r);
52187             if (reg.getActivePanel()) {
52188                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
52189                 reg.setActivePanel(reg.getActivePanel());
52190                 continue;
52191             }
52192             if (!reg.panels.length) {
52193                 continue;
52194             }
52195             reg.showPanel(reg.getPanel(0));
52196         }
52197         
52198         
52199         
52200         
52201     },
52202     
52203     /**
52204      * Returns the nested BorderLayout for this panel
52205      * @return {Roo.BorderLayout} 
52206      */
52207     getLayout : function(){
52208         return this.layout;
52209     },
52210     
52211      /**
52212      * Adds a xtype elements to the layout of the nested panel
52213      * <pre><code>
52214
52215 panel.addxtype({
52216        xtype : 'ContentPanel',
52217        region: 'west',
52218        items: [ .... ]
52219    }
52220 );
52221
52222 panel.addxtype({
52223         xtype : 'NestedLayoutPanel',
52224         region: 'west',
52225         layout: {
52226            center: { },
52227            west: { }   
52228         },
52229         items : [ ... list of content panels or nested layout panels.. ]
52230    }
52231 );
52232 </code></pre>
52233      * @param {Object} cfg Xtype definition of item to add.
52234      */
52235     addxtype : function(cfg) {
52236         return this.layout.addxtype(cfg);
52237     
52238     }
52239 });
52240
52241 Roo.ScrollPanel = function(el, config, content){
52242     config = config || {};
52243     config.fitToFrame = true;
52244     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
52245     
52246     this.el.dom.style.overflow = "hidden";
52247     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
52248     this.el.removeClass("x-layout-inactive-content");
52249     this.el.on("mousewheel", this.onWheel, this);
52250
52251     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
52252     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
52253     up.unselectable(); down.unselectable();
52254     up.on("click", this.scrollUp, this);
52255     down.on("click", this.scrollDown, this);
52256     up.addClassOnOver("x-scroller-btn-over");
52257     down.addClassOnOver("x-scroller-btn-over");
52258     up.addClassOnClick("x-scroller-btn-click");
52259     down.addClassOnClick("x-scroller-btn-click");
52260     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
52261
52262     this.resizeEl = this.el;
52263     this.el = wrap; this.up = up; this.down = down;
52264 };
52265
52266 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
52267     increment : 100,
52268     wheelIncrement : 5,
52269     scrollUp : function(){
52270         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
52271     },
52272
52273     scrollDown : function(){
52274         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
52275     },
52276
52277     afterScroll : function(){
52278         var el = this.resizeEl;
52279         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
52280         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52281         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52282     },
52283
52284     setSize : function(){
52285         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
52286         this.afterScroll();
52287     },
52288
52289     onWheel : function(e){
52290         var d = e.getWheelDelta();
52291         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
52292         this.afterScroll();
52293         e.stopEvent();
52294     },
52295
52296     setContent : function(content, loadScripts){
52297         this.resizeEl.update(content, loadScripts);
52298     }
52299
52300 });
52301
52302
52303
52304
52305
52306
52307
52308
52309
52310 /**
52311  * @class Roo.TreePanel
52312  * @extends Roo.ContentPanel
52313  * @constructor
52314  * Create a new TreePanel. - defaults to fit/scoll contents.
52315  * @param {String/Object} config A string to set only the panel's title, or a config object
52316  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
52317  */
52318 Roo.TreePanel = function(config){
52319     var el = config.el;
52320     var tree = config.tree;
52321     delete config.tree; 
52322     delete config.el; // hopefull!
52323     
52324     // wrapper for IE7 strict & safari scroll issue
52325     
52326     var treeEl = el.createChild();
52327     config.resizeEl = treeEl;
52328     
52329     
52330     
52331     Roo.TreePanel.superclass.constructor.call(this, el, config);
52332  
52333  
52334     this.tree = new Roo.tree.TreePanel(treeEl , tree);
52335     //console.log(tree);
52336     this.on('activate', function()
52337     {
52338         if (this.tree.rendered) {
52339             return;
52340         }
52341         //console.log('render tree');
52342         this.tree.render();
52343     });
52344     // this should not be needed.. - it's actually the 'el' that resizes?
52345     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
52346     
52347     //this.on('resize',  function (cp, w, h) {
52348     //        this.tree.innerCt.setWidth(w);
52349     //        this.tree.innerCt.setHeight(h);
52350     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
52351     //});
52352
52353         
52354     
52355 };
52356
52357 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
52358     fitToFrame : true,
52359     autoScroll : true
52360 });
52361
52362
52363
52364
52365
52366
52367
52368
52369
52370
52371
52372 /*
52373  * Based on:
52374  * Ext JS Library 1.1.1
52375  * Copyright(c) 2006-2007, Ext JS, LLC.
52376  *
52377  * Originally Released Under LGPL - original licence link has changed is not relivant.
52378  *
52379  * Fork - LGPL
52380  * <script type="text/javascript">
52381  */
52382  
52383
52384 /**
52385  * @class Roo.ReaderLayout
52386  * @extends Roo.BorderLayout
52387  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
52388  * center region containing two nested regions (a top one for a list view and one for item preview below),
52389  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
52390  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
52391  * expedites the setup of the overall layout and regions for this common application style.
52392  * Example:
52393  <pre><code>
52394 var reader = new Roo.ReaderLayout();
52395 var CP = Roo.ContentPanel;  // shortcut for adding
52396
52397 reader.beginUpdate();
52398 reader.add("north", new CP("north", "North"));
52399 reader.add("west", new CP("west", {title: "West"}));
52400 reader.add("east", new CP("east", {title: "East"}));
52401
52402 reader.regions.listView.add(new CP("listView", "List"));
52403 reader.regions.preview.add(new CP("preview", "Preview"));
52404 reader.endUpdate();
52405 </code></pre>
52406 * @constructor
52407 * Create a new ReaderLayout
52408 * @param {Object} config Configuration options
52409 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
52410 * document.body if omitted)
52411 */
52412 Roo.ReaderLayout = function(config, renderTo){
52413     var c = config || {size:{}};
52414     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
52415         north: c.north !== false ? Roo.apply({
52416             split:false,
52417             initialSize: 32,
52418             titlebar: false
52419         }, c.north) : false,
52420         west: c.west !== false ? Roo.apply({
52421             split:true,
52422             initialSize: 200,
52423             minSize: 175,
52424             maxSize: 400,
52425             titlebar: true,
52426             collapsible: true,
52427             animate: true,
52428             margins:{left:5,right:0,bottom:5,top:5},
52429             cmargins:{left:5,right:5,bottom:5,top:5}
52430         }, c.west) : false,
52431         east: c.east !== false ? Roo.apply({
52432             split:true,
52433             initialSize: 200,
52434             minSize: 175,
52435             maxSize: 400,
52436             titlebar: true,
52437             collapsible: true,
52438             animate: true,
52439             margins:{left:0,right:5,bottom:5,top:5},
52440             cmargins:{left:5,right:5,bottom:5,top:5}
52441         }, c.east) : false,
52442         center: Roo.apply({
52443             tabPosition: 'top',
52444             autoScroll:false,
52445             closeOnTab: true,
52446             titlebar:false,
52447             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
52448         }, c.center)
52449     });
52450
52451     this.el.addClass('x-reader');
52452
52453     this.beginUpdate();
52454
52455     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
52456         south: c.preview !== false ? Roo.apply({
52457             split:true,
52458             initialSize: 200,
52459             minSize: 100,
52460             autoScroll:true,
52461             collapsible:true,
52462             titlebar: true,
52463             cmargins:{top:5,left:0, right:0, bottom:0}
52464         }, c.preview) : false,
52465         center: Roo.apply({
52466             autoScroll:false,
52467             titlebar:false,
52468             minHeight:200
52469         }, c.listView)
52470     });
52471     this.add('center', new Roo.NestedLayoutPanel(inner,
52472             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
52473
52474     this.endUpdate();
52475
52476     this.regions.preview = inner.getRegion('south');
52477     this.regions.listView = inner.getRegion('center');
52478 };
52479
52480 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
52481  * Based on:
52482  * Ext JS Library 1.1.1
52483  * Copyright(c) 2006-2007, Ext JS, LLC.
52484  *
52485  * Originally Released Under LGPL - original licence link has changed is not relivant.
52486  *
52487  * Fork - LGPL
52488  * <script type="text/javascript">
52489  */
52490  
52491 /**
52492  * @class Roo.grid.Grid
52493  * @extends Roo.util.Observable
52494  * This class represents the primary interface of a component based grid control.
52495  * <br><br>Usage:<pre><code>
52496  var grid = new Roo.grid.Grid("my-container-id", {
52497      ds: myDataStore,
52498      cm: myColModel,
52499      selModel: mySelectionModel,
52500      autoSizeColumns: true,
52501      monitorWindowResize: false,
52502      trackMouseOver: true
52503  });
52504  // set any options
52505  grid.render();
52506  * </code></pre>
52507  * <b>Common Problems:</b><br/>
52508  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
52509  * element will correct this<br/>
52510  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
52511  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
52512  * are unpredictable.<br/>
52513  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
52514  * grid to calculate dimensions/offsets.<br/>
52515   * @constructor
52516  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
52517  * The container MUST have some type of size defined for the grid to fill. The container will be
52518  * automatically set to position relative if it isn't already.
52519  * @param {Object} config A config object that sets properties on this grid.
52520  */
52521 Roo.grid.Grid = function(container, config){
52522         // initialize the container
52523         this.container = Roo.get(container);
52524         this.container.update("");
52525         this.container.setStyle("overflow", "hidden");
52526     this.container.addClass('x-grid-container');
52527
52528     this.id = this.container.id;
52529
52530     Roo.apply(this, config);
52531     // check and correct shorthanded configs
52532     if(this.ds){
52533         this.dataSource = this.ds;
52534         delete this.ds;
52535     }
52536     if(this.cm){
52537         this.colModel = this.cm;
52538         delete this.cm;
52539     }
52540     if(this.sm){
52541         this.selModel = this.sm;
52542         delete this.sm;
52543     }
52544
52545     if (this.selModel) {
52546         this.selModel = Roo.factory(this.selModel, Roo.grid);
52547         this.sm = this.selModel;
52548         this.sm.xmodule = this.xmodule || false;
52549     }
52550     if (typeof(this.colModel.config) == 'undefined') {
52551         this.colModel = new Roo.grid.ColumnModel(this.colModel);
52552         this.cm = this.colModel;
52553         this.cm.xmodule = this.xmodule || false;
52554     }
52555     if (this.dataSource) {
52556         this.dataSource= Roo.factory(this.dataSource, Roo.data);
52557         this.ds = this.dataSource;
52558         this.ds.xmodule = this.xmodule || false;
52559          
52560     }
52561     
52562     
52563     
52564     if(this.width){
52565         this.container.setWidth(this.width);
52566     }
52567
52568     if(this.height){
52569         this.container.setHeight(this.height);
52570     }
52571     /** @private */
52572         this.addEvents({
52573         // raw events
52574         /**
52575          * @event click
52576          * The raw click event for the entire grid.
52577          * @param {Roo.EventObject} e
52578          */
52579         "click" : true,
52580         /**
52581          * @event dblclick
52582          * The raw dblclick event for the entire grid.
52583          * @param {Roo.EventObject} e
52584          */
52585         "dblclick" : true,
52586         /**
52587          * @event contextmenu
52588          * The raw contextmenu event for the entire grid.
52589          * @param {Roo.EventObject} e
52590          */
52591         "contextmenu" : true,
52592         /**
52593          * @event mousedown
52594          * The raw mousedown event for the entire grid.
52595          * @param {Roo.EventObject} e
52596          */
52597         "mousedown" : true,
52598         /**
52599          * @event mouseup
52600          * The raw mouseup event for the entire grid.
52601          * @param {Roo.EventObject} e
52602          */
52603         "mouseup" : true,
52604         /**
52605          * @event mouseover
52606          * The raw mouseover event for the entire grid.
52607          * @param {Roo.EventObject} e
52608          */
52609         "mouseover" : true,
52610         /**
52611          * @event mouseout
52612          * The raw mouseout event for the entire grid.
52613          * @param {Roo.EventObject} e
52614          */
52615         "mouseout" : true,
52616         /**
52617          * @event keypress
52618          * The raw keypress event for the entire grid.
52619          * @param {Roo.EventObject} e
52620          */
52621         "keypress" : true,
52622         /**
52623          * @event keydown
52624          * The raw keydown event for the entire grid.
52625          * @param {Roo.EventObject} e
52626          */
52627         "keydown" : true,
52628
52629         // custom events
52630
52631         /**
52632          * @event cellclick
52633          * Fires when a cell is clicked
52634          * @param {Grid} this
52635          * @param {Number} rowIndex
52636          * @param {Number} columnIndex
52637          * @param {Roo.EventObject} e
52638          */
52639         "cellclick" : true,
52640         /**
52641          * @event celldblclick
52642          * Fires when a cell is double clicked
52643          * @param {Grid} this
52644          * @param {Number} rowIndex
52645          * @param {Number} columnIndex
52646          * @param {Roo.EventObject} e
52647          */
52648         "celldblclick" : true,
52649         /**
52650          * @event rowclick
52651          * Fires when a row is clicked
52652          * @param {Grid} this
52653          * @param {Number} rowIndex
52654          * @param {Roo.EventObject} e
52655          */
52656         "rowclick" : true,
52657         /**
52658          * @event rowdblclick
52659          * Fires when a row is double clicked
52660          * @param {Grid} this
52661          * @param {Number} rowIndex
52662          * @param {Roo.EventObject} e
52663          */
52664         "rowdblclick" : true,
52665         /**
52666          * @event headerclick
52667          * Fires when a header is clicked
52668          * @param {Grid} this
52669          * @param {Number} columnIndex
52670          * @param {Roo.EventObject} e
52671          */
52672         "headerclick" : true,
52673         /**
52674          * @event headerdblclick
52675          * Fires when a header cell is double clicked
52676          * @param {Grid} this
52677          * @param {Number} columnIndex
52678          * @param {Roo.EventObject} e
52679          */
52680         "headerdblclick" : true,
52681         /**
52682          * @event rowcontextmenu
52683          * Fires when a row is right clicked
52684          * @param {Grid} this
52685          * @param {Number} rowIndex
52686          * @param {Roo.EventObject} e
52687          */
52688         "rowcontextmenu" : true,
52689         /**
52690          * @event cellcontextmenu
52691          * Fires when a cell is right clicked
52692          * @param {Grid} this
52693          * @param {Number} rowIndex
52694          * @param {Number} cellIndex
52695          * @param {Roo.EventObject} e
52696          */
52697          "cellcontextmenu" : true,
52698         /**
52699          * @event headercontextmenu
52700          * Fires when a header is right clicked
52701          * @param {Grid} this
52702          * @param {Number} columnIndex
52703          * @param {Roo.EventObject} e
52704          */
52705         "headercontextmenu" : true,
52706         /**
52707          * @event bodyscroll
52708          * Fires when the body element is scrolled
52709          * @param {Number} scrollLeft
52710          * @param {Number} scrollTop
52711          */
52712         "bodyscroll" : true,
52713         /**
52714          * @event columnresize
52715          * Fires when the user resizes a column
52716          * @param {Number} columnIndex
52717          * @param {Number} newSize
52718          */
52719         "columnresize" : true,
52720         /**
52721          * @event columnmove
52722          * Fires when the user moves a column
52723          * @param {Number} oldIndex
52724          * @param {Number} newIndex
52725          */
52726         "columnmove" : true,
52727         /**
52728          * @event startdrag
52729          * Fires when row(s) start being dragged
52730          * @param {Grid} this
52731          * @param {Roo.GridDD} dd The drag drop object
52732          * @param {event} e The raw browser event
52733          */
52734         "startdrag" : true,
52735         /**
52736          * @event enddrag
52737          * Fires when a drag operation is complete
52738          * @param {Grid} this
52739          * @param {Roo.GridDD} dd The drag drop object
52740          * @param {event} e The raw browser event
52741          */
52742         "enddrag" : true,
52743         /**
52744          * @event dragdrop
52745          * Fires when dragged row(s) are dropped on a valid DD target
52746          * @param {Grid} this
52747          * @param {Roo.GridDD} dd The drag drop object
52748          * @param {String} targetId The target drag drop object
52749          * @param {event} e The raw browser event
52750          */
52751         "dragdrop" : true,
52752         /**
52753          * @event dragover
52754          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
52755          * @param {Grid} this
52756          * @param {Roo.GridDD} dd The drag drop object
52757          * @param {String} targetId The target drag drop object
52758          * @param {event} e The raw browser event
52759          */
52760         "dragover" : true,
52761         /**
52762          * @event dragenter
52763          *  Fires when the dragged row(s) first cross another DD target while being dragged
52764          * @param {Grid} this
52765          * @param {Roo.GridDD} dd The drag drop object
52766          * @param {String} targetId The target drag drop object
52767          * @param {event} e The raw browser event
52768          */
52769         "dragenter" : true,
52770         /**
52771          * @event dragout
52772          * Fires when the dragged row(s) leave another DD target while being dragged
52773          * @param {Grid} this
52774          * @param {Roo.GridDD} dd The drag drop object
52775          * @param {String} targetId The target drag drop object
52776          * @param {event} e The raw browser event
52777          */
52778         "dragout" : true,
52779         /**
52780          * @event rowclass
52781          * Fires when a row is rendered, so you can change add a style to it.
52782          * @param {GridView} gridview   The grid view
52783          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52784          */
52785         'rowclass' : true,
52786
52787         /**
52788          * @event render
52789          * Fires when the grid is rendered
52790          * @param {Grid} grid
52791          */
52792         'render' : true
52793     });
52794
52795     Roo.grid.Grid.superclass.constructor.call(this);
52796 };
52797 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52798     
52799     /**
52800      * @cfg {String} ddGroup - drag drop group.
52801      */
52802
52803     /**
52804      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52805      */
52806     minColumnWidth : 25,
52807
52808     /**
52809      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52810      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52811      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52812      */
52813     autoSizeColumns : false,
52814
52815     /**
52816      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52817      */
52818     autoSizeHeaders : true,
52819
52820     /**
52821      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52822      */
52823     monitorWindowResize : true,
52824
52825     /**
52826      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52827      * rows measured to get a columns size. Default is 0 (all rows).
52828      */
52829     maxRowsToMeasure : 0,
52830
52831     /**
52832      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52833      */
52834     trackMouseOver : true,
52835
52836     /**
52837     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52838     */
52839     
52840     /**
52841     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52842     */
52843     enableDragDrop : false,
52844     
52845     /**
52846     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52847     */
52848     enableColumnMove : true,
52849     
52850     /**
52851     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52852     */
52853     enableColumnHide : true,
52854     
52855     /**
52856     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52857     */
52858     enableRowHeightSync : false,
52859     
52860     /**
52861     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52862     */
52863     stripeRows : true,
52864     
52865     /**
52866     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52867     */
52868     autoHeight : false,
52869
52870     /**
52871      * @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.
52872      */
52873     autoExpandColumn : false,
52874
52875     /**
52876     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52877     * Default is 50.
52878     */
52879     autoExpandMin : 50,
52880
52881     /**
52882     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52883     */
52884     autoExpandMax : 1000,
52885
52886     /**
52887     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52888     */
52889     view : null,
52890
52891     /**
52892     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52893     */
52894     loadMask : false,
52895     /**
52896     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52897     */
52898     dropTarget: false,
52899     
52900    
52901     
52902     // private
52903     rendered : false,
52904
52905     /**
52906     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52907     * of a fixed width. Default is false.
52908     */
52909     /**
52910     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52911     */
52912     /**
52913      * Called once after all setup has been completed and the grid is ready to be rendered.
52914      * @return {Roo.grid.Grid} this
52915      */
52916     render : function()
52917     {
52918         var c = this.container;
52919         // try to detect autoHeight/width mode
52920         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52921             this.autoHeight = true;
52922         }
52923         var view = this.getView();
52924         view.init(this);
52925
52926         c.on("click", this.onClick, this);
52927         c.on("dblclick", this.onDblClick, this);
52928         c.on("contextmenu", this.onContextMenu, this);
52929         c.on("keydown", this.onKeyDown, this);
52930         if (Roo.isTouch) {
52931             c.on("touchstart", this.onTouchStart, this);
52932         }
52933
52934         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52935
52936         this.getSelectionModel().init(this);
52937
52938         view.render();
52939
52940         if(this.loadMask){
52941             this.loadMask = new Roo.LoadMask(this.container,
52942                     Roo.apply({store:this.dataSource}, this.loadMask));
52943         }
52944         
52945         
52946         if (this.toolbar && this.toolbar.xtype) {
52947             this.toolbar.container = this.getView().getHeaderPanel(true);
52948             this.toolbar = new Roo.Toolbar(this.toolbar);
52949         }
52950         if (this.footer && this.footer.xtype) {
52951             this.footer.dataSource = this.getDataSource();
52952             this.footer.container = this.getView().getFooterPanel(true);
52953             this.footer = Roo.factory(this.footer, Roo);
52954         }
52955         if (this.dropTarget && this.dropTarget.xtype) {
52956             delete this.dropTarget.xtype;
52957             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52958         }
52959         
52960         
52961         this.rendered = true;
52962         this.fireEvent('render', this);
52963         return this;
52964     },
52965
52966         /**
52967          * Reconfigures the grid to use a different Store and Column Model.
52968          * The View will be bound to the new objects and refreshed.
52969          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52970          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52971          */
52972     reconfigure : function(dataSource, colModel){
52973         if(this.loadMask){
52974             this.loadMask.destroy();
52975             this.loadMask = new Roo.LoadMask(this.container,
52976                     Roo.apply({store:dataSource}, this.loadMask));
52977         }
52978         this.view.bind(dataSource, colModel);
52979         this.dataSource = dataSource;
52980         this.colModel = colModel;
52981         this.view.refresh(true);
52982     },
52983
52984     // private
52985     onKeyDown : function(e){
52986         this.fireEvent("keydown", e);
52987     },
52988
52989     /**
52990      * Destroy this grid.
52991      * @param {Boolean} removeEl True to remove the element
52992      */
52993     destroy : function(removeEl, keepListeners){
52994         if(this.loadMask){
52995             this.loadMask.destroy();
52996         }
52997         var c = this.container;
52998         c.removeAllListeners();
52999         this.view.destroy();
53000         this.colModel.purgeListeners();
53001         if(!keepListeners){
53002             this.purgeListeners();
53003         }
53004         c.update("");
53005         if(removeEl === true){
53006             c.remove();
53007         }
53008     },
53009
53010     // private
53011     processEvent : function(name, e){
53012         // does this fire select???
53013         //Roo.log('grid:processEvent '  + name);
53014         
53015         if (name != 'touchstart' ) {
53016             this.fireEvent(name, e);    
53017         }
53018         
53019         var t = e.getTarget();
53020         var v = this.view;
53021         var header = v.findHeaderIndex(t);
53022         if(header !== false){
53023             var ename = name == 'touchstart' ? 'click' : name;
53024              
53025             this.fireEvent("header" + ename, this, header, e);
53026         }else{
53027             var row = v.findRowIndex(t);
53028             var cell = v.findCellIndex(t);
53029             if (name == 'touchstart') {
53030                 // first touch is always a click.
53031                 // hopefull this happens after selection is updated.?
53032                 name = false;
53033                 
53034                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
53035                     var cs = this.selModel.getSelectedCell();
53036                     if (row == cs[0] && cell == cs[1]){
53037                         name = 'dblclick';
53038                     }
53039                 }
53040                 if (typeof(this.selModel.getSelections) != 'undefined') {
53041                     var cs = this.selModel.getSelections();
53042                     var ds = this.dataSource;
53043                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
53044                         name = 'dblclick';
53045                     }
53046                 }
53047                 if (!name) {
53048                     return;
53049                 }
53050             }
53051             
53052             
53053             if(row !== false){
53054                 this.fireEvent("row" + name, this, row, e);
53055                 if(cell !== false){
53056                     this.fireEvent("cell" + name, this, row, cell, e);
53057                 }
53058             }
53059         }
53060     },
53061
53062     // private
53063     onClick : function(e){
53064         this.processEvent("click", e);
53065     },
53066    // private
53067     onTouchStart : function(e){
53068         this.processEvent("touchstart", e);
53069     },
53070
53071     // private
53072     onContextMenu : function(e, t){
53073         this.processEvent("contextmenu", e);
53074     },
53075
53076     // private
53077     onDblClick : function(e){
53078         this.processEvent("dblclick", e);
53079     },
53080
53081     // private
53082     walkCells : function(row, col, step, fn, scope){
53083         var cm = this.colModel, clen = cm.getColumnCount();
53084         var ds = this.dataSource, rlen = ds.getCount(), first = true;
53085         if(step < 0){
53086             if(col < 0){
53087                 row--;
53088                 first = false;
53089             }
53090             while(row >= 0){
53091                 if(!first){
53092                     col = clen-1;
53093                 }
53094                 first = false;
53095                 while(col >= 0){
53096                     if(fn.call(scope || this, row, col, cm) === true){
53097                         return [row, col];
53098                     }
53099                     col--;
53100                 }
53101                 row--;
53102             }
53103         } else {
53104             if(col >= clen){
53105                 row++;
53106                 first = false;
53107             }
53108             while(row < rlen){
53109                 if(!first){
53110                     col = 0;
53111                 }
53112                 first = false;
53113                 while(col < clen){
53114                     if(fn.call(scope || this, row, col, cm) === true){
53115                         return [row, col];
53116                     }
53117                     col++;
53118                 }
53119                 row++;
53120             }
53121         }
53122         return null;
53123     },
53124
53125     // private
53126     getSelections : function(){
53127         return this.selModel.getSelections();
53128     },
53129
53130     /**
53131      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
53132      * but if manual update is required this method will initiate it.
53133      */
53134     autoSize : function(){
53135         if(this.rendered){
53136             this.view.layout();
53137             if(this.view.adjustForScroll){
53138                 this.view.adjustForScroll();
53139             }
53140         }
53141     },
53142
53143     /**
53144      * Returns the grid's underlying element.
53145      * @return {Element} The element
53146      */
53147     getGridEl : function(){
53148         return this.container;
53149     },
53150
53151     // private for compatibility, overridden by editor grid
53152     stopEditing : function(){},
53153
53154     /**
53155      * Returns the grid's SelectionModel.
53156      * @return {SelectionModel}
53157      */
53158     getSelectionModel : function(){
53159         if(!this.selModel){
53160             this.selModel = new Roo.grid.RowSelectionModel();
53161         }
53162         return this.selModel;
53163     },
53164
53165     /**
53166      * Returns the grid's DataSource.
53167      * @return {DataSource}
53168      */
53169     getDataSource : function(){
53170         return this.dataSource;
53171     },
53172
53173     /**
53174      * Returns the grid's ColumnModel.
53175      * @return {ColumnModel}
53176      */
53177     getColumnModel : function(){
53178         return this.colModel;
53179     },
53180
53181     /**
53182      * Returns the grid's GridView object.
53183      * @return {GridView}
53184      */
53185     getView : function(){
53186         if(!this.view){
53187             this.view = new Roo.grid.GridView(this.viewConfig);
53188         }
53189         return this.view;
53190     },
53191     /**
53192      * Called to get grid's drag proxy text, by default returns this.ddText.
53193      * @return {String}
53194      */
53195     getDragDropText : function(){
53196         var count = this.selModel.getCount();
53197         return String.format(this.ddText, count, count == 1 ? '' : 's');
53198     }
53199 });
53200 /**
53201  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
53202  * %0 is replaced with the number of selected rows.
53203  * @type String
53204  */
53205 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
53206  * Based on:
53207  * Ext JS Library 1.1.1
53208  * Copyright(c) 2006-2007, Ext JS, LLC.
53209  *
53210  * Originally Released Under LGPL - original licence link has changed is not relivant.
53211  *
53212  * Fork - LGPL
53213  * <script type="text/javascript">
53214  */
53215  
53216 Roo.grid.AbstractGridView = function(){
53217         this.grid = null;
53218         
53219         this.events = {
53220             "beforerowremoved" : true,
53221             "beforerowsinserted" : true,
53222             "beforerefresh" : true,
53223             "rowremoved" : true,
53224             "rowsinserted" : true,
53225             "rowupdated" : true,
53226             "refresh" : true
53227         };
53228     Roo.grid.AbstractGridView.superclass.constructor.call(this);
53229 };
53230
53231 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
53232     rowClass : "x-grid-row",
53233     cellClass : "x-grid-cell",
53234     tdClass : "x-grid-td",
53235     hdClass : "x-grid-hd",
53236     splitClass : "x-grid-hd-split",
53237     
53238     init: function(grid){
53239         this.grid = grid;
53240                 var cid = this.grid.getGridEl().id;
53241         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
53242         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
53243         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
53244         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
53245         },
53246         
53247     getColumnRenderers : function(){
53248         var renderers = [];
53249         var cm = this.grid.colModel;
53250         var colCount = cm.getColumnCount();
53251         for(var i = 0; i < colCount; i++){
53252             renderers[i] = cm.getRenderer(i);
53253         }
53254         return renderers;
53255     },
53256     
53257     getColumnIds : function(){
53258         var ids = [];
53259         var cm = this.grid.colModel;
53260         var colCount = cm.getColumnCount();
53261         for(var i = 0; i < colCount; i++){
53262             ids[i] = cm.getColumnId(i);
53263         }
53264         return ids;
53265     },
53266     
53267     getDataIndexes : function(){
53268         if(!this.indexMap){
53269             this.indexMap = this.buildIndexMap();
53270         }
53271         return this.indexMap.colToData;
53272     },
53273     
53274     getColumnIndexByDataIndex : function(dataIndex){
53275         if(!this.indexMap){
53276             this.indexMap = this.buildIndexMap();
53277         }
53278         return this.indexMap.dataToCol[dataIndex];
53279     },
53280     
53281     /**
53282      * Set a css style for a column dynamically. 
53283      * @param {Number} colIndex The index of the column
53284      * @param {String} name The css property name
53285      * @param {String} value The css value
53286      */
53287     setCSSStyle : function(colIndex, name, value){
53288         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
53289         Roo.util.CSS.updateRule(selector, name, value);
53290     },
53291     
53292     generateRules : function(cm){
53293         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
53294         Roo.util.CSS.removeStyleSheet(rulesId);
53295         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53296             var cid = cm.getColumnId(i);
53297             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
53298                          this.tdSelector, cid, " {\n}\n",
53299                          this.hdSelector, cid, " {\n}\n",
53300                          this.splitSelector, cid, " {\n}\n");
53301         }
53302         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53303     }
53304 });/*
53305  * Based on:
53306  * Ext JS Library 1.1.1
53307  * Copyright(c) 2006-2007, Ext JS, LLC.
53308  *
53309  * Originally Released Under LGPL - original licence link has changed is not relivant.
53310  *
53311  * Fork - LGPL
53312  * <script type="text/javascript">
53313  */
53314
53315 // private
53316 // This is a support class used internally by the Grid components
53317 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
53318     this.grid = grid;
53319     this.view = grid.getView();
53320     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53321     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
53322     if(hd2){
53323         this.setHandleElId(Roo.id(hd));
53324         this.setOuterHandleElId(Roo.id(hd2));
53325     }
53326     this.scroll = false;
53327 };
53328 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
53329     maxDragWidth: 120,
53330     getDragData : function(e){
53331         var t = Roo.lib.Event.getTarget(e);
53332         var h = this.view.findHeaderCell(t);
53333         if(h){
53334             return {ddel: h.firstChild, header:h};
53335         }
53336         return false;
53337     },
53338
53339     onInitDrag : function(e){
53340         this.view.headersDisabled = true;
53341         var clone = this.dragData.ddel.cloneNode(true);
53342         clone.id = Roo.id();
53343         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
53344         this.proxy.update(clone);
53345         return true;
53346     },
53347
53348     afterValidDrop : function(){
53349         var v = this.view;
53350         setTimeout(function(){
53351             v.headersDisabled = false;
53352         }, 50);
53353     },
53354
53355     afterInvalidDrop : function(){
53356         var v = this.view;
53357         setTimeout(function(){
53358             v.headersDisabled = false;
53359         }, 50);
53360     }
53361 });
53362 /*
53363  * Based on:
53364  * Ext JS Library 1.1.1
53365  * Copyright(c) 2006-2007, Ext JS, LLC.
53366  *
53367  * Originally Released Under LGPL - original licence link has changed is not relivant.
53368  *
53369  * Fork - LGPL
53370  * <script type="text/javascript">
53371  */
53372 // private
53373 // This is a support class used internally by the Grid components
53374 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
53375     this.grid = grid;
53376     this.view = grid.getView();
53377     // split the proxies so they don't interfere with mouse events
53378     this.proxyTop = Roo.DomHelper.append(document.body, {
53379         cls:"col-move-top", html:"&#160;"
53380     }, true);
53381     this.proxyBottom = Roo.DomHelper.append(document.body, {
53382         cls:"col-move-bottom", html:"&#160;"
53383     }, true);
53384     this.proxyTop.hide = this.proxyBottom.hide = function(){
53385         this.setLeftTop(-100,-100);
53386         this.setStyle("visibility", "hidden");
53387     };
53388     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53389     // temporarily disabled
53390     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
53391     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
53392 };
53393 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
53394     proxyOffsets : [-4, -9],
53395     fly: Roo.Element.fly,
53396
53397     getTargetFromEvent : function(e){
53398         var t = Roo.lib.Event.getTarget(e);
53399         var cindex = this.view.findCellIndex(t);
53400         if(cindex !== false){
53401             return this.view.getHeaderCell(cindex);
53402         }
53403         return null;
53404     },
53405
53406     nextVisible : function(h){
53407         var v = this.view, cm = this.grid.colModel;
53408         h = h.nextSibling;
53409         while(h){
53410             if(!cm.isHidden(v.getCellIndex(h))){
53411                 return h;
53412             }
53413             h = h.nextSibling;
53414         }
53415         return null;
53416     },
53417
53418     prevVisible : function(h){
53419         var v = this.view, cm = this.grid.colModel;
53420         h = h.prevSibling;
53421         while(h){
53422             if(!cm.isHidden(v.getCellIndex(h))){
53423                 return h;
53424             }
53425             h = h.prevSibling;
53426         }
53427         return null;
53428     },
53429
53430     positionIndicator : function(h, n, e){
53431         var x = Roo.lib.Event.getPageX(e);
53432         var r = Roo.lib.Dom.getRegion(n.firstChild);
53433         var px, pt, py = r.top + this.proxyOffsets[1];
53434         if((r.right - x) <= (r.right-r.left)/2){
53435             px = r.right+this.view.borderWidth;
53436             pt = "after";
53437         }else{
53438             px = r.left;
53439             pt = "before";
53440         }
53441         var oldIndex = this.view.getCellIndex(h);
53442         var newIndex = this.view.getCellIndex(n);
53443
53444         if(this.grid.colModel.isFixed(newIndex)){
53445             return false;
53446         }
53447
53448         var locked = this.grid.colModel.isLocked(newIndex);
53449
53450         if(pt == "after"){
53451             newIndex++;
53452         }
53453         if(oldIndex < newIndex){
53454             newIndex--;
53455         }
53456         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
53457             return false;
53458         }
53459         px +=  this.proxyOffsets[0];
53460         this.proxyTop.setLeftTop(px, py);
53461         this.proxyTop.show();
53462         if(!this.bottomOffset){
53463             this.bottomOffset = this.view.mainHd.getHeight();
53464         }
53465         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
53466         this.proxyBottom.show();
53467         return pt;
53468     },
53469
53470     onNodeEnter : function(n, dd, e, data){
53471         if(data.header != n){
53472             this.positionIndicator(data.header, n, e);
53473         }
53474     },
53475
53476     onNodeOver : function(n, dd, e, data){
53477         var result = false;
53478         if(data.header != n){
53479             result = this.positionIndicator(data.header, n, e);
53480         }
53481         if(!result){
53482             this.proxyTop.hide();
53483             this.proxyBottom.hide();
53484         }
53485         return result ? this.dropAllowed : this.dropNotAllowed;
53486     },
53487
53488     onNodeOut : function(n, dd, e, data){
53489         this.proxyTop.hide();
53490         this.proxyBottom.hide();
53491     },
53492
53493     onNodeDrop : function(n, dd, e, data){
53494         var h = data.header;
53495         if(h != n){
53496             var cm = this.grid.colModel;
53497             var x = Roo.lib.Event.getPageX(e);
53498             var r = Roo.lib.Dom.getRegion(n.firstChild);
53499             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
53500             var oldIndex = this.view.getCellIndex(h);
53501             var newIndex = this.view.getCellIndex(n);
53502             var locked = cm.isLocked(newIndex);
53503             if(pt == "after"){
53504                 newIndex++;
53505             }
53506             if(oldIndex < newIndex){
53507                 newIndex--;
53508             }
53509             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
53510                 return false;
53511             }
53512             cm.setLocked(oldIndex, locked, true);
53513             cm.moveColumn(oldIndex, newIndex);
53514             this.grid.fireEvent("columnmove", oldIndex, newIndex);
53515             return true;
53516         }
53517         return false;
53518     }
53519 });
53520 /*
53521  * Based on:
53522  * Ext JS Library 1.1.1
53523  * Copyright(c) 2006-2007, Ext JS, LLC.
53524  *
53525  * Originally Released Under LGPL - original licence link has changed is not relivant.
53526  *
53527  * Fork - LGPL
53528  * <script type="text/javascript">
53529  */
53530   
53531 /**
53532  * @class Roo.grid.GridView
53533  * @extends Roo.util.Observable
53534  *
53535  * @constructor
53536  * @param {Object} config
53537  */
53538 Roo.grid.GridView = function(config){
53539     Roo.grid.GridView.superclass.constructor.call(this);
53540     this.el = null;
53541
53542     Roo.apply(this, config);
53543 };
53544
53545 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
53546
53547     unselectable :  'unselectable="on"',
53548     unselectableCls :  'x-unselectable',
53549     
53550     
53551     rowClass : "x-grid-row",
53552
53553     cellClass : "x-grid-col",
53554
53555     tdClass : "x-grid-td",
53556
53557     hdClass : "x-grid-hd",
53558
53559     splitClass : "x-grid-split",
53560
53561     sortClasses : ["sort-asc", "sort-desc"],
53562
53563     enableMoveAnim : false,
53564
53565     hlColor: "C3DAF9",
53566
53567     dh : Roo.DomHelper,
53568
53569     fly : Roo.Element.fly,
53570
53571     css : Roo.util.CSS,
53572
53573     borderWidth: 1,
53574
53575     splitOffset: 3,
53576
53577     scrollIncrement : 22,
53578
53579     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
53580
53581     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
53582
53583     bind : function(ds, cm){
53584         if(this.ds){
53585             this.ds.un("load", this.onLoad, this);
53586             this.ds.un("datachanged", this.onDataChange, this);
53587             this.ds.un("add", this.onAdd, this);
53588             this.ds.un("remove", this.onRemove, this);
53589             this.ds.un("update", this.onUpdate, this);
53590             this.ds.un("clear", this.onClear, this);
53591         }
53592         if(ds){
53593             ds.on("load", this.onLoad, this);
53594             ds.on("datachanged", this.onDataChange, this);
53595             ds.on("add", this.onAdd, this);
53596             ds.on("remove", this.onRemove, this);
53597             ds.on("update", this.onUpdate, this);
53598             ds.on("clear", this.onClear, this);
53599         }
53600         this.ds = ds;
53601
53602         if(this.cm){
53603             this.cm.un("widthchange", this.onColWidthChange, this);
53604             this.cm.un("headerchange", this.onHeaderChange, this);
53605             this.cm.un("hiddenchange", this.onHiddenChange, this);
53606             this.cm.un("columnmoved", this.onColumnMove, this);
53607             this.cm.un("columnlockchange", this.onColumnLock, this);
53608         }
53609         if(cm){
53610             this.generateRules(cm);
53611             cm.on("widthchange", this.onColWidthChange, this);
53612             cm.on("headerchange", this.onHeaderChange, this);
53613             cm.on("hiddenchange", this.onHiddenChange, this);
53614             cm.on("columnmoved", this.onColumnMove, this);
53615             cm.on("columnlockchange", this.onColumnLock, this);
53616         }
53617         this.cm = cm;
53618     },
53619
53620     init: function(grid){
53621         Roo.grid.GridView.superclass.init.call(this, grid);
53622
53623         this.bind(grid.dataSource, grid.colModel);
53624
53625         grid.on("headerclick", this.handleHeaderClick, this);
53626
53627         if(grid.trackMouseOver){
53628             grid.on("mouseover", this.onRowOver, this);
53629             grid.on("mouseout", this.onRowOut, this);
53630         }
53631         grid.cancelTextSelection = function(){};
53632         this.gridId = grid.id;
53633
53634         var tpls = this.templates || {};
53635
53636         if(!tpls.master){
53637             tpls.master = new Roo.Template(
53638                '<div class="x-grid" hidefocus="true">',
53639                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
53640                   '<div class="x-grid-topbar"></div>',
53641                   '<div class="x-grid-scroller"><div></div></div>',
53642                   '<div class="x-grid-locked">',
53643                       '<div class="x-grid-header">{lockedHeader}</div>',
53644                       '<div class="x-grid-body">{lockedBody}</div>',
53645                   "</div>",
53646                   '<div class="x-grid-viewport">',
53647                       '<div class="x-grid-header">{header}</div>',
53648                       '<div class="x-grid-body">{body}</div>',
53649                   "</div>",
53650                   '<div class="x-grid-bottombar"></div>',
53651                  
53652                   '<div class="x-grid-resize-proxy">&#160;</div>',
53653                "</div>"
53654             );
53655             tpls.master.disableformats = true;
53656         }
53657
53658         if(!tpls.header){
53659             tpls.header = new Roo.Template(
53660                '<table border="0" cellspacing="0" cellpadding="0">',
53661                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
53662                "</table>{splits}"
53663             );
53664             tpls.header.disableformats = true;
53665         }
53666         tpls.header.compile();
53667
53668         if(!tpls.hcell){
53669             tpls.hcell = new Roo.Template(
53670                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
53671                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
53672                 "</div></td>"
53673              );
53674              tpls.hcell.disableFormats = true;
53675         }
53676         tpls.hcell.compile();
53677
53678         if(!tpls.hsplit){
53679             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
53680                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
53681             tpls.hsplit.disableFormats = true;
53682         }
53683         tpls.hsplit.compile();
53684
53685         if(!tpls.body){
53686             tpls.body = new Roo.Template(
53687                '<table border="0" cellspacing="0" cellpadding="0">',
53688                "<tbody>{rows}</tbody>",
53689                "</table>"
53690             );
53691             tpls.body.disableFormats = true;
53692         }
53693         tpls.body.compile();
53694
53695         if(!tpls.row){
53696             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
53697             tpls.row.disableFormats = true;
53698         }
53699         tpls.row.compile();
53700
53701         if(!tpls.cell){
53702             tpls.cell = new Roo.Template(
53703                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
53704                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
53705                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
53706                 "</td>"
53707             );
53708             tpls.cell.disableFormats = true;
53709         }
53710         tpls.cell.compile();
53711
53712         this.templates = tpls;
53713     },
53714
53715     // remap these for backwards compat
53716     onColWidthChange : function(){
53717         this.updateColumns.apply(this, arguments);
53718     },
53719     onHeaderChange : function(){
53720         this.updateHeaders.apply(this, arguments);
53721     }, 
53722     onHiddenChange : function(){
53723         this.handleHiddenChange.apply(this, arguments);
53724     },
53725     onColumnMove : function(){
53726         this.handleColumnMove.apply(this, arguments);
53727     },
53728     onColumnLock : function(){
53729         this.handleLockChange.apply(this, arguments);
53730     },
53731
53732     onDataChange : function(){
53733         this.refresh();
53734         this.updateHeaderSortState();
53735     },
53736
53737     onClear : function(){
53738         this.refresh();
53739     },
53740
53741     onUpdate : function(ds, record){
53742         this.refreshRow(record);
53743     },
53744
53745     refreshRow : function(record){
53746         var ds = this.ds, index;
53747         if(typeof record == 'number'){
53748             index = record;
53749             record = ds.getAt(index);
53750         }else{
53751             index = ds.indexOf(record);
53752         }
53753         this.insertRows(ds, index, index, true);
53754         this.onRemove(ds, record, index+1, true);
53755         this.syncRowHeights(index, index);
53756         this.layout();
53757         this.fireEvent("rowupdated", this, index, record);
53758     },
53759
53760     onAdd : function(ds, records, index){
53761         this.insertRows(ds, index, index + (records.length-1));
53762     },
53763
53764     onRemove : function(ds, record, index, isUpdate){
53765         if(isUpdate !== true){
53766             this.fireEvent("beforerowremoved", this, index, record);
53767         }
53768         var bt = this.getBodyTable(), lt = this.getLockedTable();
53769         if(bt.rows[index]){
53770             bt.firstChild.removeChild(bt.rows[index]);
53771         }
53772         if(lt.rows[index]){
53773             lt.firstChild.removeChild(lt.rows[index]);
53774         }
53775         if(isUpdate !== true){
53776             this.stripeRows(index);
53777             this.syncRowHeights(index, index);
53778             this.layout();
53779             this.fireEvent("rowremoved", this, index, record);
53780         }
53781     },
53782
53783     onLoad : function(){
53784         this.scrollToTop();
53785     },
53786
53787     /**
53788      * Scrolls the grid to the top
53789      */
53790     scrollToTop : function(){
53791         if(this.scroller){
53792             this.scroller.dom.scrollTop = 0;
53793             this.syncScroll();
53794         }
53795     },
53796
53797     /**
53798      * Gets a panel in the header of the grid that can be used for toolbars etc.
53799      * After modifying the contents of this panel a call to grid.autoSize() may be
53800      * required to register any changes in size.
53801      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53802      * @return Roo.Element
53803      */
53804     getHeaderPanel : function(doShow){
53805         if(doShow){
53806             this.headerPanel.show();
53807         }
53808         return this.headerPanel;
53809     },
53810
53811     /**
53812      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53813      * After modifying the contents of this panel a call to grid.autoSize() may be
53814      * required to register any changes in size.
53815      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53816      * @return Roo.Element
53817      */
53818     getFooterPanel : function(doShow){
53819         if(doShow){
53820             this.footerPanel.show();
53821         }
53822         return this.footerPanel;
53823     },
53824
53825     initElements : function(){
53826         var E = Roo.Element;
53827         var el = this.grid.getGridEl().dom.firstChild;
53828         var cs = el.childNodes;
53829
53830         this.el = new E(el);
53831         
53832          this.focusEl = new E(el.firstChild);
53833         this.focusEl.swallowEvent("click", true);
53834         
53835         this.headerPanel = new E(cs[1]);
53836         this.headerPanel.enableDisplayMode("block");
53837
53838         this.scroller = new E(cs[2]);
53839         this.scrollSizer = new E(this.scroller.dom.firstChild);
53840
53841         this.lockedWrap = new E(cs[3]);
53842         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53843         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53844
53845         this.mainWrap = new E(cs[4]);
53846         this.mainHd = new E(this.mainWrap.dom.firstChild);
53847         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53848
53849         this.footerPanel = new E(cs[5]);
53850         this.footerPanel.enableDisplayMode("block");
53851
53852         this.resizeProxy = new E(cs[6]);
53853
53854         this.headerSelector = String.format(
53855            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53856            this.lockedHd.id, this.mainHd.id
53857         );
53858
53859         this.splitterSelector = String.format(
53860            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53861            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53862         );
53863     },
53864     idToCssName : function(s)
53865     {
53866         return s.replace(/[^a-z0-9]+/ig, '-');
53867     },
53868
53869     getHeaderCell : function(index){
53870         return Roo.DomQuery.select(this.headerSelector)[index];
53871     },
53872
53873     getHeaderCellMeasure : function(index){
53874         return this.getHeaderCell(index).firstChild;
53875     },
53876
53877     getHeaderCellText : function(index){
53878         return this.getHeaderCell(index).firstChild.firstChild;
53879     },
53880
53881     getLockedTable : function(){
53882         return this.lockedBody.dom.firstChild;
53883     },
53884
53885     getBodyTable : function(){
53886         return this.mainBody.dom.firstChild;
53887     },
53888
53889     getLockedRow : function(index){
53890         return this.getLockedTable().rows[index];
53891     },
53892
53893     getRow : function(index){
53894         return this.getBodyTable().rows[index];
53895     },
53896
53897     getRowComposite : function(index){
53898         if(!this.rowEl){
53899             this.rowEl = new Roo.CompositeElementLite();
53900         }
53901         var els = [], lrow, mrow;
53902         if(lrow = this.getLockedRow(index)){
53903             els.push(lrow);
53904         }
53905         if(mrow = this.getRow(index)){
53906             els.push(mrow);
53907         }
53908         this.rowEl.elements = els;
53909         return this.rowEl;
53910     },
53911     /**
53912      * Gets the 'td' of the cell
53913      * 
53914      * @param {Integer} rowIndex row to select
53915      * @param {Integer} colIndex column to select
53916      * 
53917      * @return {Object} 
53918      */
53919     getCell : function(rowIndex, colIndex){
53920         var locked = this.cm.getLockedCount();
53921         var source;
53922         if(colIndex < locked){
53923             source = this.lockedBody.dom.firstChild;
53924         }else{
53925             source = this.mainBody.dom.firstChild;
53926             colIndex -= locked;
53927         }
53928         return source.rows[rowIndex].childNodes[colIndex];
53929     },
53930
53931     getCellText : function(rowIndex, colIndex){
53932         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53933     },
53934
53935     getCellBox : function(cell){
53936         var b = this.fly(cell).getBox();
53937         if(Roo.isOpera){ // opera fails to report the Y
53938             b.y = cell.offsetTop + this.mainBody.getY();
53939         }
53940         return b;
53941     },
53942
53943     getCellIndex : function(cell){
53944         var id = String(cell.className).match(this.cellRE);
53945         if(id){
53946             return parseInt(id[1], 10);
53947         }
53948         return 0;
53949     },
53950
53951     findHeaderIndex : function(n){
53952         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53953         return r ? this.getCellIndex(r) : false;
53954     },
53955
53956     findHeaderCell : function(n){
53957         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53958         return r ? r : false;
53959     },
53960
53961     findRowIndex : function(n){
53962         if(!n){
53963             return false;
53964         }
53965         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53966         return r ? r.rowIndex : false;
53967     },
53968
53969     findCellIndex : function(node){
53970         var stop = this.el.dom;
53971         while(node && node != stop){
53972             if(this.findRE.test(node.className)){
53973                 return this.getCellIndex(node);
53974             }
53975             node = node.parentNode;
53976         }
53977         return false;
53978     },
53979
53980     getColumnId : function(index){
53981         return this.cm.getColumnId(index);
53982     },
53983
53984     getSplitters : function()
53985     {
53986         if(this.splitterSelector){
53987            return Roo.DomQuery.select(this.splitterSelector);
53988         }else{
53989             return null;
53990       }
53991     },
53992
53993     getSplitter : function(index){
53994         return this.getSplitters()[index];
53995     },
53996
53997     onRowOver : function(e, t){
53998         var row;
53999         if((row = this.findRowIndex(t)) !== false){
54000             this.getRowComposite(row).addClass("x-grid-row-over");
54001         }
54002     },
54003
54004     onRowOut : function(e, t){
54005         var row;
54006         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
54007             this.getRowComposite(row).removeClass("x-grid-row-over");
54008         }
54009     },
54010
54011     renderHeaders : function(){
54012         var cm = this.cm;
54013         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
54014         var cb = [], lb = [], sb = [], lsb = [], p = {};
54015         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54016             p.cellId = "x-grid-hd-0-" + i;
54017             p.splitId = "x-grid-csplit-0-" + i;
54018             p.id = cm.getColumnId(i);
54019             p.value = cm.getColumnHeader(i) || "";
54020             p.title = cm.getColumnTooltip(i) || p.value.match(/\</)  ? '' :  p.value  || "";
54021             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
54022             if(!cm.isLocked(i)){
54023                 cb[cb.length] = ct.apply(p);
54024                 sb[sb.length] = st.apply(p);
54025             }else{
54026                 lb[lb.length] = ct.apply(p);
54027                 lsb[lsb.length] = st.apply(p);
54028             }
54029         }
54030         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
54031                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
54032     },
54033
54034     updateHeaders : function(){
54035         var html = this.renderHeaders();
54036         this.lockedHd.update(html[0]);
54037         this.mainHd.update(html[1]);
54038     },
54039
54040     /**
54041      * Focuses the specified row.
54042      * @param {Number} row The row index
54043      */
54044     focusRow : function(row)
54045     {
54046         //Roo.log('GridView.focusRow');
54047         var x = this.scroller.dom.scrollLeft;
54048         this.focusCell(row, 0, false);
54049         this.scroller.dom.scrollLeft = x;
54050     },
54051
54052     /**
54053      * Focuses the specified cell.
54054      * @param {Number} row The row index
54055      * @param {Number} col The column index
54056      * @param {Boolean} hscroll false to disable horizontal scrolling
54057      */
54058     focusCell : function(row, col, hscroll)
54059     {
54060         //Roo.log('GridView.focusCell');
54061         var el = this.ensureVisible(row, col, hscroll);
54062         this.focusEl.alignTo(el, "tl-tl");
54063         if(Roo.isGecko){
54064             this.focusEl.focus();
54065         }else{
54066             this.focusEl.focus.defer(1, this.focusEl);
54067         }
54068     },
54069
54070     /**
54071      * Scrolls the specified cell into view
54072      * @param {Number} row The row index
54073      * @param {Number} col The column index
54074      * @param {Boolean} hscroll false to disable horizontal scrolling
54075      */
54076     ensureVisible : function(row, col, hscroll)
54077     {
54078         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
54079         //return null; //disable for testing.
54080         if(typeof row != "number"){
54081             row = row.rowIndex;
54082         }
54083         if(row < 0 && row >= this.ds.getCount()){
54084             return  null;
54085         }
54086         col = (col !== undefined ? col : 0);
54087         var cm = this.grid.colModel;
54088         while(cm.isHidden(col)){
54089             col++;
54090         }
54091
54092         var el = this.getCell(row, col);
54093         if(!el){
54094             return null;
54095         }
54096         var c = this.scroller.dom;
54097
54098         var ctop = parseInt(el.offsetTop, 10);
54099         var cleft = parseInt(el.offsetLeft, 10);
54100         var cbot = ctop + el.offsetHeight;
54101         var cright = cleft + el.offsetWidth;
54102         
54103         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
54104         var stop = parseInt(c.scrollTop, 10);
54105         var sleft = parseInt(c.scrollLeft, 10);
54106         var sbot = stop + ch;
54107         var sright = sleft + c.clientWidth;
54108         /*
54109         Roo.log('GridView.ensureVisible:' +
54110                 ' ctop:' + ctop +
54111                 ' c.clientHeight:' + c.clientHeight +
54112                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
54113                 ' stop:' + stop +
54114                 ' cbot:' + cbot +
54115                 ' sbot:' + sbot +
54116                 ' ch:' + ch  
54117                 );
54118         */
54119         if(ctop < stop){
54120              c.scrollTop = ctop;
54121             //Roo.log("set scrolltop to ctop DISABLE?");
54122         }else if(cbot > sbot){
54123             //Roo.log("set scrolltop to cbot-ch");
54124             c.scrollTop = cbot-ch;
54125         }
54126         
54127         if(hscroll !== false){
54128             if(cleft < sleft){
54129                 c.scrollLeft = cleft;
54130             }else if(cright > sright){
54131                 c.scrollLeft = cright-c.clientWidth;
54132             }
54133         }
54134          
54135         return el;
54136     },
54137
54138     updateColumns : function(){
54139         this.grid.stopEditing();
54140         var cm = this.grid.colModel, colIds = this.getColumnIds();
54141         //var totalWidth = cm.getTotalWidth();
54142         var pos = 0;
54143         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54144             //if(cm.isHidden(i)) continue;
54145             var w = cm.getColumnWidth(i);
54146             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
54147             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
54148         }
54149         this.updateSplitters();
54150     },
54151
54152     generateRules : function(cm){
54153         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
54154         Roo.util.CSS.removeStyleSheet(rulesId);
54155         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54156             var cid = cm.getColumnId(i);
54157             var align = '';
54158             if(cm.config[i].align){
54159                 align = 'text-align:'+cm.config[i].align+';';
54160             }
54161             var hidden = '';
54162             if(cm.isHidden(i)){
54163                 hidden = 'display:none;';
54164             }
54165             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
54166             ruleBuf.push(
54167                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
54168                     this.hdSelector, cid, " {\n", align, width, "}\n",
54169                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
54170                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
54171         }
54172         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
54173     },
54174
54175     updateSplitters : function(){
54176         var cm = this.cm, s = this.getSplitters();
54177         if(s){ // splitters not created yet
54178             var pos = 0, locked = true;
54179             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54180                 if(cm.isHidden(i)) {
54181                     continue;
54182                 }
54183                 var w = cm.getColumnWidth(i); // make sure it's a number
54184                 if(!cm.isLocked(i) && locked){
54185                     pos = 0;
54186                     locked = false;
54187                 }
54188                 pos += w;
54189                 s[i].style.left = (pos-this.splitOffset) + "px";
54190             }
54191         }
54192     },
54193
54194     handleHiddenChange : function(colModel, colIndex, hidden){
54195         if(hidden){
54196             this.hideColumn(colIndex);
54197         }else{
54198             this.unhideColumn(colIndex);
54199         }
54200     },
54201
54202     hideColumn : function(colIndex){
54203         var cid = this.getColumnId(colIndex);
54204         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
54205         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
54206         if(Roo.isSafari){
54207             this.updateHeaders();
54208         }
54209         this.updateSplitters();
54210         this.layout();
54211     },
54212
54213     unhideColumn : function(colIndex){
54214         var cid = this.getColumnId(colIndex);
54215         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
54216         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
54217
54218         if(Roo.isSafari){
54219             this.updateHeaders();
54220         }
54221         this.updateSplitters();
54222         this.layout();
54223     },
54224
54225     insertRows : function(dm, firstRow, lastRow, isUpdate){
54226         if(firstRow == 0 && lastRow == dm.getCount()-1){
54227             this.refresh();
54228         }else{
54229             if(!isUpdate){
54230                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
54231             }
54232             var s = this.getScrollState();
54233             var markup = this.renderRows(firstRow, lastRow);
54234             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
54235             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
54236             this.restoreScroll(s);
54237             if(!isUpdate){
54238                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
54239                 this.syncRowHeights(firstRow, lastRow);
54240                 this.stripeRows(firstRow);
54241                 this.layout();
54242             }
54243         }
54244     },
54245
54246     bufferRows : function(markup, target, index){
54247         var before = null, trows = target.rows, tbody = target.tBodies[0];
54248         if(index < trows.length){
54249             before = trows[index];
54250         }
54251         var b = document.createElement("div");
54252         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
54253         var rows = b.firstChild.rows;
54254         for(var i = 0, len = rows.length; i < len; i++){
54255             if(before){
54256                 tbody.insertBefore(rows[0], before);
54257             }else{
54258                 tbody.appendChild(rows[0]);
54259             }
54260         }
54261         b.innerHTML = "";
54262         b = null;
54263     },
54264
54265     deleteRows : function(dm, firstRow, lastRow){
54266         if(dm.getRowCount()<1){
54267             this.fireEvent("beforerefresh", this);
54268             this.mainBody.update("");
54269             this.lockedBody.update("");
54270             this.fireEvent("refresh", this);
54271         }else{
54272             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
54273             var bt = this.getBodyTable();
54274             var tbody = bt.firstChild;
54275             var rows = bt.rows;
54276             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
54277                 tbody.removeChild(rows[firstRow]);
54278             }
54279             this.stripeRows(firstRow);
54280             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
54281         }
54282     },
54283
54284     updateRows : function(dataSource, firstRow, lastRow){
54285         var s = this.getScrollState();
54286         this.refresh();
54287         this.restoreScroll(s);
54288     },
54289
54290     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
54291         if(!noRefresh){
54292            this.refresh();
54293         }
54294         this.updateHeaderSortState();
54295     },
54296
54297     getScrollState : function(){
54298         
54299         var sb = this.scroller.dom;
54300         return {left: sb.scrollLeft, top: sb.scrollTop};
54301     },
54302
54303     stripeRows : function(startRow){
54304         if(!this.grid.stripeRows || this.ds.getCount() < 1){
54305             return;
54306         }
54307         startRow = startRow || 0;
54308         var rows = this.getBodyTable().rows;
54309         var lrows = this.getLockedTable().rows;
54310         var cls = ' x-grid-row-alt ';
54311         for(var i = startRow, len = rows.length; i < len; i++){
54312             var row = rows[i], lrow = lrows[i];
54313             var isAlt = ((i+1) % 2 == 0);
54314             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
54315             if(isAlt == hasAlt){
54316                 continue;
54317             }
54318             if(isAlt){
54319                 row.className += " x-grid-row-alt";
54320             }else{
54321                 row.className = row.className.replace("x-grid-row-alt", "");
54322             }
54323             if(lrow){
54324                 lrow.className = row.className;
54325             }
54326         }
54327     },
54328
54329     restoreScroll : function(state){
54330         //Roo.log('GridView.restoreScroll');
54331         var sb = this.scroller.dom;
54332         sb.scrollLeft = state.left;
54333         sb.scrollTop = state.top;
54334         this.syncScroll();
54335     },
54336
54337     syncScroll : function(){
54338         //Roo.log('GridView.syncScroll');
54339         var sb = this.scroller.dom;
54340         var sh = this.mainHd.dom;
54341         var bs = this.mainBody.dom;
54342         var lv = this.lockedBody.dom;
54343         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
54344         lv.scrollTop = bs.scrollTop = sb.scrollTop;
54345     },
54346
54347     handleScroll : function(e){
54348         this.syncScroll();
54349         var sb = this.scroller.dom;
54350         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
54351         e.stopEvent();
54352     },
54353
54354     handleWheel : function(e){
54355         var d = e.getWheelDelta();
54356         this.scroller.dom.scrollTop -= d*22;
54357         // set this here to prevent jumpy scrolling on large tables
54358         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
54359         e.stopEvent();
54360     },
54361
54362     renderRows : function(startRow, endRow){
54363         // pull in all the crap needed to render rows
54364         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
54365         var colCount = cm.getColumnCount();
54366
54367         if(ds.getCount() < 1){
54368             return ["", ""];
54369         }
54370
54371         // build a map for all the columns
54372         var cs = [];
54373         for(var i = 0; i < colCount; i++){
54374             var name = cm.getDataIndex(i);
54375             cs[i] = {
54376                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
54377                 renderer : cm.getRenderer(i),
54378                 id : cm.getColumnId(i),
54379                 locked : cm.isLocked(i),
54380                 has_editor : cm.isCellEditable(i)
54381             };
54382         }
54383
54384         startRow = startRow || 0;
54385         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
54386
54387         // records to render
54388         var rs = ds.getRange(startRow, endRow);
54389
54390         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
54391     },
54392
54393     // As much as I hate to duplicate code, this was branched because FireFox really hates
54394     // [].join("") on strings. The performance difference was substantial enough to
54395     // branch this function
54396     doRender : Roo.isGecko ?
54397             function(cs, rs, ds, startRow, colCount, stripe){
54398                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54399                 // buffers
54400                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54401                 
54402                 var hasListener = this.grid.hasListener('rowclass');
54403                 var rowcfg = {};
54404                 for(var j = 0, len = rs.length; j < len; j++){
54405                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
54406                     for(var i = 0; i < colCount; i++){
54407                         c = cs[i];
54408                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54409                         p.id = c.id;
54410                         p.css = p.attr = "";
54411                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54412                         if(p.value == undefined || p.value === "") {
54413                             p.value = "&#160;";
54414                         }
54415                         if(c.has_editor){
54416                             p.css += ' x-grid-editable-cell';
54417                         }
54418                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
54419                             p.css +=  ' x-grid-dirty-cell';
54420                         }
54421                         var markup = ct.apply(p);
54422                         if(!c.locked){
54423                             cb+= markup;
54424                         }else{
54425                             lcb+= markup;
54426                         }
54427                     }
54428                     var alt = [];
54429                     if(stripe && ((rowIndex+1) % 2 == 0)){
54430                         alt.push("x-grid-row-alt")
54431                     }
54432                     if(r.dirty){
54433                         alt.push(  " x-grid-dirty-row");
54434                     }
54435                     rp.cells = lcb;
54436                     if(this.getRowClass){
54437                         alt.push(this.getRowClass(r, rowIndex));
54438                     }
54439                     if (hasListener) {
54440                         rowcfg = {
54441                              
54442                             record: r,
54443                             rowIndex : rowIndex,
54444                             rowClass : ''
54445                         };
54446                         this.grid.fireEvent('rowclass', this, rowcfg);
54447                         alt.push(rowcfg.rowClass);
54448                     }
54449                     rp.alt = alt.join(" ");
54450                     lbuf+= rt.apply(rp);
54451                     rp.cells = cb;
54452                     buf+=  rt.apply(rp);
54453                 }
54454                 return [lbuf, buf];
54455             } :
54456             function(cs, rs, ds, startRow, colCount, stripe){
54457                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54458                 // buffers
54459                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54460                 var hasListener = this.grid.hasListener('rowclass');
54461  
54462                 var rowcfg = {};
54463                 for(var j = 0, len = rs.length; j < len; j++){
54464                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
54465                     for(var i = 0; i < colCount; i++){
54466                         c = cs[i];
54467                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54468                         p.id = c.id;
54469                         p.css = p.attr = "";
54470                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54471                         if(p.value == undefined || p.value === "") {
54472                             p.value = "&#160;";
54473                         }
54474                         //Roo.log(c);
54475                          if(c.has_editor){
54476                             p.css += ' x-grid-editable-cell';
54477                         }
54478                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54479                             p.css += ' x-grid-dirty-cell' 
54480                         }
54481                         
54482                         var markup = ct.apply(p);
54483                         if(!c.locked){
54484                             cb[cb.length] = markup;
54485                         }else{
54486                             lcb[lcb.length] = markup;
54487                         }
54488                     }
54489                     var alt = [];
54490                     if(stripe && ((rowIndex+1) % 2 == 0)){
54491                         alt.push( "x-grid-row-alt");
54492                     }
54493                     if(r.dirty){
54494                         alt.push(" x-grid-dirty-row");
54495                     }
54496                     rp.cells = lcb;
54497                     if(this.getRowClass){
54498                         alt.push( this.getRowClass(r, rowIndex));
54499                     }
54500                     if (hasListener) {
54501                         rowcfg = {
54502                              
54503                             record: r,
54504                             rowIndex : rowIndex,
54505                             rowClass : ''
54506                         };
54507                         this.grid.fireEvent('rowclass', this, rowcfg);
54508                         alt.push(rowcfg.rowClass);
54509                     }
54510                     
54511                     rp.alt = alt.join(" ");
54512                     rp.cells = lcb.join("");
54513                     lbuf[lbuf.length] = rt.apply(rp);
54514                     rp.cells = cb.join("");
54515                     buf[buf.length] =  rt.apply(rp);
54516                 }
54517                 return [lbuf.join(""), buf.join("")];
54518             },
54519
54520     renderBody : function(){
54521         var markup = this.renderRows();
54522         var bt = this.templates.body;
54523         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
54524     },
54525
54526     /**
54527      * Refreshes the grid
54528      * @param {Boolean} headersToo
54529      */
54530     refresh : function(headersToo){
54531         this.fireEvent("beforerefresh", this);
54532         this.grid.stopEditing();
54533         var result = this.renderBody();
54534         this.lockedBody.update(result[0]);
54535         this.mainBody.update(result[1]);
54536         if(headersToo === true){
54537             this.updateHeaders();
54538             this.updateColumns();
54539             this.updateSplitters();
54540             this.updateHeaderSortState();
54541         }
54542         this.syncRowHeights();
54543         this.layout();
54544         this.fireEvent("refresh", this);
54545     },
54546
54547     handleColumnMove : function(cm, oldIndex, newIndex){
54548         this.indexMap = null;
54549         var s = this.getScrollState();
54550         this.refresh(true);
54551         this.restoreScroll(s);
54552         this.afterMove(newIndex);
54553     },
54554
54555     afterMove : function(colIndex){
54556         if(this.enableMoveAnim && Roo.enableFx){
54557             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
54558         }
54559         // if multisort - fix sortOrder, and reload..
54560         if (this.grid.dataSource.multiSort) {
54561             // the we can call sort again..
54562             var dm = this.grid.dataSource;
54563             var cm = this.grid.colModel;
54564             var so = [];
54565             for(var i = 0; i < cm.config.length; i++ ) {
54566                 
54567                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
54568                     continue; // dont' bother, it's not in sort list or being set.
54569                 }
54570                 
54571                 so.push(cm.config[i].dataIndex);
54572             };
54573             dm.sortOrder = so;
54574             dm.load(dm.lastOptions);
54575             
54576             
54577         }
54578         
54579     },
54580
54581     updateCell : function(dm, rowIndex, dataIndex){
54582         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
54583         if(typeof colIndex == "undefined"){ // not present in grid
54584             return;
54585         }
54586         var cm = this.grid.colModel;
54587         var cell = this.getCell(rowIndex, colIndex);
54588         var cellText = this.getCellText(rowIndex, colIndex);
54589
54590         var p = {
54591             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
54592             id : cm.getColumnId(colIndex),
54593             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
54594         };
54595         var renderer = cm.getRenderer(colIndex);
54596         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
54597         if(typeof val == "undefined" || val === "") {
54598             val = "&#160;";
54599         }
54600         cellText.innerHTML = val;
54601         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
54602         this.syncRowHeights(rowIndex, rowIndex);
54603     },
54604
54605     calcColumnWidth : function(colIndex, maxRowsToMeasure){
54606         var maxWidth = 0;
54607         if(this.grid.autoSizeHeaders){
54608             var h = this.getHeaderCellMeasure(colIndex);
54609             maxWidth = Math.max(maxWidth, h.scrollWidth);
54610         }
54611         var tb, index;
54612         if(this.cm.isLocked(colIndex)){
54613             tb = this.getLockedTable();
54614             index = colIndex;
54615         }else{
54616             tb = this.getBodyTable();
54617             index = colIndex - this.cm.getLockedCount();
54618         }
54619         if(tb && tb.rows){
54620             var rows = tb.rows;
54621             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
54622             for(var i = 0; i < stopIndex; i++){
54623                 var cell = rows[i].childNodes[index].firstChild;
54624                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
54625             }
54626         }
54627         return maxWidth + /*margin for error in IE*/ 5;
54628     },
54629     /**
54630      * Autofit a column to its content.
54631      * @param {Number} colIndex
54632      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
54633      */
54634      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
54635          if(this.cm.isHidden(colIndex)){
54636              return; // can't calc a hidden column
54637          }
54638         if(forceMinSize){
54639             var cid = this.cm.getColumnId(colIndex);
54640             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
54641            if(this.grid.autoSizeHeaders){
54642                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
54643            }
54644         }
54645         var newWidth = this.calcColumnWidth(colIndex);
54646         this.cm.setColumnWidth(colIndex,
54647             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
54648         if(!suppressEvent){
54649             this.grid.fireEvent("columnresize", colIndex, newWidth);
54650         }
54651     },
54652
54653     /**
54654      * Autofits all columns to their content and then expands to fit any extra space in the grid
54655      */
54656      autoSizeColumns : function(){
54657         var cm = this.grid.colModel;
54658         var colCount = cm.getColumnCount();
54659         for(var i = 0; i < colCount; i++){
54660             this.autoSizeColumn(i, true, true);
54661         }
54662         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
54663             this.fitColumns();
54664         }else{
54665             this.updateColumns();
54666             this.layout();
54667         }
54668     },
54669
54670     /**
54671      * Autofits all columns to the grid's width proportionate with their current size
54672      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
54673      */
54674     fitColumns : function(reserveScrollSpace){
54675         var cm = this.grid.colModel;
54676         var colCount = cm.getColumnCount();
54677         var cols = [];
54678         var width = 0;
54679         var i, w;
54680         for (i = 0; i < colCount; i++){
54681             if(!cm.isHidden(i) && !cm.isFixed(i)){
54682                 w = cm.getColumnWidth(i);
54683                 cols.push(i);
54684                 cols.push(w);
54685                 width += w;
54686             }
54687         }
54688         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
54689         if(reserveScrollSpace){
54690             avail -= 17;
54691         }
54692         var frac = (avail - cm.getTotalWidth())/width;
54693         while (cols.length){
54694             w = cols.pop();
54695             i = cols.pop();
54696             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
54697         }
54698         this.updateColumns();
54699         this.layout();
54700     },
54701
54702     onRowSelect : function(rowIndex){
54703         var row = this.getRowComposite(rowIndex);
54704         row.addClass("x-grid-row-selected");
54705     },
54706
54707     onRowDeselect : function(rowIndex){
54708         var row = this.getRowComposite(rowIndex);
54709         row.removeClass("x-grid-row-selected");
54710     },
54711
54712     onCellSelect : function(row, col){
54713         var cell = this.getCell(row, col);
54714         if(cell){
54715             Roo.fly(cell).addClass("x-grid-cell-selected");
54716         }
54717     },
54718
54719     onCellDeselect : function(row, col){
54720         var cell = this.getCell(row, col);
54721         if(cell){
54722             Roo.fly(cell).removeClass("x-grid-cell-selected");
54723         }
54724     },
54725
54726     updateHeaderSortState : function(){
54727         
54728         // sort state can be single { field: xxx, direction : yyy}
54729         // or   { xxx=>ASC , yyy : DESC ..... }
54730         
54731         var mstate = {};
54732         if (!this.ds.multiSort) { 
54733             var state = this.ds.getSortState();
54734             if(!state){
54735                 return;
54736             }
54737             mstate[state.field] = state.direction;
54738             // FIXME... - this is not used here.. but might be elsewhere..
54739             this.sortState = state;
54740             
54741         } else {
54742             mstate = this.ds.sortToggle;
54743         }
54744         //remove existing sort classes..
54745         
54746         var sc = this.sortClasses;
54747         var hds = this.el.select(this.headerSelector).removeClass(sc);
54748         
54749         for(var f in mstate) {
54750         
54751             var sortColumn = this.cm.findColumnIndex(f);
54752             
54753             if(sortColumn != -1){
54754                 var sortDir = mstate[f];        
54755                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
54756             }
54757         }
54758         
54759          
54760         
54761     },
54762
54763
54764     handleHeaderClick : function(g, index,e){
54765         
54766         Roo.log("header click");
54767         
54768         if (Roo.isTouch) {
54769             // touch events on header are handled by context
54770             this.handleHdCtx(g,index,e);
54771             return;
54772         }
54773         
54774         
54775         if(this.headersDisabled){
54776             return;
54777         }
54778         var dm = g.dataSource, cm = g.colModel;
54779         if(!cm.isSortable(index)){
54780             return;
54781         }
54782         g.stopEditing();
54783         
54784         if (dm.multiSort) {
54785             // update the sortOrder
54786             var so = [];
54787             for(var i = 0; i < cm.config.length; i++ ) {
54788                 
54789                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
54790                     continue; // dont' bother, it's not in sort list or being set.
54791                 }
54792                 
54793                 so.push(cm.config[i].dataIndex);
54794             };
54795             dm.sortOrder = so;
54796         }
54797         
54798         
54799         dm.sort(cm.getDataIndex(index));
54800     },
54801
54802
54803     destroy : function(){
54804         if(this.colMenu){
54805             this.colMenu.removeAll();
54806             Roo.menu.MenuMgr.unregister(this.colMenu);
54807             this.colMenu.getEl().remove();
54808             delete this.colMenu;
54809         }
54810         if(this.hmenu){
54811             this.hmenu.removeAll();
54812             Roo.menu.MenuMgr.unregister(this.hmenu);
54813             this.hmenu.getEl().remove();
54814             delete this.hmenu;
54815         }
54816         if(this.grid.enableColumnMove){
54817             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54818             if(dds){
54819                 for(var dd in dds){
54820                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54821                         var elid = dds[dd].dragElId;
54822                         dds[dd].unreg();
54823                         Roo.get(elid).remove();
54824                     } else if(dds[dd].config.isTarget){
54825                         dds[dd].proxyTop.remove();
54826                         dds[dd].proxyBottom.remove();
54827                         dds[dd].unreg();
54828                     }
54829                     if(Roo.dd.DDM.locationCache[dd]){
54830                         delete Roo.dd.DDM.locationCache[dd];
54831                     }
54832                 }
54833                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54834             }
54835         }
54836         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54837         this.bind(null, null);
54838         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54839     },
54840
54841     handleLockChange : function(){
54842         this.refresh(true);
54843     },
54844
54845     onDenyColumnLock : function(){
54846
54847     },
54848
54849     onDenyColumnHide : function(){
54850
54851     },
54852
54853     handleHdMenuClick : function(item){
54854         var index = this.hdCtxIndex;
54855         var cm = this.cm, ds = this.ds;
54856         switch(item.id){
54857             case "asc":
54858                 ds.sort(cm.getDataIndex(index), "ASC");
54859                 break;
54860             case "desc":
54861                 ds.sort(cm.getDataIndex(index), "DESC");
54862                 break;
54863             case "lock":
54864                 var lc = cm.getLockedCount();
54865                 if(cm.getColumnCount(true) <= lc+1){
54866                     this.onDenyColumnLock();
54867                     return;
54868                 }
54869                 if(lc != index){
54870                     cm.setLocked(index, true, true);
54871                     cm.moveColumn(index, lc);
54872                     this.grid.fireEvent("columnmove", index, lc);
54873                 }else{
54874                     cm.setLocked(index, true);
54875                 }
54876             break;
54877             case "unlock":
54878                 var lc = cm.getLockedCount();
54879                 if((lc-1) != index){
54880                     cm.setLocked(index, false, true);
54881                     cm.moveColumn(index, lc-1);
54882                     this.grid.fireEvent("columnmove", index, lc-1);
54883                 }else{
54884                     cm.setLocked(index, false);
54885                 }
54886             break;
54887             case 'wider': // used to expand cols on touch..
54888             case 'narrow':
54889                 var cw = cm.getColumnWidth(index);
54890                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54891                 cw = Math.max(0, cw);
54892                 cw = Math.min(cw,4000);
54893                 cm.setColumnWidth(index, cw);
54894                 break;
54895                 
54896             default:
54897                 index = cm.getIndexById(item.id.substr(4));
54898                 if(index != -1){
54899                     if(item.checked && cm.getColumnCount(true) <= 1){
54900                         this.onDenyColumnHide();
54901                         return false;
54902                     }
54903                     cm.setHidden(index, item.checked);
54904                 }
54905         }
54906         return true;
54907     },
54908
54909     beforeColMenuShow : function(){
54910         var cm = this.cm,  colCount = cm.getColumnCount();
54911         this.colMenu.removeAll();
54912         for(var i = 0; i < colCount; i++){
54913             this.colMenu.add(new Roo.menu.CheckItem({
54914                 id: "col-"+cm.getColumnId(i),
54915                 text: cm.getColumnHeader(i),
54916                 checked: !cm.isHidden(i),
54917                 hideOnClick:false
54918             }));
54919         }
54920     },
54921
54922     handleHdCtx : function(g, index, e){
54923         e.stopEvent();
54924         var hd = this.getHeaderCell(index);
54925         this.hdCtxIndex = index;
54926         var ms = this.hmenu.items, cm = this.cm;
54927         ms.get("asc").setDisabled(!cm.isSortable(index));
54928         ms.get("desc").setDisabled(!cm.isSortable(index));
54929         if(this.grid.enableColLock !== false){
54930             ms.get("lock").setDisabled(cm.isLocked(index));
54931             ms.get("unlock").setDisabled(!cm.isLocked(index));
54932         }
54933         this.hmenu.show(hd, "tl-bl");
54934     },
54935
54936     handleHdOver : function(e){
54937         var hd = this.findHeaderCell(e.getTarget());
54938         if(hd && !this.headersDisabled){
54939             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54940                this.fly(hd).addClass("x-grid-hd-over");
54941             }
54942         }
54943     },
54944
54945     handleHdOut : function(e){
54946         var hd = this.findHeaderCell(e.getTarget());
54947         if(hd){
54948             this.fly(hd).removeClass("x-grid-hd-over");
54949         }
54950     },
54951
54952     handleSplitDblClick : function(e, t){
54953         var i = this.getCellIndex(t);
54954         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54955             this.autoSizeColumn(i, true);
54956             this.layout();
54957         }
54958     },
54959
54960     render : function(){
54961
54962         var cm = this.cm;
54963         var colCount = cm.getColumnCount();
54964
54965         if(this.grid.monitorWindowResize === true){
54966             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54967         }
54968         var header = this.renderHeaders();
54969         var body = this.templates.body.apply({rows:""});
54970         var html = this.templates.master.apply({
54971             lockedBody: body,
54972             body: body,
54973             lockedHeader: header[0],
54974             header: header[1]
54975         });
54976
54977         //this.updateColumns();
54978
54979         this.grid.getGridEl().dom.innerHTML = html;
54980
54981         this.initElements();
54982         
54983         // a kludge to fix the random scolling effect in webkit
54984         this.el.on("scroll", function() {
54985             this.el.dom.scrollTop=0; // hopefully not recursive..
54986         },this);
54987
54988         this.scroller.on("scroll", this.handleScroll, this);
54989         this.lockedBody.on("mousewheel", this.handleWheel, this);
54990         this.mainBody.on("mousewheel", this.handleWheel, this);
54991
54992         this.mainHd.on("mouseover", this.handleHdOver, this);
54993         this.mainHd.on("mouseout", this.handleHdOut, this);
54994         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54995                 {delegate: "."+this.splitClass});
54996
54997         this.lockedHd.on("mouseover", this.handleHdOver, this);
54998         this.lockedHd.on("mouseout", this.handleHdOut, this);
54999         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
55000                 {delegate: "."+this.splitClass});
55001
55002         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
55003             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
55004         }
55005
55006         this.updateSplitters();
55007
55008         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
55009             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
55010             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
55011         }
55012
55013         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
55014             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
55015             this.hmenu.add(
55016                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
55017                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
55018             );
55019             if(this.grid.enableColLock !== false){
55020                 this.hmenu.add('-',
55021                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
55022                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
55023                 );
55024             }
55025             if (Roo.isTouch) {
55026                  this.hmenu.add('-',
55027                     {id:"wider", text: this.columnsWiderText},
55028                     {id:"narrow", text: this.columnsNarrowText }
55029                 );
55030                 
55031                  
55032             }
55033             
55034             if(this.grid.enableColumnHide !== false){
55035
55036                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
55037                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
55038                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
55039
55040                 this.hmenu.add('-',
55041                     {id:"columns", text: this.columnsText, menu: this.colMenu}
55042                 );
55043             }
55044             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
55045
55046             this.grid.on("headercontextmenu", this.handleHdCtx, this);
55047         }
55048
55049         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
55050             this.dd = new Roo.grid.GridDragZone(this.grid, {
55051                 ddGroup : this.grid.ddGroup || 'GridDD'
55052             });
55053             
55054         }
55055
55056         /*
55057         for(var i = 0; i < colCount; i++){
55058             if(cm.isHidden(i)){
55059                 this.hideColumn(i);
55060             }
55061             if(cm.config[i].align){
55062                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
55063                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
55064             }
55065         }*/
55066         
55067         this.updateHeaderSortState();
55068
55069         this.beforeInitialResize();
55070         this.layout(true);
55071
55072         // two part rendering gives faster view to the user
55073         this.renderPhase2.defer(1, this);
55074     },
55075
55076     renderPhase2 : function(){
55077         // render the rows now
55078         this.refresh();
55079         if(this.grid.autoSizeColumns){
55080             this.autoSizeColumns();
55081         }
55082     },
55083
55084     beforeInitialResize : function(){
55085
55086     },
55087
55088     onColumnSplitterMoved : function(i, w){
55089         this.userResized = true;
55090         var cm = this.grid.colModel;
55091         cm.setColumnWidth(i, w, true);
55092         var cid = cm.getColumnId(i);
55093         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
55094         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
55095         this.updateSplitters();
55096         this.layout();
55097         this.grid.fireEvent("columnresize", i, w);
55098     },
55099
55100     syncRowHeights : function(startIndex, endIndex){
55101         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
55102             startIndex = startIndex || 0;
55103             var mrows = this.getBodyTable().rows;
55104             var lrows = this.getLockedTable().rows;
55105             var len = mrows.length-1;
55106             endIndex = Math.min(endIndex || len, len);
55107             for(var i = startIndex; i <= endIndex; i++){
55108                 var m = mrows[i], l = lrows[i];
55109                 var h = Math.max(m.offsetHeight, l.offsetHeight);
55110                 m.style.height = l.style.height = h + "px";
55111             }
55112         }
55113     },
55114
55115     layout : function(initialRender, is2ndPass){
55116         var g = this.grid;
55117         var auto = g.autoHeight;
55118         var scrollOffset = 16;
55119         var c = g.getGridEl(), cm = this.cm,
55120                 expandCol = g.autoExpandColumn,
55121                 gv = this;
55122         //c.beginMeasure();
55123
55124         if(!c.dom.offsetWidth){ // display:none?
55125             if(initialRender){
55126                 this.lockedWrap.show();
55127                 this.mainWrap.show();
55128             }
55129             return;
55130         }
55131
55132         var hasLock = this.cm.isLocked(0);
55133
55134         var tbh = this.headerPanel.getHeight();
55135         var bbh = this.footerPanel.getHeight();
55136
55137         if(auto){
55138             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
55139             var newHeight = ch + c.getBorderWidth("tb");
55140             if(g.maxHeight){
55141                 newHeight = Math.min(g.maxHeight, newHeight);
55142             }
55143             c.setHeight(newHeight);
55144         }
55145
55146         if(g.autoWidth){
55147             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
55148         }
55149
55150         var s = this.scroller;
55151
55152         var csize = c.getSize(true);
55153
55154         this.el.setSize(csize.width, csize.height);
55155
55156         this.headerPanel.setWidth(csize.width);
55157         this.footerPanel.setWidth(csize.width);
55158
55159         var hdHeight = this.mainHd.getHeight();
55160         var vw = csize.width;
55161         var vh = csize.height - (tbh + bbh);
55162
55163         s.setSize(vw, vh);
55164
55165         var bt = this.getBodyTable();
55166         
55167         if(cm.getLockedCount() == cm.config.length){
55168             bt = this.getLockedTable();
55169         }
55170         
55171         var ltWidth = hasLock ?
55172                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
55173
55174         var scrollHeight = bt.offsetHeight;
55175         var scrollWidth = ltWidth + bt.offsetWidth;
55176         var vscroll = false, hscroll = false;
55177
55178         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
55179
55180         var lw = this.lockedWrap, mw = this.mainWrap;
55181         var lb = this.lockedBody, mb = this.mainBody;
55182
55183         setTimeout(function(){
55184             var t = s.dom.offsetTop;
55185             var w = s.dom.clientWidth,
55186                 h = s.dom.clientHeight;
55187
55188             lw.setTop(t);
55189             lw.setSize(ltWidth, h);
55190
55191             mw.setLeftTop(ltWidth, t);
55192             mw.setSize(w-ltWidth, h);
55193
55194             lb.setHeight(h-hdHeight);
55195             mb.setHeight(h-hdHeight);
55196
55197             if(is2ndPass !== true && !gv.userResized && expandCol){
55198                 // high speed resize without full column calculation
55199                 
55200                 var ci = cm.getIndexById(expandCol);
55201                 if (ci < 0) {
55202                     ci = cm.findColumnIndex(expandCol);
55203                 }
55204                 ci = Math.max(0, ci); // make sure it's got at least the first col.
55205                 var expandId = cm.getColumnId(ci);
55206                 var  tw = cm.getTotalWidth(false);
55207                 var currentWidth = cm.getColumnWidth(ci);
55208                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
55209                 if(currentWidth != cw){
55210                     cm.setColumnWidth(ci, cw, true);
55211                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
55212                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
55213                     gv.updateSplitters();
55214                     gv.layout(false, true);
55215                 }
55216             }
55217
55218             if(initialRender){
55219                 lw.show();
55220                 mw.show();
55221             }
55222             //c.endMeasure();
55223         }, 10);
55224     },
55225
55226     onWindowResize : function(){
55227         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
55228             return;
55229         }
55230         this.layout();
55231     },
55232
55233     appendFooter : function(parentEl){
55234         return null;
55235     },
55236
55237     sortAscText : "Sort Ascending",
55238     sortDescText : "Sort Descending",
55239     lockText : "Lock Column",
55240     unlockText : "Unlock Column",
55241     columnsText : "Columns",
55242  
55243     columnsWiderText : "Wider",
55244     columnsNarrowText : "Thinner"
55245 });
55246
55247
55248 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
55249     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
55250     this.proxy.el.addClass('x-grid3-col-dd');
55251 };
55252
55253 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
55254     handleMouseDown : function(e){
55255
55256     },
55257
55258     callHandleMouseDown : function(e){
55259         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
55260     }
55261 });
55262 /*
55263  * Based on:
55264  * Ext JS Library 1.1.1
55265  * Copyright(c) 2006-2007, Ext JS, LLC.
55266  *
55267  * Originally Released Under LGPL - original licence link has changed is not relivant.
55268  *
55269  * Fork - LGPL
55270  * <script type="text/javascript">
55271  */
55272  
55273 // private
55274 // This is a support class used internally by the Grid components
55275 Roo.grid.SplitDragZone = function(grid, hd, hd2){
55276     this.grid = grid;
55277     this.view = grid.getView();
55278     this.proxy = this.view.resizeProxy;
55279     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
55280         "gridSplitters" + this.grid.getGridEl().id, {
55281         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
55282     });
55283     this.setHandleElId(Roo.id(hd));
55284     this.setOuterHandleElId(Roo.id(hd2));
55285     this.scroll = false;
55286 };
55287 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
55288     fly: Roo.Element.fly,
55289
55290     b4StartDrag : function(x, y){
55291         this.view.headersDisabled = true;
55292         this.proxy.setHeight(this.view.mainWrap.getHeight());
55293         var w = this.cm.getColumnWidth(this.cellIndex);
55294         var minw = Math.max(w-this.grid.minColumnWidth, 0);
55295         this.resetConstraints();
55296         this.setXConstraint(minw, 1000);
55297         this.setYConstraint(0, 0);
55298         this.minX = x - minw;
55299         this.maxX = x + 1000;
55300         this.startPos = x;
55301         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
55302     },
55303
55304
55305     handleMouseDown : function(e){
55306         ev = Roo.EventObject.setEvent(e);
55307         var t = this.fly(ev.getTarget());
55308         if(t.hasClass("x-grid-split")){
55309             this.cellIndex = this.view.getCellIndex(t.dom);
55310             this.split = t.dom;
55311             this.cm = this.grid.colModel;
55312             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
55313                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
55314             }
55315         }
55316     },
55317
55318     endDrag : function(e){
55319         this.view.headersDisabled = false;
55320         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
55321         var diff = endX - this.startPos;
55322         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
55323     },
55324
55325     autoOffset : function(){
55326         this.setDelta(0,0);
55327     }
55328 });/*
55329  * Based on:
55330  * Ext JS Library 1.1.1
55331  * Copyright(c) 2006-2007, Ext JS, LLC.
55332  *
55333  * Originally Released Under LGPL - original licence link has changed is not relivant.
55334  *
55335  * Fork - LGPL
55336  * <script type="text/javascript">
55337  */
55338  
55339 // private
55340 // This is a support class used internally by the Grid components
55341 Roo.grid.GridDragZone = function(grid, config){
55342     this.view = grid.getView();
55343     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
55344     if(this.view.lockedBody){
55345         this.setHandleElId(Roo.id(this.view.mainBody.dom));
55346         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
55347     }
55348     this.scroll = false;
55349     this.grid = grid;
55350     this.ddel = document.createElement('div');
55351     this.ddel.className = 'x-grid-dd-wrap';
55352 };
55353
55354 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
55355     ddGroup : "GridDD",
55356
55357     getDragData : function(e){
55358         var t = Roo.lib.Event.getTarget(e);
55359         var rowIndex = this.view.findRowIndex(t);
55360         var sm = this.grid.selModel;
55361             
55362         //Roo.log(rowIndex);
55363         
55364         if (sm.getSelectedCell) {
55365             // cell selection..
55366             if (!sm.getSelectedCell()) {
55367                 return false;
55368             }
55369             if (rowIndex != sm.getSelectedCell()[0]) {
55370                 return false;
55371             }
55372         
55373         }
55374         
55375         if(rowIndex !== false){
55376             
55377             // if editorgrid.. 
55378             
55379             
55380             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
55381                
55382             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
55383               //  
55384             //}
55385             if (e.hasModifier()){
55386                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
55387             }
55388             
55389             Roo.log("getDragData");
55390             
55391             return {
55392                 grid: this.grid,
55393                 ddel: this.ddel,
55394                 rowIndex: rowIndex,
55395                 selections:sm.getSelections ? sm.getSelections() : (
55396                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
55397                 )
55398             };
55399         }
55400         return false;
55401     },
55402
55403     onInitDrag : function(e){
55404         var data = this.dragData;
55405         this.ddel.innerHTML = this.grid.getDragDropText();
55406         this.proxy.update(this.ddel);
55407         // fire start drag?
55408     },
55409
55410     afterRepair : function(){
55411         this.dragging = false;
55412     },
55413
55414     getRepairXY : function(e, data){
55415         return false;
55416     },
55417
55418     onEndDrag : function(data, e){
55419         // fire end drag?
55420     },
55421
55422     onValidDrop : function(dd, e, id){
55423         // fire drag drop?
55424         this.hideProxy();
55425     },
55426
55427     beforeInvalidDrop : function(e, id){
55428
55429     }
55430 });/*
55431  * Based on:
55432  * Ext JS Library 1.1.1
55433  * Copyright(c) 2006-2007, Ext JS, LLC.
55434  *
55435  * Originally Released Under LGPL - original licence link has changed is not relivant.
55436  *
55437  * Fork - LGPL
55438  * <script type="text/javascript">
55439  */
55440  
55441
55442 /**
55443  * @class Roo.grid.ColumnModel
55444  * @extends Roo.util.Observable
55445  * This is the default implementation of a ColumnModel used by the Grid. It defines
55446  * the columns in the grid.
55447  * <br>Usage:<br>
55448  <pre><code>
55449  var colModel = new Roo.grid.ColumnModel([
55450         {header: "Ticker", width: 60, sortable: true, locked: true},
55451         {header: "Company Name", width: 150, sortable: true},
55452         {header: "Market Cap.", width: 100, sortable: true},
55453         {header: "$ Sales", width: 100, sortable: true, renderer: money},
55454         {header: "Employees", width: 100, sortable: true, resizable: false}
55455  ]);
55456  </code></pre>
55457  * <p>
55458  
55459  * The config options listed for this class are options which may appear in each
55460  * individual column definition.
55461  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
55462  * @constructor
55463  * @param {Object} config An Array of column config objects. See this class's
55464  * config objects for details.
55465 */
55466 Roo.grid.ColumnModel = function(config){
55467         /**
55468      * The config passed into the constructor
55469      */
55470     this.config = config;
55471     this.lookup = {};
55472
55473     // if no id, create one
55474     // if the column does not have a dataIndex mapping,
55475     // map it to the order it is in the config
55476     for(var i = 0, len = config.length; i < len; i++){
55477         var c = config[i];
55478         if(typeof c.dataIndex == "undefined"){
55479             c.dataIndex = i;
55480         }
55481         if(typeof c.renderer == "string"){
55482             c.renderer = Roo.util.Format[c.renderer];
55483         }
55484         if(typeof c.id == "undefined"){
55485             c.id = Roo.id();
55486         }
55487         if(c.editor && c.editor.xtype){
55488             c.editor  = Roo.factory(c.editor, Roo.grid);
55489         }
55490         if(c.editor && c.editor.isFormField){
55491             c.editor = new Roo.grid.GridEditor(c.editor);
55492         }
55493         this.lookup[c.id] = c;
55494     }
55495
55496     /**
55497      * The width of columns which have no width specified (defaults to 100)
55498      * @type Number
55499      */
55500     this.defaultWidth = 100;
55501
55502     /**
55503      * Default sortable of columns which have no sortable specified (defaults to false)
55504      * @type Boolean
55505      */
55506     this.defaultSortable = false;
55507
55508     this.addEvents({
55509         /**
55510              * @event widthchange
55511              * Fires when the width of a column changes.
55512              * @param {ColumnModel} this
55513              * @param {Number} columnIndex The column index
55514              * @param {Number} newWidth The new width
55515              */
55516             "widthchange": true,
55517         /**
55518              * @event headerchange
55519              * Fires when the text of a header changes.
55520              * @param {ColumnModel} this
55521              * @param {Number} columnIndex The column index
55522              * @param {Number} newText The new header text
55523              */
55524             "headerchange": true,
55525         /**
55526              * @event hiddenchange
55527              * Fires when a column is hidden or "unhidden".
55528              * @param {ColumnModel} this
55529              * @param {Number} columnIndex The column index
55530              * @param {Boolean} hidden true if hidden, false otherwise
55531              */
55532             "hiddenchange": true,
55533             /**
55534          * @event columnmoved
55535          * Fires when a column is moved.
55536          * @param {ColumnModel} this
55537          * @param {Number} oldIndex
55538          * @param {Number} newIndex
55539          */
55540         "columnmoved" : true,
55541         /**
55542          * @event columlockchange
55543          * Fires when a column's locked state is changed
55544          * @param {ColumnModel} this
55545          * @param {Number} colIndex
55546          * @param {Boolean} locked true if locked
55547          */
55548         "columnlockchange" : true
55549     });
55550     Roo.grid.ColumnModel.superclass.constructor.call(this);
55551 };
55552 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
55553     /**
55554      * @cfg {String} header The header text to display in the Grid view.
55555      */
55556     /**
55557      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
55558      * {@link Roo.data.Record} definition from which to draw the column's value. If not
55559      * specified, the column's index is used as an index into the Record's data Array.
55560      */
55561     /**
55562      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
55563      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
55564      */
55565     /**
55566      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
55567      * Defaults to the value of the {@link #defaultSortable} property.
55568      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
55569      */
55570     /**
55571      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
55572      */
55573     /**
55574      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
55575      */
55576     /**
55577      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
55578      */
55579     /**
55580      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
55581      */
55582     /**
55583      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
55584      * given the cell's data value. See {@link #setRenderer}. If not specified, the
55585      * default renderer uses the raw data value. If an object is returned (bootstrap only)
55586      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
55587      */
55588        /**
55589      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
55590      */
55591     /**
55592      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
55593      */
55594     /**
55595      * @cfg {String} cursor (Optional)
55596      */
55597     /**
55598      * @cfg {String} tooltip (Optional)
55599      */
55600     /**
55601      * @cfg {Number} xs (Optional)
55602      */
55603     /**
55604      * @cfg {Number} sm (Optional)
55605      */
55606     /**
55607      * @cfg {Number} md (Optional)
55608      */
55609     /**
55610      * @cfg {Number} lg (Optional)
55611      */
55612     /**
55613      * Returns the id of the column at the specified index.
55614      * @param {Number} index The column index
55615      * @return {String} the id
55616      */
55617     getColumnId : function(index){
55618         return this.config[index].id;
55619     },
55620
55621     /**
55622      * Returns the column for a specified id.
55623      * @param {String} id The column id
55624      * @return {Object} the column
55625      */
55626     getColumnById : function(id){
55627         return this.lookup[id];
55628     },
55629
55630     
55631     /**
55632      * Returns the column for a specified dataIndex.
55633      * @param {String} dataIndex The column dataIndex
55634      * @return {Object|Boolean} the column or false if not found
55635      */
55636     getColumnByDataIndex: function(dataIndex){
55637         var index = this.findColumnIndex(dataIndex);
55638         return index > -1 ? this.config[index] : false;
55639     },
55640     
55641     /**
55642      * Returns the index for a specified column id.
55643      * @param {String} id The column id
55644      * @return {Number} the index, or -1 if not found
55645      */
55646     getIndexById : function(id){
55647         for(var i = 0, len = this.config.length; i < len; i++){
55648             if(this.config[i].id == id){
55649                 return i;
55650             }
55651         }
55652         return -1;
55653     },
55654     
55655     /**
55656      * Returns the index for a specified column dataIndex.
55657      * @param {String} dataIndex The column dataIndex
55658      * @return {Number} the index, or -1 if not found
55659      */
55660     
55661     findColumnIndex : function(dataIndex){
55662         for(var i = 0, len = this.config.length; i < len; i++){
55663             if(this.config[i].dataIndex == dataIndex){
55664                 return i;
55665             }
55666         }
55667         return -1;
55668     },
55669     
55670     
55671     moveColumn : function(oldIndex, newIndex){
55672         var c = this.config[oldIndex];
55673         this.config.splice(oldIndex, 1);
55674         this.config.splice(newIndex, 0, c);
55675         this.dataMap = null;
55676         this.fireEvent("columnmoved", this, oldIndex, newIndex);
55677     },
55678
55679     isLocked : function(colIndex){
55680         return this.config[colIndex].locked === true;
55681     },
55682
55683     setLocked : function(colIndex, value, suppressEvent){
55684         if(this.isLocked(colIndex) == value){
55685             return;
55686         }
55687         this.config[colIndex].locked = value;
55688         if(!suppressEvent){
55689             this.fireEvent("columnlockchange", this, colIndex, value);
55690         }
55691     },
55692
55693     getTotalLockedWidth : function(){
55694         var totalWidth = 0;
55695         for(var i = 0; i < this.config.length; i++){
55696             if(this.isLocked(i) && !this.isHidden(i)){
55697                 this.totalWidth += this.getColumnWidth(i);
55698             }
55699         }
55700         return totalWidth;
55701     },
55702
55703     getLockedCount : function(){
55704         for(var i = 0, len = this.config.length; i < len; i++){
55705             if(!this.isLocked(i)){
55706                 return i;
55707             }
55708         }
55709         
55710         return this.config.length;
55711     },
55712
55713     /**
55714      * Returns the number of columns.
55715      * @return {Number}
55716      */
55717     getColumnCount : function(visibleOnly){
55718         if(visibleOnly === true){
55719             var c = 0;
55720             for(var i = 0, len = this.config.length; i < len; i++){
55721                 if(!this.isHidden(i)){
55722                     c++;
55723                 }
55724             }
55725             return c;
55726         }
55727         return this.config.length;
55728     },
55729
55730     /**
55731      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
55732      * @param {Function} fn
55733      * @param {Object} scope (optional)
55734      * @return {Array} result
55735      */
55736     getColumnsBy : function(fn, scope){
55737         var r = [];
55738         for(var i = 0, len = this.config.length; i < len; i++){
55739             var c = this.config[i];
55740             if(fn.call(scope||this, c, i) === true){
55741                 r[r.length] = c;
55742             }
55743         }
55744         return r;
55745     },
55746
55747     /**
55748      * Returns true if the specified column is sortable.
55749      * @param {Number} col The column index
55750      * @return {Boolean}
55751      */
55752     isSortable : function(col){
55753         if(typeof this.config[col].sortable == "undefined"){
55754             return this.defaultSortable;
55755         }
55756         return this.config[col].sortable;
55757     },
55758
55759     /**
55760      * Returns the rendering (formatting) function defined for the column.
55761      * @param {Number} col The column index.
55762      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
55763      */
55764     getRenderer : function(col){
55765         if(!this.config[col].renderer){
55766             return Roo.grid.ColumnModel.defaultRenderer;
55767         }
55768         return this.config[col].renderer;
55769     },
55770
55771     /**
55772      * Sets the rendering (formatting) function for a column.
55773      * @param {Number} col The column index
55774      * @param {Function} fn The function to use to process the cell's raw data
55775      * to return HTML markup for the grid view. The render function is called with
55776      * the following parameters:<ul>
55777      * <li>Data value.</li>
55778      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
55779      * <li>css A CSS style string to apply to the table cell.</li>
55780      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
55781      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
55782      * <li>Row index</li>
55783      * <li>Column index</li>
55784      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
55785      */
55786     setRenderer : function(col, fn){
55787         this.config[col].renderer = fn;
55788     },
55789
55790     /**
55791      * Returns the width for the specified column.
55792      * @param {Number} col The column index
55793      * @return {Number}
55794      */
55795     getColumnWidth : function(col){
55796         return this.config[col].width * 1 || this.defaultWidth;
55797     },
55798
55799     /**
55800      * Sets the width for a column.
55801      * @param {Number} col The column index
55802      * @param {Number} width The new width
55803      */
55804     setColumnWidth : function(col, width, suppressEvent){
55805         this.config[col].width = width;
55806         this.totalWidth = null;
55807         if(!suppressEvent){
55808              this.fireEvent("widthchange", this, col, width);
55809         }
55810     },
55811
55812     /**
55813      * Returns the total width of all columns.
55814      * @param {Boolean} includeHidden True to include hidden column widths
55815      * @return {Number}
55816      */
55817     getTotalWidth : function(includeHidden){
55818         if(!this.totalWidth){
55819             this.totalWidth = 0;
55820             for(var i = 0, len = this.config.length; i < len; i++){
55821                 if(includeHidden || !this.isHidden(i)){
55822                     this.totalWidth += this.getColumnWidth(i);
55823                 }
55824             }
55825         }
55826         return this.totalWidth;
55827     },
55828
55829     /**
55830      * Returns the header for the specified column.
55831      * @param {Number} col The column index
55832      * @return {String}
55833      */
55834     getColumnHeader : function(col){
55835         return this.config[col].header;
55836     },
55837
55838     /**
55839      * Sets the header for a column.
55840      * @param {Number} col The column index
55841      * @param {String} header The new header
55842      */
55843     setColumnHeader : function(col, header){
55844         this.config[col].header = header;
55845         this.fireEvent("headerchange", this, col, header);
55846     },
55847
55848     /**
55849      * Returns the tooltip for the specified column.
55850      * @param {Number} col The column index
55851      * @return {String}
55852      */
55853     getColumnTooltip : function(col){
55854             return this.config[col].tooltip;
55855     },
55856     /**
55857      * Sets the tooltip for a column.
55858      * @param {Number} col The column index
55859      * @param {String} tooltip The new tooltip
55860      */
55861     setColumnTooltip : function(col, tooltip){
55862             this.config[col].tooltip = tooltip;
55863     },
55864
55865     /**
55866      * Returns the dataIndex for the specified column.
55867      * @param {Number} col The column index
55868      * @return {Number}
55869      */
55870     getDataIndex : function(col){
55871         return this.config[col].dataIndex;
55872     },
55873
55874     /**
55875      * Sets the dataIndex for a column.
55876      * @param {Number} col The column index
55877      * @param {Number} dataIndex The new dataIndex
55878      */
55879     setDataIndex : function(col, dataIndex){
55880         this.config[col].dataIndex = dataIndex;
55881     },
55882
55883     
55884     
55885     /**
55886      * Returns true if the cell is editable.
55887      * @param {Number} colIndex The column index
55888      * @param {Number} rowIndex The row index - this is nto actually used..?
55889      * @return {Boolean}
55890      */
55891     isCellEditable : function(colIndex, rowIndex){
55892         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55893     },
55894
55895     /**
55896      * Returns the editor defined for the cell/column.
55897      * return false or null to disable editing.
55898      * @param {Number} colIndex The column index
55899      * @param {Number} rowIndex The row index
55900      * @return {Object}
55901      */
55902     getCellEditor : function(colIndex, rowIndex){
55903         return this.config[colIndex].editor;
55904     },
55905
55906     /**
55907      * Sets if a column is editable.
55908      * @param {Number} col The column index
55909      * @param {Boolean} editable True if the column is editable
55910      */
55911     setEditable : function(col, editable){
55912         this.config[col].editable = editable;
55913     },
55914
55915
55916     /**
55917      * Returns true if the column is hidden.
55918      * @param {Number} colIndex The column index
55919      * @return {Boolean}
55920      */
55921     isHidden : function(colIndex){
55922         return this.config[colIndex].hidden;
55923     },
55924
55925
55926     /**
55927      * Returns true if the column width cannot be changed
55928      */
55929     isFixed : function(colIndex){
55930         return this.config[colIndex].fixed;
55931     },
55932
55933     /**
55934      * Returns true if the column can be resized
55935      * @return {Boolean}
55936      */
55937     isResizable : function(colIndex){
55938         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55939     },
55940     /**
55941      * Sets if a column is hidden.
55942      * @param {Number} colIndex The column index
55943      * @param {Boolean} hidden True if the column is hidden
55944      */
55945     setHidden : function(colIndex, hidden){
55946         this.config[colIndex].hidden = hidden;
55947         this.totalWidth = null;
55948         this.fireEvent("hiddenchange", this, colIndex, hidden);
55949     },
55950
55951     /**
55952      * Sets the editor for a column.
55953      * @param {Number} col The column index
55954      * @param {Object} editor The editor object
55955      */
55956     setEditor : function(col, editor){
55957         this.config[col].editor = editor;
55958     }
55959 });
55960
55961 Roo.grid.ColumnModel.defaultRenderer = function(value){
55962         if(typeof value == "string" && value.length < 1){
55963             return "&#160;";
55964         }
55965         return value;
55966 };
55967
55968 // Alias for backwards compatibility
55969 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55970 /*
55971  * Based on:
55972  * Ext JS Library 1.1.1
55973  * Copyright(c) 2006-2007, Ext JS, LLC.
55974  *
55975  * Originally Released Under LGPL - original licence link has changed is not relivant.
55976  *
55977  * Fork - LGPL
55978  * <script type="text/javascript">
55979  */
55980
55981 /**
55982  * @class Roo.grid.AbstractSelectionModel
55983  * @extends Roo.util.Observable
55984  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55985  * implemented by descendant classes.  This class should not be directly instantiated.
55986  * @constructor
55987  */
55988 Roo.grid.AbstractSelectionModel = function(){
55989     this.locked = false;
55990     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55991 };
55992
55993 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55994     /** @ignore Called by the grid automatically. Do not call directly. */
55995     init : function(grid){
55996         this.grid = grid;
55997         this.initEvents();
55998     },
55999
56000     /**
56001      * Locks the selections.
56002      */
56003     lock : function(){
56004         this.locked = true;
56005     },
56006
56007     /**
56008      * Unlocks the selections.
56009      */
56010     unlock : function(){
56011         this.locked = false;
56012     },
56013
56014     /**
56015      * Returns true if the selections are locked.
56016      * @return {Boolean}
56017      */
56018     isLocked : function(){
56019         return this.locked;
56020     }
56021 });/*
56022  * Based on:
56023  * Ext JS Library 1.1.1
56024  * Copyright(c) 2006-2007, Ext JS, LLC.
56025  *
56026  * Originally Released Under LGPL - original licence link has changed is not relivant.
56027  *
56028  * Fork - LGPL
56029  * <script type="text/javascript">
56030  */
56031 /**
56032  * @extends Roo.grid.AbstractSelectionModel
56033  * @class Roo.grid.RowSelectionModel
56034  * The default SelectionModel used by {@link Roo.grid.Grid}.
56035  * It supports multiple selections and keyboard selection/navigation. 
56036  * @constructor
56037  * @param {Object} config
56038  */
56039 Roo.grid.RowSelectionModel = function(config){
56040     Roo.apply(this, config);
56041     this.selections = new Roo.util.MixedCollection(false, function(o){
56042         return o.id;
56043     });
56044
56045     this.last = false;
56046     this.lastActive = false;
56047
56048     this.addEvents({
56049         /**
56050              * @event selectionchange
56051              * Fires when the selection changes
56052              * @param {SelectionModel} this
56053              */
56054             "selectionchange" : true,
56055         /**
56056              * @event afterselectionchange
56057              * Fires after the selection changes (eg. by key press or clicking)
56058              * @param {SelectionModel} this
56059              */
56060             "afterselectionchange" : true,
56061         /**
56062              * @event beforerowselect
56063              * Fires when a row is selected being selected, return false to cancel.
56064              * @param {SelectionModel} this
56065              * @param {Number} rowIndex The selected index
56066              * @param {Boolean} keepExisting False if other selections will be cleared
56067              */
56068             "beforerowselect" : true,
56069         /**
56070              * @event rowselect
56071              * Fires when a row is selected.
56072              * @param {SelectionModel} this
56073              * @param {Number} rowIndex The selected index
56074              * @param {Roo.data.Record} r The record
56075              */
56076             "rowselect" : true,
56077         /**
56078              * @event rowdeselect
56079              * Fires when a row is deselected.
56080              * @param {SelectionModel} this
56081              * @param {Number} rowIndex The selected index
56082              */
56083         "rowdeselect" : true
56084     });
56085     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
56086     this.locked = false;
56087 };
56088
56089 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
56090     /**
56091      * @cfg {Boolean} singleSelect
56092      * True to allow selection of only one row at a time (defaults to false)
56093      */
56094     singleSelect : false,
56095
56096     // private
56097     initEvents : function(){
56098
56099         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
56100             this.grid.on("mousedown", this.handleMouseDown, this);
56101         }else{ // allow click to work like normal
56102             this.grid.on("rowclick", this.handleDragableRowClick, this);
56103         }
56104
56105         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
56106             "up" : function(e){
56107                 if(!e.shiftKey){
56108                     this.selectPrevious(e.shiftKey);
56109                 }else if(this.last !== false && this.lastActive !== false){
56110                     var last = this.last;
56111                     this.selectRange(this.last,  this.lastActive-1);
56112                     this.grid.getView().focusRow(this.lastActive);
56113                     if(last !== false){
56114                         this.last = last;
56115                     }
56116                 }else{
56117                     this.selectFirstRow();
56118                 }
56119                 this.fireEvent("afterselectionchange", this);
56120             },
56121             "down" : function(e){
56122                 if(!e.shiftKey){
56123                     this.selectNext(e.shiftKey);
56124                 }else if(this.last !== false && this.lastActive !== false){
56125                     var last = this.last;
56126                     this.selectRange(this.last,  this.lastActive+1);
56127                     this.grid.getView().focusRow(this.lastActive);
56128                     if(last !== false){
56129                         this.last = last;
56130                     }
56131                 }else{
56132                     this.selectFirstRow();
56133                 }
56134                 this.fireEvent("afterselectionchange", this);
56135             },
56136             scope: this
56137         });
56138
56139         var view = this.grid.view;
56140         view.on("refresh", this.onRefresh, this);
56141         view.on("rowupdated", this.onRowUpdated, this);
56142         view.on("rowremoved", this.onRemove, this);
56143     },
56144
56145     // private
56146     onRefresh : function(){
56147         var ds = this.grid.dataSource, i, v = this.grid.view;
56148         var s = this.selections;
56149         s.each(function(r){
56150             if((i = ds.indexOfId(r.id)) != -1){
56151                 v.onRowSelect(i);
56152                 s.add(ds.getAt(i)); // updating the selection relate data
56153             }else{
56154                 s.remove(r);
56155             }
56156         });
56157     },
56158
56159     // private
56160     onRemove : function(v, index, r){
56161         this.selections.remove(r);
56162     },
56163
56164     // private
56165     onRowUpdated : function(v, index, r){
56166         if(this.isSelected(r)){
56167             v.onRowSelect(index);
56168         }
56169     },
56170
56171     /**
56172      * Select records.
56173      * @param {Array} records The records to select
56174      * @param {Boolean} keepExisting (optional) True to keep existing selections
56175      */
56176     selectRecords : function(records, keepExisting){
56177         if(!keepExisting){
56178             this.clearSelections();
56179         }
56180         var ds = this.grid.dataSource;
56181         for(var i = 0, len = records.length; i < len; i++){
56182             this.selectRow(ds.indexOf(records[i]), true);
56183         }
56184     },
56185
56186     /**
56187      * Gets the number of selected rows.
56188      * @return {Number}
56189      */
56190     getCount : function(){
56191         return this.selections.length;
56192     },
56193
56194     /**
56195      * Selects the first row in the grid.
56196      */
56197     selectFirstRow : function(){
56198         this.selectRow(0);
56199     },
56200
56201     /**
56202      * Select the last row.
56203      * @param {Boolean} keepExisting (optional) True to keep existing selections
56204      */
56205     selectLastRow : function(keepExisting){
56206         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
56207     },
56208
56209     /**
56210      * Selects the row immediately following the last selected row.
56211      * @param {Boolean} keepExisting (optional) True to keep existing selections
56212      */
56213     selectNext : function(keepExisting){
56214         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
56215             this.selectRow(this.last+1, keepExisting);
56216             this.grid.getView().focusRow(this.last);
56217         }
56218     },
56219
56220     /**
56221      * Selects the row that precedes the last selected row.
56222      * @param {Boolean} keepExisting (optional) True to keep existing selections
56223      */
56224     selectPrevious : function(keepExisting){
56225         if(this.last){
56226             this.selectRow(this.last-1, keepExisting);
56227             this.grid.getView().focusRow(this.last);
56228         }
56229     },
56230
56231     /**
56232      * Returns the selected records
56233      * @return {Array} Array of selected records
56234      */
56235     getSelections : function(){
56236         return [].concat(this.selections.items);
56237     },
56238
56239     /**
56240      * Returns the first selected record.
56241      * @return {Record}
56242      */
56243     getSelected : function(){
56244         return this.selections.itemAt(0);
56245     },
56246
56247
56248     /**
56249      * Clears all selections.
56250      */
56251     clearSelections : function(fast){
56252         if(this.locked) {
56253             return;
56254         }
56255         if(fast !== true){
56256             var ds = this.grid.dataSource;
56257             var s = this.selections;
56258             s.each(function(r){
56259                 this.deselectRow(ds.indexOfId(r.id));
56260             }, this);
56261             s.clear();
56262         }else{
56263             this.selections.clear();
56264         }
56265         this.last = false;
56266     },
56267
56268
56269     /**
56270      * Selects all rows.
56271      */
56272     selectAll : function(){
56273         if(this.locked) {
56274             return;
56275         }
56276         this.selections.clear();
56277         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
56278             this.selectRow(i, true);
56279         }
56280     },
56281
56282     /**
56283      * Returns True if there is a selection.
56284      * @return {Boolean}
56285      */
56286     hasSelection : function(){
56287         return this.selections.length > 0;
56288     },
56289
56290     /**
56291      * Returns True if the specified row is selected.
56292      * @param {Number/Record} record The record or index of the record to check
56293      * @return {Boolean}
56294      */
56295     isSelected : function(index){
56296         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
56297         return (r && this.selections.key(r.id) ? true : false);
56298     },
56299
56300     /**
56301      * Returns True if the specified record id is selected.
56302      * @param {String} id The id of record to check
56303      * @return {Boolean}
56304      */
56305     isIdSelected : function(id){
56306         return (this.selections.key(id) ? true : false);
56307     },
56308
56309     // private
56310     handleMouseDown : function(e, t){
56311         var view = this.grid.getView(), rowIndex;
56312         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
56313             return;
56314         };
56315         if(e.shiftKey && this.last !== false){
56316             var last = this.last;
56317             this.selectRange(last, rowIndex, e.ctrlKey);
56318             this.last = last; // reset the last
56319             view.focusRow(rowIndex);
56320         }else{
56321             var isSelected = this.isSelected(rowIndex);
56322             if(e.button !== 0 && isSelected){
56323                 view.focusRow(rowIndex);
56324             }else if(e.ctrlKey && isSelected){
56325                 this.deselectRow(rowIndex);
56326             }else if(!isSelected){
56327                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
56328                 view.focusRow(rowIndex);
56329             }
56330         }
56331         this.fireEvent("afterselectionchange", this);
56332     },
56333     // private
56334     handleDragableRowClick :  function(grid, rowIndex, e) 
56335     {
56336         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
56337             this.selectRow(rowIndex, false);
56338             grid.view.focusRow(rowIndex);
56339              this.fireEvent("afterselectionchange", this);
56340         }
56341     },
56342     
56343     /**
56344      * Selects multiple rows.
56345      * @param {Array} rows Array of the indexes of the row to select
56346      * @param {Boolean} keepExisting (optional) True to keep existing selections
56347      */
56348     selectRows : function(rows, keepExisting){
56349         if(!keepExisting){
56350             this.clearSelections();
56351         }
56352         for(var i = 0, len = rows.length; i < len; i++){
56353             this.selectRow(rows[i], true);
56354         }
56355     },
56356
56357     /**
56358      * Selects a range of rows. All rows in between startRow and endRow are also selected.
56359      * @param {Number} startRow The index of the first row in the range
56360      * @param {Number} endRow The index of the last row in the range
56361      * @param {Boolean} keepExisting (optional) True to retain existing selections
56362      */
56363     selectRange : function(startRow, endRow, keepExisting){
56364         if(this.locked) {
56365             return;
56366         }
56367         if(!keepExisting){
56368             this.clearSelections();
56369         }
56370         if(startRow <= endRow){
56371             for(var i = startRow; i <= endRow; i++){
56372                 this.selectRow(i, true);
56373             }
56374         }else{
56375             for(var i = startRow; i >= endRow; i--){
56376                 this.selectRow(i, true);
56377             }
56378         }
56379     },
56380
56381     /**
56382      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
56383      * @param {Number} startRow The index of the first row in the range
56384      * @param {Number} endRow The index of the last row in the range
56385      */
56386     deselectRange : function(startRow, endRow, preventViewNotify){
56387         if(this.locked) {
56388             return;
56389         }
56390         for(var i = startRow; i <= endRow; i++){
56391             this.deselectRow(i, preventViewNotify);
56392         }
56393     },
56394
56395     /**
56396      * Selects a row.
56397      * @param {Number} row The index of the row to select
56398      * @param {Boolean} keepExisting (optional) True to keep existing selections
56399      */
56400     selectRow : function(index, keepExisting, preventViewNotify){
56401         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
56402             return;
56403         }
56404         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
56405             if(!keepExisting || this.singleSelect){
56406                 this.clearSelections();
56407             }
56408             var r = this.grid.dataSource.getAt(index);
56409             this.selections.add(r);
56410             this.last = this.lastActive = index;
56411             if(!preventViewNotify){
56412                 this.grid.getView().onRowSelect(index);
56413             }
56414             this.fireEvent("rowselect", this, index, r);
56415             this.fireEvent("selectionchange", this);
56416         }
56417     },
56418
56419     /**
56420      * Deselects a row.
56421      * @param {Number} row The index of the row to deselect
56422      */
56423     deselectRow : function(index, preventViewNotify){
56424         if(this.locked) {
56425             return;
56426         }
56427         if(this.last == index){
56428             this.last = false;
56429         }
56430         if(this.lastActive == index){
56431             this.lastActive = false;
56432         }
56433         var r = this.grid.dataSource.getAt(index);
56434         this.selections.remove(r);
56435         if(!preventViewNotify){
56436             this.grid.getView().onRowDeselect(index);
56437         }
56438         this.fireEvent("rowdeselect", this, index);
56439         this.fireEvent("selectionchange", this);
56440     },
56441
56442     // private
56443     restoreLast : function(){
56444         if(this._last){
56445             this.last = this._last;
56446         }
56447     },
56448
56449     // private
56450     acceptsNav : function(row, col, cm){
56451         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56452     },
56453
56454     // private
56455     onEditorKey : function(field, e){
56456         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
56457         if(k == e.TAB){
56458             e.stopEvent();
56459             ed.completeEdit();
56460             if(e.shiftKey){
56461                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56462             }else{
56463                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56464             }
56465         }else if(k == e.ENTER && !e.ctrlKey){
56466             e.stopEvent();
56467             ed.completeEdit();
56468             if(e.shiftKey){
56469                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
56470             }else{
56471                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
56472             }
56473         }else if(k == e.ESC){
56474             ed.cancelEdit();
56475         }
56476         if(newCell){
56477             g.startEditing(newCell[0], newCell[1]);
56478         }
56479     }
56480 });/*
56481  * Based on:
56482  * Ext JS Library 1.1.1
56483  * Copyright(c) 2006-2007, Ext JS, LLC.
56484  *
56485  * Originally Released Under LGPL - original licence link has changed is not relivant.
56486  *
56487  * Fork - LGPL
56488  * <script type="text/javascript">
56489  */
56490 /**
56491  * @class Roo.grid.CellSelectionModel
56492  * @extends Roo.grid.AbstractSelectionModel
56493  * This class provides the basic implementation for cell selection in a grid.
56494  * @constructor
56495  * @param {Object} config The object containing the configuration of this model.
56496  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
56497  */
56498 Roo.grid.CellSelectionModel = function(config){
56499     Roo.apply(this, config);
56500
56501     this.selection = null;
56502
56503     this.addEvents({
56504         /**
56505              * @event beforerowselect
56506              * Fires before a cell is selected.
56507              * @param {SelectionModel} this
56508              * @param {Number} rowIndex The selected row index
56509              * @param {Number} colIndex The selected cell index
56510              */
56511             "beforecellselect" : true,
56512         /**
56513              * @event cellselect
56514              * Fires when a cell is selected.
56515              * @param {SelectionModel} this
56516              * @param {Number} rowIndex The selected row index
56517              * @param {Number} colIndex The selected cell index
56518              */
56519             "cellselect" : true,
56520         /**
56521              * @event selectionchange
56522              * Fires when the active selection changes.
56523              * @param {SelectionModel} this
56524              * @param {Object} selection null for no selection or an object (o) with two properties
56525                 <ul>
56526                 <li>o.record: the record object for the row the selection is in</li>
56527                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
56528                 </ul>
56529              */
56530             "selectionchange" : true,
56531         /**
56532              * @event tabend
56533              * Fires when the tab (or enter) was pressed on the last editable cell
56534              * You can use this to trigger add new row.
56535              * @param {SelectionModel} this
56536              */
56537             "tabend" : true,
56538          /**
56539              * @event beforeeditnext
56540              * Fires before the next editable sell is made active
56541              * You can use this to skip to another cell or fire the tabend
56542              *    if you set cell to false
56543              * @param {Object} eventdata object : { cell : [ row, col ] } 
56544              */
56545             "beforeeditnext" : true
56546     });
56547     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
56548 };
56549
56550 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
56551     
56552     enter_is_tab: false,
56553
56554     /** @ignore */
56555     initEvents : function(){
56556         this.grid.on("mousedown", this.handleMouseDown, this);
56557         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
56558         var view = this.grid.view;
56559         view.on("refresh", this.onViewChange, this);
56560         view.on("rowupdated", this.onRowUpdated, this);
56561         view.on("beforerowremoved", this.clearSelections, this);
56562         view.on("beforerowsinserted", this.clearSelections, this);
56563         if(this.grid.isEditor){
56564             this.grid.on("beforeedit", this.beforeEdit,  this);
56565         }
56566     },
56567
56568         //private
56569     beforeEdit : function(e){
56570         this.select(e.row, e.column, false, true, e.record);
56571     },
56572
56573         //private
56574     onRowUpdated : function(v, index, r){
56575         if(this.selection && this.selection.record == r){
56576             v.onCellSelect(index, this.selection.cell[1]);
56577         }
56578     },
56579
56580         //private
56581     onViewChange : function(){
56582         this.clearSelections(true);
56583     },
56584
56585         /**
56586          * Returns the currently selected cell,.
56587          * @return {Array} The selected cell (row, column) or null if none selected.
56588          */
56589     getSelectedCell : function(){
56590         return this.selection ? this.selection.cell : null;
56591     },
56592
56593     /**
56594      * Clears all selections.
56595      * @param {Boolean} true to prevent the gridview from being notified about the change.
56596      */
56597     clearSelections : function(preventNotify){
56598         var s = this.selection;
56599         if(s){
56600             if(preventNotify !== true){
56601                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
56602             }
56603             this.selection = null;
56604             this.fireEvent("selectionchange", this, null);
56605         }
56606     },
56607
56608     /**
56609      * Returns true if there is a selection.
56610      * @return {Boolean}
56611      */
56612     hasSelection : function(){
56613         return this.selection ? true : false;
56614     },
56615
56616     /** @ignore */
56617     handleMouseDown : function(e, t){
56618         var v = this.grid.getView();
56619         if(this.isLocked()){
56620             return;
56621         };
56622         var row = v.findRowIndex(t);
56623         var cell = v.findCellIndex(t);
56624         if(row !== false && cell !== false){
56625             this.select(row, cell);
56626         }
56627     },
56628
56629     /**
56630      * Selects a cell.
56631      * @param {Number} rowIndex
56632      * @param {Number} collIndex
56633      */
56634     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
56635         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
56636             this.clearSelections();
56637             r = r || this.grid.dataSource.getAt(rowIndex);
56638             this.selection = {
56639                 record : r,
56640                 cell : [rowIndex, colIndex]
56641             };
56642             if(!preventViewNotify){
56643                 var v = this.grid.getView();
56644                 v.onCellSelect(rowIndex, colIndex);
56645                 if(preventFocus !== true){
56646                     v.focusCell(rowIndex, colIndex);
56647                 }
56648             }
56649             this.fireEvent("cellselect", this, rowIndex, colIndex);
56650             this.fireEvent("selectionchange", this, this.selection);
56651         }
56652     },
56653
56654         //private
56655     isSelectable : function(rowIndex, colIndex, cm){
56656         return !cm.isHidden(colIndex);
56657     },
56658
56659     /** @ignore */
56660     handleKeyDown : function(e){
56661         //Roo.log('Cell Sel Model handleKeyDown');
56662         if(!e.isNavKeyPress()){
56663             return;
56664         }
56665         var g = this.grid, s = this.selection;
56666         if(!s){
56667             e.stopEvent();
56668             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
56669             if(cell){
56670                 this.select(cell[0], cell[1]);
56671             }
56672             return;
56673         }
56674         var sm = this;
56675         var walk = function(row, col, step){
56676             return g.walkCells(row, col, step, sm.isSelectable,  sm);
56677         };
56678         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
56679         var newCell;
56680
56681       
56682
56683         switch(k){
56684             case e.TAB:
56685                 // handled by onEditorKey
56686                 if (g.isEditor && g.editing) {
56687                     return;
56688                 }
56689                 if(e.shiftKey) {
56690                     newCell = walk(r, c-1, -1);
56691                 } else {
56692                     newCell = walk(r, c+1, 1);
56693                 }
56694                 break;
56695             
56696             case e.DOWN:
56697                newCell = walk(r+1, c, 1);
56698                 break;
56699             
56700             case e.UP:
56701                 newCell = walk(r-1, c, -1);
56702                 break;
56703             
56704             case e.RIGHT:
56705                 newCell = walk(r, c+1, 1);
56706                 break;
56707             
56708             case e.LEFT:
56709                 newCell = walk(r, c-1, -1);
56710                 break;
56711             
56712             case e.ENTER:
56713                 
56714                 if(g.isEditor && !g.editing){
56715                    g.startEditing(r, c);
56716                    e.stopEvent();
56717                    return;
56718                 }
56719                 
56720                 
56721              break;
56722         };
56723         if(newCell){
56724             this.select(newCell[0], newCell[1]);
56725             e.stopEvent();
56726             
56727         }
56728     },
56729
56730     acceptsNav : function(row, col, cm){
56731         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56732     },
56733     /**
56734      * Selects a cell.
56735      * @param {Number} field (not used) - as it's normally used as a listener
56736      * @param {Number} e - event - fake it by using
56737      *
56738      * var e = Roo.EventObjectImpl.prototype;
56739      * e.keyCode = e.TAB
56740      *
56741      * 
56742      */
56743     onEditorKey : function(field, e){
56744         
56745         var k = e.getKey(),
56746             newCell,
56747             g = this.grid,
56748             ed = g.activeEditor,
56749             forward = false;
56750         ///Roo.log('onEditorKey' + k);
56751         
56752         
56753         if (this.enter_is_tab && k == e.ENTER) {
56754             k = e.TAB;
56755         }
56756         
56757         if(k == e.TAB){
56758             if(e.shiftKey){
56759                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56760             }else{
56761                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56762                 forward = true;
56763             }
56764             
56765             e.stopEvent();
56766             
56767         } else if(k == e.ENTER &&  !e.ctrlKey){
56768             ed.completeEdit();
56769             e.stopEvent();
56770             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56771         
56772                 } else if(k == e.ESC){
56773             ed.cancelEdit();
56774         }
56775                 
56776         if (newCell) {
56777             var ecall = { cell : newCell, forward : forward };
56778             this.fireEvent('beforeeditnext', ecall );
56779             newCell = ecall.cell;
56780                         forward = ecall.forward;
56781         }
56782                 
56783         if(newCell){
56784             //Roo.log('next cell after edit');
56785             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
56786         } else if (forward) {
56787             // tabbed past last
56788             this.fireEvent.defer(100, this, ['tabend',this]);
56789         }
56790     }
56791 });/*
56792  * Based on:
56793  * Ext JS Library 1.1.1
56794  * Copyright(c) 2006-2007, Ext JS, LLC.
56795  *
56796  * Originally Released Under LGPL - original licence link has changed is not relivant.
56797  *
56798  * Fork - LGPL
56799  * <script type="text/javascript">
56800  */
56801  
56802 /**
56803  * @class Roo.grid.EditorGrid
56804  * @extends Roo.grid.Grid
56805  * Class for creating and editable grid.
56806  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
56807  * The container MUST have some type of size defined for the grid to fill. The container will be 
56808  * automatically set to position relative if it isn't already.
56809  * @param {Object} dataSource The data model to bind to
56810  * @param {Object} colModel The column model with info about this grid's columns
56811  */
56812 Roo.grid.EditorGrid = function(container, config){
56813     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
56814     this.getGridEl().addClass("xedit-grid");
56815
56816     if(!this.selModel){
56817         this.selModel = new Roo.grid.CellSelectionModel();
56818     }
56819
56820     this.activeEditor = null;
56821
56822         this.addEvents({
56823             /**
56824              * @event beforeedit
56825              * Fires before cell editing is triggered. The edit event object has the following properties <br />
56826              * <ul style="padding:5px;padding-left:16px;">
56827              * <li>grid - This grid</li>
56828              * <li>record - The record being edited</li>
56829              * <li>field - The field name being edited</li>
56830              * <li>value - The value for the field being edited.</li>
56831              * <li>row - The grid row index</li>
56832              * <li>column - The grid column index</li>
56833              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56834              * </ul>
56835              * @param {Object} e An edit event (see above for description)
56836              */
56837             "beforeedit" : true,
56838             /**
56839              * @event afteredit
56840              * Fires after a cell is edited. <br />
56841              * <ul style="padding:5px;padding-left:16px;">
56842              * <li>grid - This grid</li>
56843              * <li>record - The record being edited</li>
56844              * <li>field - The field name being edited</li>
56845              * <li>value - The value being set</li>
56846              * <li>originalValue - The original value for the field, before the edit.</li>
56847              * <li>row - The grid row index</li>
56848              * <li>column - The grid column index</li>
56849              * </ul>
56850              * @param {Object} e An edit event (see above for description)
56851              */
56852             "afteredit" : true,
56853             /**
56854              * @event validateedit
56855              * Fires after a cell is edited, but before the value is set in the record. 
56856          * You can use this to modify the value being set in the field, Return false
56857              * to cancel the change. The edit event object has the following properties <br />
56858              * <ul style="padding:5px;padding-left:16px;">
56859          * <li>editor - This editor</li>
56860              * <li>grid - This grid</li>
56861              * <li>record - The record being edited</li>
56862              * <li>field - The field name being edited</li>
56863              * <li>value - The value being set</li>
56864              * <li>originalValue - The original value for the field, before the edit.</li>
56865              * <li>row - The grid row index</li>
56866              * <li>column - The grid column index</li>
56867              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56868              * </ul>
56869              * @param {Object} e An edit event (see above for description)
56870              */
56871             "validateedit" : true
56872         });
56873     this.on("bodyscroll", this.stopEditing,  this);
56874     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56875 };
56876
56877 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56878     /**
56879      * @cfg {Number} clicksToEdit
56880      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56881      */
56882     clicksToEdit: 2,
56883
56884     // private
56885     isEditor : true,
56886     // private
56887     trackMouseOver: false, // causes very odd FF errors
56888
56889     onCellDblClick : function(g, row, col){
56890         this.startEditing(row, col);
56891     },
56892
56893     onEditComplete : function(ed, value, startValue){
56894         this.editing = false;
56895         this.activeEditor = null;
56896         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56897         var r = ed.record;
56898         var field = this.colModel.getDataIndex(ed.col);
56899         var e = {
56900             grid: this,
56901             record: r,
56902             field: field,
56903             originalValue: startValue,
56904             value: value,
56905             row: ed.row,
56906             column: ed.col,
56907             cancel:false,
56908             editor: ed
56909         };
56910         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
56911         cell.show();
56912           
56913         if(String(value) !== String(startValue)){
56914             
56915             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56916                 r.set(field, e.value);
56917                 // if we are dealing with a combo box..
56918                 // then we also set the 'name' colum to be the displayField
56919                 if (ed.field.displayField && ed.field.name) {
56920                     r.set(ed.field.name, ed.field.el.dom.value);
56921                 }
56922                 
56923                 delete e.cancel; //?? why!!!
56924                 this.fireEvent("afteredit", e);
56925             }
56926         } else {
56927             this.fireEvent("afteredit", e); // always fire it!
56928         }
56929         this.view.focusCell(ed.row, ed.col);
56930     },
56931
56932     /**
56933      * Starts editing the specified for the specified row/column
56934      * @param {Number} rowIndex
56935      * @param {Number} colIndex
56936      */
56937     startEditing : function(row, col){
56938         this.stopEditing();
56939         if(this.colModel.isCellEditable(col, row)){
56940             this.view.ensureVisible(row, col, true);
56941           
56942             var r = this.dataSource.getAt(row);
56943             var field = this.colModel.getDataIndex(col);
56944             var cell = Roo.get(this.view.getCell(row,col));
56945             var e = {
56946                 grid: this,
56947                 record: r,
56948                 field: field,
56949                 value: r.data[field],
56950                 row: row,
56951                 column: col,
56952                 cancel:false 
56953             };
56954             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56955                 this.editing = true;
56956                 var ed = this.colModel.getCellEditor(col, row);
56957                 
56958                 if (!ed) {
56959                     return;
56960                 }
56961                 if(!ed.rendered){
56962                     ed.render(ed.parentEl || document.body);
56963                 }
56964                 ed.field.reset();
56965                
56966                 cell.hide();
56967                 
56968                 (function(){ // complex but required for focus issues in safari, ie and opera
56969                     ed.row = row;
56970                     ed.col = col;
56971                     ed.record = r;
56972                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56973                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56974                     this.activeEditor = ed;
56975                     var v = r.data[field];
56976                     ed.startEdit(this.view.getCell(row, col), v);
56977                     // combo's with 'displayField and name set
56978                     if (ed.field.displayField && ed.field.name) {
56979                         ed.field.el.dom.value = r.data[ed.field.name];
56980                     }
56981                     
56982                     
56983                 }).defer(50, this);
56984             }
56985         }
56986     },
56987         
56988     /**
56989      * Stops any active editing
56990      */
56991     stopEditing : function(){
56992         if(this.activeEditor){
56993             this.activeEditor.completeEdit();
56994         }
56995         this.activeEditor = null;
56996     },
56997         
56998          /**
56999      * Called to get grid's drag proxy text, by default returns this.ddText.
57000      * @return {String}
57001      */
57002     getDragDropText : function(){
57003         var count = this.selModel.getSelectedCell() ? 1 : 0;
57004         return String.format(this.ddText, count, count == 1 ? '' : 's');
57005     }
57006         
57007 });/*
57008  * Based on:
57009  * Ext JS Library 1.1.1
57010  * Copyright(c) 2006-2007, Ext JS, LLC.
57011  *
57012  * Originally Released Under LGPL - original licence link has changed is not relivant.
57013  *
57014  * Fork - LGPL
57015  * <script type="text/javascript">
57016  */
57017
57018 // private - not really -- you end up using it !
57019 // This is a support class used internally by the Grid components
57020
57021 /**
57022  * @class Roo.grid.GridEditor
57023  * @extends Roo.Editor
57024  * Class for creating and editable grid elements.
57025  * @param {Object} config any settings (must include field)
57026  */
57027 Roo.grid.GridEditor = function(field, config){
57028     if (!config && field.field) {
57029         config = field;
57030         field = Roo.factory(config.field, Roo.form);
57031     }
57032     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
57033     field.monitorTab = false;
57034 };
57035
57036 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
57037     
57038     /**
57039      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
57040      */
57041     
57042     alignment: "tl-tl",
57043     autoSize: "width",
57044     hideEl : false,
57045     cls: "x-small-editor x-grid-editor",
57046     shim:false,
57047     shadow:"frame"
57048 });/*
57049  * Based on:
57050  * Ext JS Library 1.1.1
57051  * Copyright(c) 2006-2007, Ext JS, LLC.
57052  *
57053  * Originally Released Under LGPL - original licence link has changed is not relivant.
57054  *
57055  * Fork - LGPL
57056  * <script type="text/javascript">
57057  */
57058   
57059
57060   
57061 Roo.grid.PropertyRecord = Roo.data.Record.create([
57062     {name:'name',type:'string'},  'value'
57063 ]);
57064
57065
57066 Roo.grid.PropertyStore = function(grid, source){
57067     this.grid = grid;
57068     this.store = new Roo.data.Store({
57069         recordType : Roo.grid.PropertyRecord
57070     });
57071     this.store.on('update', this.onUpdate,  this);
57072     if(source){
57073         this.setSource(source);
57074     }
57075     Roo.grid.PropertyStore.superclass.constructor.call(this);
57076 };
57077
57078
57079
57080 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
57081     setSource : function(o){
57082         this.source = o;
57083         this.store.removeAll();
57084         var data = [];
57085         for(var k in o){
57086             if(this.isEditableValue(o[k])){
57087                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
57088             }
57089         }
57090         this.store.loadRecords({records: data}, {}, true);
57091     },
57092
57093     onUpdate : function(ds, record, type){
57094         if(type == Roo.data.Record.EDIT){
57095             var v = record.data['value'];
57096             var oldValue = record.modified['value'];
57097             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
57098                 this.source[record.id] = v;
57099                 record.commit();
57100                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
57101             }else{
57102                 record.reject();
57103             }
57104         }
57105     },
57106
57107     getProperty : function(row){
57108        return this.store.getAt(row);
57109     },
57110
57111     isEditableValue: function(val){
57112         if(val && val instanceof Date){
57113             return true;
57114         }else if(typeof val == 'object' || typeof val == 'function'){
57115             return false;
57116         }
57117         return true;
57118     },
57119
57120     setValue : function(prop, value){
57121         this.source[prop] = value;
57122         this.store.getById(prop).set('value', value);
57123     },
57124
57125     getSource : function(){
57126         return this.source;
57127     }
57128 });
57129
57130 Roo.grid.PropertyColumnModel = function(grid, store){
57131     this.grid = grid;
57132     var g = Roo.grid;
57133     g.PropertyColumnModel.superclass.constructor.call(this, [
57134         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
57135         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
57136     ]);
57137     this.store = store;
57138     this.bselect = Roo.DomHelper.append(document.body, {
57139         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
57140             {tag: 'option', value: 'true', html: 'true'},
57141             {tag: 'option', value: 'false', html: 'false'}
57142         ]
57143     });
57144     Roo.id(this.bselect);
57145     var f = Roo.form;
57146     this.editors = {
57147         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
57148         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
57149         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
57150         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
57151         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
57152     };
57153     this.renderCellDelegate = this.renderCell.createDelegate(this);
57154     this.renderPropDelegate = this.renderProp.createDelegate(this);
57155 };
57156
57157 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
57158     
57159     
57160     nameText : 'Name',
57161     valueText : 'Value',
57162     
57163     dateFormat : 'm/j/Y',
57164     
57165     
57166     renderDate : function(dateVal){
57167         return dateVal.dateFormat(this.dateFormat);
57168     },
57169
57170     renderBool : function(bVal){
57171         return bVal ? 'true' : 'false';
57172     },
57173
57174     isCellEditable : function(colIndex, rowIndex){
57175         return colIndex == 1;
57176     },
57177
57178     getRenderer : function(col){
57179         return col == 1 ?
57180             this.renderCellDelegate : this.renderPropDelegate;
57181     },
57182
57183     renderProp : function(v){
57184         return this.getPropertyName(v);
57185     },
57186
57187     renderCell : function(val){
57188         var rv = val;
57189         if(val instanceof Date){
57190             rv = this.renderDate(val);
57191         }else if(typeof val == 'boolean'){
57192             rv = this.renderBool(val);
57193         }
57194         return Roo.util.Format.htmlEncode(rv);
57195     },
57196
57197     getPropertyName : function(name){
57198         var pn = this.grid.propertyNames;
57199         return pn && pn[name] ? pn[name] : name;
57200     },
57201
57202     getCellEditor : function(colIndex, rowIndex){
57203         var p = this.store.getProperty(rowIndex);
57204         var n = p.data['name'], val = p.data['value'];
57205         
57206         if(typeof(this.grid.customEditors[n]) == 'string'){
57207             return this.editors[this.grid.customEditors[n]];
57208         }
57209         if(typeof(this.grid.customEditors[n]) != 'undefined'){
57210             return this.grid.customEditors[n];
57211         }
57212         if(val instanceof Date){
57213             return this.editors['date'];
57214         }else if(typeof val == 'number'){
57215             return this.editors['number'];
57216         }else if(typeof val == 'boolean'){
57217             return this.editors['boolean'];
57218         }else{
57219             return this.editors['string'];
57220         }
57221     }
57222 });
57223
57224 /**
57225  * @class Roo.grid.PropertyGrid
57226  * @extends Roo.grid.EditorGrid
57227  * This class represents the  interface of a component based property grid control.
57228  * <br><br>Usage:<pre><code>
57229  var grid = new Roo.grid.PropertyGrid("my-container-id", {
57230       
57231  });
57232  // set any options
57233  grid.render();
57234  * </code></pre>
57235   
57236  * @constructor
57237  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57238  * The container MUST have some type of size defined for the grid to fill. The container will be
57239  * automatically set to position relative if it isn't already.
57240  * @param {Object} config A config object that sets properties on this grid.
57241  */
57242 Roo.grid.PropertyGrid = function(container, config){
57243     config = config || {};
57244     var store = new Roo.grid.PropertyStore(this);
57245     this.store = store;
57246     var cm = new Roo.grid.PropertyColumnModel(this, store);
57247     store.store.sort('name', 'ASC');
57248     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
57249         ds: store.store,
57250         cm: cm,
57251         enableColLock:false,
57252         enableColumnMove:false,
57253         stripeRows:false,
57254         trackMouseOver: false,
57255         clicksToEdit:1
57256     }, config));
57257     this.getGridEl().addClass('x-props-grid');
57258     this.lastEditRow = null;
57259     this.on('columnresize', this.onColumnResize, this);
57260     this.addEvents({
57261          /**
57262              * @event beforepropertychange
57263              * Fires before a property changes (return false to stop?)
57264              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57265              * @param {String} id Record Id
57266              * @param {String} newval New Value
57267          * @param {String} oldval Old Value
57268              */
57269         "beforepropertychange": true,
57270         /**
57271              * @event propertychange
57272              * Fires after a property changes
57273              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57274              * @param {String} id Record Id
57275              * @param {String} newval New Value
57276          * @param {String} oldval Old Value
57277              */
57278         "propertychange": true
57279     });
57280     this.customEditors = this.customEditors || {};
57281 };
57282 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
57283     
57284      /**
57285      * @cfg {Object} customEditors map of colnames=> custom editors.
57286      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
57287      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
57288      * false disables editing of the field.
57289          */
57290     
57291       /**
57292      * @cfg {Object} propertyNames map of property Names to their displayed value
57293          */
57294     
57295     render : function(){
57296         Roo.grid.PropertyGrid.superclass.render.call(this);
57297         this.autoSize.defer(100, this);
57298     },
57299
57300     autoSize : function(){
57301         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
57302         if(this.view){
57303             this.view.fitColumns();
57304         }
57305     },
57306
57307     onColumnResize : function(){
57308         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
57309         this.autoSize();
57310     },
57311     /**
57312      * Sets the data for the Grid
57313      * accepts a Key => Value object of all the elements avaiable.
57314      * @param {Object} data  to appear in grid.
57315      */
57316     setSource : function(source){
57317         this.store.setSource(source);
57318         //this.autoSize();
57319     },
57320     /**
57321      * Gets all the data from the grid.
57322      * @return {Object} data  data stored in grid
57323      */
57324     getSource : function(){
57325         return this.store.getSource();
57326     }
57327 });/*
57328   
57329  * Licence LGPL
57330  
57331  */
57332  
57333 /**
57334  * @class Roo.grid.Calendar
57335  * @extends Roo.util.Grid
57336  * This class extends the Grid to provide a calendar widget
57337  * <br><br>Usage:<pre><code>
57338  var grid = new Roo.grid.Calendar("my-container-id", {
57339      ds: myDataStore,
57340      cm: myColModel,
57341      selModel: mySelectionModel,
57342      autoSizeColumns: true,
57343      monitorWindowResize: false,
57344      trackMouseOver: true
57345      eventstore : real data store..
57346  });
57347  // set any options
57348  grid.render();
57349   
57350   * @constructor
57351  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57352  * The container MUST have some type of size defined for the grid to fill. The container will be
57353  * automatically set to position relative if it isn't already.
57354  * @param {Object} config A config object that sets properties on this grid.
57355  */
57356 Roo.grid.Calendar = function(container, config){
57357         // initialize the container
57358         this.container = Roo.get(container);
57359         this.container.update("");
57360         this.container.setStyle("overflow", "hidden");
57361     this.container.addClass('x-grid-container');
57362
57363     this.id = this.container.id;
57364
57365     Roo.apply(this, config);
57366     // check and correct shorthanded configs
57367     
57368     var rows = [];
57369     var d =1;
57370     for (var r = 0;r < 6;r++) {
57371         
57372         rows[r]=[];
57373         for (var c =0;c < 7;c++) {
57374             rows[r][c]= '';
57375         }
57376     }
57377     if (this.eventStore) {
57378         this.eventStore= Roo.factory(this.eventStore, Roo.data);
57379         this.eventStore.on('load',this.onLoad, this);
57380         this.eventStore.on('beforeload',this.clearEvents, this);
57381          
57382     }
57383     
57384     this.dataSource = new Roo.data.Store({
57385             proxy: new Roo.data.MemoryProxy(rows),
57386             reader: new Roo.data.ArrayReader({}, [
57387                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
57388     });
57389
57390     this.dataSource.load();
57391     this.ds = this.dataSource;
57392     this.ds.xmodule = this.xmodule || false;
57393     
57394     
57395     var cellRender = function(v,x,r)
57396     {
57397         return String.format(
57398             '<div class="fc-day  fc-widget-content"><div>' +
57399                 '<div class="fc-event-container"></div>' +
57400                 '<div class="fc-day-number">{0}</div>'+
57401                 
57402                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
57403             '</div></div>', v);
57404     
57405     }
57406     
57407     
57408     this.colModel = new Roo.grid.ColumnModel( [
57409         {
57410             xtype: 'ColumnModel',
57411             xns: Roo.grid,
57412             dataIndex : 'weekday0',
57413             header : 'Sunday',
57414             renderer : cellRender
57415         },
57416         {
57417             xtype: 'ColumnModel',
57418             xns: Roo.grid,
57419             dataIndex : 'weekday1',
57420             header : 'Monday',
57421             renderer : cellRender
57422         },
57423         {
57424             xtype: 'ColumnModel',
57425             xns: Roo.grid,
57426             dataIndex : 'weekday2',
57427             header : 'Tuesday',
57428             renderer : cellRender
57429         },
57430         {
57431             xtype: 'ColumnModel',
57432             xns: Roo.grid,
57433             dataIndex : 'weekday3',
57434             header : 'Wednesday',
57435             renderer : cellRender
57436         },
57437         {
57438             xtype: 'ColumnModel',
57439             xns: Roo.grid,
57440             dataIndex : 'weekday4',
57441             header : 'Thursday',
57442             renderer : cellRender
57443         },
57444         {
57445             xtype: 'ColumnModel',
57446             xns: Roo.grid,
57447             dataIndex : 'weekday5',
57448             header : 'Friday',
57449             renderer : cellRender
57450         },
57451         {
57452             xtype: 'ColumnModel',
57453             xns: Roo.grid,
57454             dataIndex : 'weekday6',
57455             header : 'Saturday',
57456             renderer : cellRender
57457         }
57458     ]);
57459     this.cm = this.colModel;
57460     this.cm.xmodule = this.xmodule || false;
57461  
57462         
57463           
57464     //this.selModel = new Roo.grid.CellSelectionModel();
57465     //this.sm = this.selModel;
57466     //this.selModel.init(this);
57467     
57468     
57469     if(this.width){
57470         this.container.setWidth(this.width);
57471     }
57472
57473     if(this.height){
57474         this.container.setHeight(this.height);
57475     }
57476     /** @private */
57477         this.addEvents({
57478         // raw events
57479         /**
57480          * @event click
57481          * The raw click event for the entire grid.
57482          * @param {Roo.EventObject} e
57483          */
57484         "click" : true,
57485         /**
57486          * @event dblclick
57487          * The raw dblclick event for the entire grid.
57488          * @param {Roo.EventObject} e
57489          */
57490         "dblclick" : true,
57491         /**
57492          * @event contextmenu
57493          * The raw contextmenu event for the entire grid.
57494          * @param {Roo.EventObject} e
57495          */
57496         "contextmenu" : true,
57497         /**
57498          * @event mousedown
57499          * The raw mousedown event for the entire grid.
57500          * @param {Roo.EventObject} e
57501          */
57502         "mousedown" : true,
57503         /**
57504          * @event mouseup
57505          * The raw mouseup event for the entire grid.
57506          * @param {Roo.EventObject} e
57507          */
57508         "mouseup" : true,
57509         /**
57510          * @event mouseover
57511          * The raw mouseover event for the entire grid.
57512          * @param {Roo.EventObject} e
57513          */
57514         "mouseover" : true,
57515         /**
57516          * @event mouseout
57517          * The raw mouseout event for the entire grid.
57518          * @param {Roo.EventObject} e
57519          */
57520         "mouseout" : true,
57521         /**
57522          * @event keypress
57523          * The raw keypress event for the entire grid.
57524          * @param {Roo.EventObject} e
57525          */
57526         "keypress" : true,
57527         /**
57528          * @event keydown
57529          * The raw keydown event for the entire grid.
57530          * @param {Roo.EventObject} e
57531          */
57532         "keydown" : true,
57533
57534         // custom events
57535
57536         /**
57537          * @event cellclick
57538          * Fires when a cell is clicked
57539          * @param {Grid} this
57540          * @param {Number} rowIndex
57541          * @param {Number} columnIndex
57542          * @param {Roo.EventObject} e
57543          */
57544         "cellclick" : true,
57545         /**
57546          * @event celldblclick
57547          * Fires when a cell is double clicked
57548          * @param {Grid} this
57549          * @param {Number} rowIndex
57550          * @param {Number} columnIndex
57551          * @param {Roo.EventObject} e
57552          */
57553         "celldblclick" : true,
57554         /**
57555          * @event rowclick
57556          * Fires when a row is clicked
57557          * @param {Grid} this
57558          * @param {Number} rowIndex
57559          * @param {Roo.EventObject} e
57560          */
57561         "rowclick" : true,
57562         /**
57563          * @event rowdblclick
57564          * Fires when a row is double clicked
57565          * @param {Grid} this
57566          * @param {Number} rowIndex
57567          * @param {Roo.EventObject} e
57568          */
57569         "rowdblclick" : true,
57570         /**
57571          * @event headerclick
57572          * Fires when a header is clicked
57573          * @param {Grid} this
57574          * @param {Number} columnIndex
57575          * @param {Roo.EventObject} e
57576          */
57577         "headerclick" : true,
57578         /**
57579          * @event headerdblclick
57580          * Fires when a header cell is double clicked
57581          * @param {Grid} this
57582          * @param {Number} columnIndex
57583          * @param {Roo.EventObject} e
57584          */
57585         "headerdblclick" : true,
57586         /**
57587          * @event rowcontextmenu
57588          * Fires when a row is right clicked
57589          * @param {Grid} this
57590          * @param {Number} rowIndex
57591          * @param {Roo.EventObject} e
57592          */
57593         "rowcontextmenu" : true,
57594         /**
57595          * @event cellcontextmenu
57596          * Fires when a cell is right clicked
57597          * @param {Grid} this
57598          * @param {Number} rowIndex
57599          * @param {Number} cellIndex
57600          * @param {Roo.EventObject} e
57601          */
57602          "cellcontextmenu" : true,
57603         /**
57604          * @event headercontextmenu
57605          * Fires when a header is right clicked
57606          * @param {Grid} this
57607          * @param {Number} columnIndex
57608          * @param {Roo.EventObject} e
57609          */
57610         "headercontextmenu" : true,
57611         /**
57612          * @event bodyscroll
57613          * Fires when the body element is scrolled
57614          * @param {Number} scrollLeft
57615          * @param {Number} scrollTop
57616          */
57617         "bodyscroll" : true,
57618         /**
57619          * @event columnresize
57620          * Fires when the user resizes a column
57621          * @param {Number} columnIndex
57622          * @param {Number} newSize
57623          */
57624         "columnresize" : true,
57625         /**
57626          * @event columnmove
57627          * Fires when the user moves a column
57628          * @param {Number} oldIndex
57629          * @param {Number} newIndex
57630          */
57631         "columnmove" : true,
57632         /**
57633          * @event startdrag
57634          * Fires when row(s) start being dragged
57635          * @param {Grid} this
57636          * @param {Roo.GridDD} dd The drag drop object
57637          * @param {event} e The raw browser event
57638          */
57639         "startdrag" : true,
57640         /**
57641          * @event enddrag
57642          * Fires when a drag operation is complete
57643          * @param {Grid} this
57644          * @param {Roo.GridDD} dd The drag drop object
57645          * @param {event} e The raw browser event
57646          */
57647         "enddrag" : true,
57648         /**
57649          * @event dragdrop
57650          * Fires when dragged row(s) are dropped on a valid DD target
57651          * @param {Grid} this
57652          * @param {Roo.GridDD} dd The drag drop object
57653          * @param {String} targetId The target drag drop object
57654          * @param {event} e The raw browser event
57655          */
57656         "dragdrop" : true,
57657         /**
57658          * @event dragover
57659          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57660          * @param {Grid} this
57661          * @param {Roo.GridDD} dd The drag drop object
57662          * @param {String} targetId The target drag drop object
57663          * @param {event} e The raw browser event
57664          */
57665         "dragover" : true,
57666         /**
57667          * @event dragenter
57668          *  Fires when the dragged row(s) first cross another DD target while being dragged
57669          * @param {Grid} this
57670          * @param {Roo.GridDD} dd The drag drop object
57671          * @param {String} targetId The target drag drop object
57672          * @param {event} e The raw browser event
57673          */
57674         "dragenter" : true,
57675         /**
57676          * @event dragout
57677          * Fires when the dragged row(s) leave another DD target while being dragged
57678          * @param {Grid} this
57679          * @param {Roo.GridDD} dd The drag drop object
57680          * @param {String} targetId The target drag drop object
57681          * @param {event} e The raw browser event
57682          */
57683         "dragout" : true,
57684         /**
57685          * @event rowclass
57686          * Fires when a row is rendered, so you can change add a style to it.
57687          * @param {GridView} gridview   The grid view
57688          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57689          */
57690         'rowclass' : true,
57691
57692         /**
57693          * @event render
57694          * Fires when the grid is rendered
57695          * @param {Grid} grid
57696          */
57697         'render' : true,
57698             /**
57699              * @event select
57700              * Fires when a date is selected
57701              * @param {DatePicker} this
57702              * @param {Date} date The selected date
57703              */
57704         'select': true,
57705         /**
57706              * @event monthchange
57707              * Fires when the displayed month changes 
57708              * @param {DatePicker} this
57709              * @param {Date} date The selected month
57710              */
57711         'monthchange': true,
57712         /**
57713              * @event evententer
57714              * Fires when mouse over an event
57715              * @param {Calendar} this
57716              * @param {event} Event
57717              */
57718         'evententer': true,
57719         /**
57720              * @event eventleave
57721              * Fires when the mouse leaves an
57722              * @param {Calendar} this
57723              * @param {event}
57724              */
57725         'eventleave': true,
57726         /**
57727              * @event eventclick
57728              * Fires when the mouse click an
57729              * @param {Calendar} this
57730              * @param {event}
57731              */
57732         'eventclick': true,
57733         /**
57734              * @event eventrender
57735              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
57736              * @param {Calendar} this
57737              * @param {data} data to be modified
57738              */
57739         'eventrender': true
57740         
57741     });
57742
57743     Roo.grid.Grid.superclass.constructor.call(this);
57744     this.on('render', function() {
57745         this.view.el.addClass('x-grid-cal'); 
57746         
57747         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
57748
57749     },this);
57750     
57751     if (!Roo.grid.Calendar.style) {
57752         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
57753             
57754             
57755             '.x-grid-cal .x-grid-col' :  {
57756                 height: 'auto !important',
57757                 'vertical-align': 'top'
57758             },
57759             '.x-grid-cal  .fc-event-hori' : {
57760                 height: '14px'
57761             }
57762              
57763             
57764         }, Roo.id());
57765     }
57766
57767     
57768     
57769 };
57770 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
57771     /**
57772      * @cfg {Store} eventStore The store that loads events.
57773      */
57774     eventStore : 25,
57775
57776      
57777     activeDate : false,
57778     startDay : 0,
57779     autoWidth : true,
57780     monitorWindowResize : false,
57781
57782     
57783     resizeColumns : function() {
57784         var col = (this.view.el.getWidth() / 7) - 3;
57785         // loop through cols, and setWidth
57786         for(var i =0 ; i < 7 ; i++){
57787             this.cm.setColumnWidth(i, col);
57788         }
57789     },
57790      setDate :function(date) {
57791         
57792         Roo.log('setDate?');
57793         
57794         this.resizeColumns();
57795         var vd = this.activeDate;
57796         this.activeDate = date;
57797 //        if(vd && this.el){
57798 //            var t = date.getTime();
57799 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
57800 //                Roo.log('using add remove');
57801 //                
57802 //                this.fireEvent('monthchange', this, date);
57803 //                
57804 //                this.cells.removeClass("fc-state-highlight");
57805 //                this.cells.each(function(c){
57806 //                   if(c.dateValue == t){
57807 //                       c.addClass("fc-state-highlight");
57808 //                       setTimeout(function(){
57809 //                            try{c.dom.firstChild.focus();}catch(e){}
57810 //                       }, 50);
57811 //                       return false;
57812 //                   }
57813 //                   return true;
57814 //                });
57815 //                return;
57816 //            }
57817 //        }
57818         
57819         var days = date.getDaysInMonth();
57820         
57821         var firstOfMonth = date.getFirstDateOfMonth();
57822         var startingPos = firstOfMonth.getDay()-this.startDay;
57823         
57824         if(startingPos < this.startDay){
57825             startingPos += 7;
57826         }
57827         
57828         var pm = date.add(Date.MONTH, -1);
57829         var prevStart = pm.getDaysInMonth()-startingPos;
57830 //        
57831         
57832         
57833         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57834         
57835         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57836         //this.cells.addClassOnOver('fc-state-hover');
57837         
57838         var cells = this.cells.elements;
57839         var textEls = this.textNodes;
57840         
57841         //Roo.each(cells, function(cell){
57842         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57843         //});
57844         
57845         days += startingPos;
57846
57847         // convert everything to numbers so it's fast
57848         var day = 86400000;
57849         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57850         //Roo.log(d);
57851         //Roo.log(pm);
57852         //Roo.log(prevStart);
57853         
57854         var today = new Date().clearTime().getTime();
57855         var sel = date.clearTime().getTime();
57856         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57857         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57858         var ddMatch = this.disabledDatesRE;
57859         var ddText = this.disabledDatesText;
57860         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57861         var ddaysText = this.disabledDaysText;
57862         var format = this.format;
57863         
57864         var setCellClass = function(cal, cell){
57865             
57866             //Roo.log('set Cell Class');
57867             cell.title = "";
57868             var t = d.getTime();
57869             
57870             //Roo.log(d);
57871             
57872             
57873             cell.dateValue = t;
57874             if(t == today){
57875                 cell.className += " fc-today";
57876                 cell.className += " fc-state-highlight";
57877                 cell.title = cal.todayText;
57878             }
57879             if(t == sel){
57880                 // disable highlight in other month..
57881                 cell.className += " fc-state-highlight";
57882                 
57883             }
57884             // disabling
57885             if(t < min) {
57886                 //cell.className = " fc-state-disabled";
57887                 cell.title = cal.minText;
57888                 return;
57889             }
57890             if(t > max) {
57891                 //cell.className = " fc-state-disabled";
57892                 cell.title = cal.maxText;
57893                 return;
57894             }
57895             if(ddays){
57896                 if(ddays.indexOf(d.getDay()) != -1){
57897                     // cell.title = ddaysText;
57898                    // cell.className = " fc-state-disabled";
57899                 }
57900             }
57901             if(ddMatch && format){
57902                 var fvalue = d.dateFormat(format);
57903                 if(ddMatch.test(fvalue)){
57904                     cell.title = ddText.replace("%0", fvalue);
57905                    cell.className = " fc-state-disabled";
57906                 }
57907             }
57908             
57909             if (!cell.initialClassName) {
57910                 cell.initialClassName = cell.dom.className;
57911             }
57912             
57913             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57914         };
57915
57916         var i = 0;
57917         
57918         for(; i < startingPos; i++) {
57919             cells[i].dayName =  (++prevStart);
57920             Roo.log(textEls[i]);
57921             d.setDate(d.getDate()+1);
57922             
57923             //cells[i].className = "fc-past fc-other-month";
57924             setCellClass(this, cells[i]);
57925         }
57926         
57927         var intDay = 0;
57928         
57929         for(; i < days; i++){
57930             intDay = i - startingPos + 1;
57931             cells[i].dayName =  (intDay);
57932             d.setDate(d.getDate()+1);
57933             
57934             cells[i].className = ''; // "x-date-active";
57935             setCellClass(this, cells[i]);
57936         }
57937         var extraDays = 0;
57938         
57939         for(; i < 42; i++) {
57940             //textEls[i].innerHTML = (++extraDays);
57941             
57942             d.setDate(d.getDate()+1);
57943             cells[i].dayName = (++extraDays);
57944             cells[i].className = "fc-future fc-other-month";
57945             setCellClass(this, cells[i]);
57946         }
57947         
57948         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57949         
57950         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57951         
57952         // this will cause all the cells to mis
57953         var rows= [];
57954         var i =0;
57955         for (var r = 0;r < 6;r++) {
57956             for (var c =0;c < 7;c++) {
57957                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57958             }    
57959         }
57960         
57961         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57962         for(i=0;i<cells.length;i++) {
57963             
57964             this.cells.elements[i].dayName = cells[i].dayName ;
57965             this.cells.elements[i].className = cells[i].className;
57966             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57967             this.cells.elements[i].title = cells[i].title ;
57968             this.cells.elements[i].dateValue = cells[i].dateValue ;
57969         }
57970         
57971         
57972         
57973         
57974         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57975         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57976         
57977         ////if(totalRows != 6){
57978             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57979            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57980        // }
57981         
57982         this.fireEvent('monthchange', this, date);
57983         
57984         
57985     },
57986  /**
57987      * Returns the grid's SelectionModel.
57988      * @return {SelectionModel}
57989      */
57990     getSelectionModel : function(){
57991         if(!this.selModel){
57992             this.selModel = new Roo.grid.CellSelectionModel();
57993         }
57994         return this.selModel;
57995     },
57996
57997     load: function() {
57998         this.eventStore.load()
57999         
58000         
58001         
58002     },
58003     
58004     findCell : function(dt) {
58005         dt = dt.clearTime().getTime();
58006         var ret = false;
58007         this.cells.each(function(c){
58008             //Roo.log("check " +c.dateValue + '?=' + dt);
58009             if(c.dateValue == dt){
58010                 ret = c;
58011                 return false;
58012             }
58013             return true;
58014         });
58015         
58016         return ret;
58017     },
58018     
58019     findCells : function(rec) {
58020         var s = rec.data.start_dt.clone().clearTime().getTime();
58021        // Roo.log(s);
58022         var e= rec.data.end_dt.clone().clearTime().getTime();
58023        // Roo.log(e);
58024         var ret = [];
58025         this.cells.each(function(c){
58026              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
58027             
58028             if(c.dateValue > e){
58029                 return ;
58030             }
58031             if(c.dateValue < s){
58032                 return ;
58033             }
58034             ret.push(c);
58035         });
58036         
58037         return ret;    
58038     },
58039     
58040     findBestRow: function(cells)
58041     {
58042         var ret = 0;
58043         
58044         for (var i =0 ; i < cells.length;i++) {
58045             ret  = Math.max(cells[i].rows || 0,ret);
58046         }
58047         return ret;
58048         
58049     },
58050     
58051     
58052     addItem : function(rec)
58053     {
58054         // look for vertical location slot in
58055         var cells = this.findCells(rec);
58056         
58057         rec.row = this.findBestRow(cells);
58058         
58059         // work out the location.
58060         
58061         var crow = false;
58062         var rows = [];
58063         for(var i =0; i < cells.length; i++) {
58064             if (!crow) {
58065                 crow = {
58066                     start : cells[i],
58067                     end :  cells[i]
58068                 };
58069                 continue;
58070             }
58071             if (crow.start.getY() == cells[i].getY()) {
58072                 // on same row.
58073                 crow.end = cells[i];
58074                 continue;
58075             }
58076             // different row.
58077             rows.push(crow);
58078             crow = {
58079                 start: cells[i],
58080                 end : cells[i]
58081             };
58082             
58083         }
58084         
58085         rows.push(crow);
58086         rec.els = [];
58087         rec.rows = rows;
58088         rec.cells = cells;
58089         for (var i = 0; i < cells.length;i++) {
58090             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
58091             
58092         }
58093         
58094         
58095     },
58096     
58097     clearEvents: function() {
58098         
58099         if (!this.eventStore.getCount()) {
58100             return;
58101         }
58102         // reset number of rows in cells.
58103         Roo.each(this.cells.elements, function(c){
58104             c.rows = 0;
58105         });
58106         
58107         this.eventStore.each(function(e) {
58108             this.clearEvent(e);
58109         },this);
58110         
58111     },
58112     
58113     clearEvent : function(ev)
58114     {
58115         if (ev.els) {
58116             Roo.each(ev.els, function(el) {
58117                 el.un('mouseenter' ,this.onEventEnter, this);
58118                 el.un('mouseleave' ,this.onEventLeave, this);
58119                 el.remove();
58120             },this);
58121             ev.els = [];
58122         }
58123     },
58124     
58125     
58126     renderEvent : function(ev,ctr) {
58127         if (!ctr) {
58128              ctr = this.view.el.select('.fc-event-container',true).first();
58129         }
58130         
58131          
58132         this.clearEvent(ev);
58133             //code
58134        
58135         
58136         
58137         ev.els = [];
58138         var cells = ev.cells;
58139         var rows = ev.rows;
58140         this.fireEvent('eventrender', this, ev);
58141         
58142         for(var i =0; i < rows.length; i++) {
58143             
58144             cls = '';
58145             if (i == 0) {
58146                 cls += ' fc-event-start';
58147             }
58148             if ((i+1) == rows.length) {
58149                 cls += ' fc-event-end';
58150             }
58151             
58152             //Roo.log(ev.data);
58153             // how many rows should it span..
58154             var cg = this.eventTmpl.append(ctr,Roo.apply({
58155                 fccls : cls
58156                 
58157             }, ev.data) , true);
58158             
58159             
58160             cg.on('mouseenter' ,this.onEventEnter, this, ev);
58161             cg.on('mouseleave' ,this.onEventLeave, this, ev);
58162             cg.on('click', this.onEventClick, this, ev);
58163             
58164             ev.els.push(cg);
58165             
58166             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
58167             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
58168             //Roo.log(cg);
58169              
58170             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
58171             cg.setWidth(ebox.right - sbox.x -2);
58172         }
58173     },
58174     
58175     renderEvents: function()
58176     {   
58177         // first make sure there is enough space..
58178         
58179         if (!this.eventTmpl) {
58180             this.eventTmpl = new Roo.Template(
58181                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
58182                     '<div class="fc-event-inner">' +
58183                         '<span class="fc-event-time">{time}</span>' +
58184                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
58185                     '</div>' +
58186                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
58187                 '</div>'
58188             );
58189                 
58190         }
58191                
58192         
58193         
58194         this.cells.each(function(c) {
58195             //Roo.log(c.select('.fc-day-content div',true).first());
58196             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
58197         });
58198         
58199         var ctr = this.view.el.select('.fc-event-container',true).first();
58200         
58201         var cls;
58202         this.eventStore.each(function(ev){
58203             
58204             this.renderEvent(ev);
58205              
58206              
58207         }, this);
58208         this.view.layout();
58209         
58210     },
58211     
58212     onEventEnter: function (e, el,event,d) {
58213         this.fireEvent('evententer', this, el, event);
58214     },
58215     
58216     onEventLeave: function (e, el,event,d) {
58217         this.fireEvent('eventleave', this, el, event);
58218     },
58219     
58220     onEventClick: function (e, el,event,d) {
58221         this.fireEvent('eventclick', this, el, event);
58222     },
58223     
58224     onMonthChange: function () {
58225         this.store.load();
58226     },
58227     
58228     onLoad: function () {
58229         
58230         //Roo.log('calendar onload');
58231 //         
58232         if(this.eventStore.getCount() > 0){
58233             
58234            
58235             
58236             this.eventStore.each(function(d){
58237                 
58238                 
58239                 // FIXME..
58240                 var add =   d.data;
58241                 if (typeof(add.end_dt) == 'undefined')  {
58242                     Roo.log("Missing End time in calendar data: ");
58243                     Roo.log(d);
58244                     return;
58245                 }
58246                 if (typeof(add.start_dt) == 'undefined')  {
58247                     Roo.log("Missing Start time in calendar data: ");
58248                     Roo.log(d);
58249                     return;
58250                 }
58251                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
58252                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
58253                 add.id = add.id || d.id;
58254                 add.title = add.title || '??';
58255                 
58256                 this.addItem(d);
58257                 
58258              
58259             },this);
58260         }
58261         
58262         this.renderEvents();
58263     }
58264     
58265
58266 });
58267 /*
58268  grid : {
58269                 xtype: 'Grid',
58270                 xns: Roo.grid,
58271                 listeners : {
58272                     render : function ()
58273                     {
58274                         _this.grid = this;
58275                         
58276                         if (!this.view.el.hasClass('course-timesheet')) {
58277                             this.view.el.addClass('course-timesheet');
58278                         }
58279                         if (this.tsStyle) {
58280                             this.ds.load({});
58281                             return; 
58282                         }
58283                         Roo.log('width');
58284                         Roo.log(_this.grid.view.el.getWidth());
58285                         
58286                         
58287                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
58288                             '.course-timesheet .x-grid-row' : {
58289                                 height: '80px'
58290                             },
58291                             '.x-grid-row td' : {
58292                                 'vertical-align' : 0
58293                             },
58294                             '.course-edit-link' : {
58295                                 'color' : 'blue',
58296                                 'text-overflow' : 'ellipsis',
58297                                 'overflow' : 'hidden',
58298                                 'white-space' : 'nowrap',
58299                                 'cursor' : 'pointer'
58300                             },
58301                             '.sub-link' : {
58302                                 'color' : 'green'
58303                             },
58304                             '.de-act-sup-link' : {
58305                                 'color' : 'purple',
58306                                 'text-decoration' : 'line-through'
58307                             },
58308                             '.de-act-link' : {
58309                                 'color' : 'red',
58310                                 'text-decoration' : 'line-through'
58311                             },
58312                             '.course-timesheet .course-highlight' : {
58313                                 'border-top-style': 'dashed !important',
58314                                 'border-bottom-bottom': 'dashed !important'
58315                             },
58316                             '.course-timesheet .course-item' : {
58317                                 'font-family'   : 'tahoma, arial, helvetica',
58318                                 'font-size'     : '11px',
58319                                 'overflow'      : 'hidden',
58320                                 'padding-left'  : '10px',
58321                                 'padding-right' : '10px',
58322                                 'padding-top' : '10px' 
58323                             }
58324                             
58325                         }, Roo.id());
58326                                 this.ds.load({});
58327                     }
58328                 },
58329                 autoWidth : true,
58330                 monitorWindowResize : false,
58331                 cellrenderer : function(v,x,r)
58332                 {
58333                     return v;
58334                 },
58335                 sm : {
58336                     xtype: 'CellSelectionModel',
58337                     xns: Roo.grid
58338                 },
58339                 dataSource : {
58340                     xtype: 'Store',
58341                     xns: Roo.data,
58342                     listeners : {
58343                         beforeload : function (_self, options)
58344                         {
58345                             options.params = options.params || {};
58346                             options.params._month = _this.monthField.getValue();
58347                             options.params.limit = 9999;
58348                             options.params['sort'] = 'when_dt';    
58349                             options.params['dir'] = 'ASC';    
58350                             this.proxy.loadResponse = this.loadResponse;
58351                             Roo.log("load?");
58352                             //this.addColumns();
58353                         },
58354                         load : function (_self, records, options)
58355                         {
58356                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
58357                                 // if you click on the translation.. you can edit it...
58358                                 var el = Roo.get(this);
58359                                 var id = el.dom.getAttribute('data-id');
58360                                 var d = el.dom.getAttribute('data-date');
58361                                 var t = el.dom.getAttribute('data-time');
58362                                 //var id = this.child('span').dom.textContent;
58363                                 
58364                                 //Roo.log(this);
58365                                 Pman.Dialog.CourseCalendar.show({
58366                                     id : id,
58367                                     when_d : d,
58368                                     when_t : t,
58369                                     productitem_active : id ? 1 : 0
58370                                 }, function() {
58371                                     _this.grid.ds.load({});
58372                                 });
58373                            
58374                            });
58375                            
58376                            _this.panel.fireEvent('resize', [ '', '' ]);
58377                         }
58378                     },
58379                     loadResponse : function(o, success, response){
58380                             // this is overridden on before load..
58381                             
58382                             Roo.log("our code?");       
58383                             //Roo.log(success);
58384                             //Roo.log(response)
58385                             delete this.activeRequest;
58386                             if(!success){
58387                                 this.fireEvent("loadexception", this, o, response);
58388                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58389                                 return;
58390                             }
58391                             var result;
58392                             try {
58393                                 result = o.reader.read(response);
58394                             }catch(e){
58395                                 Roo.log("load exception?");
58396                                 this.fireEvent("loadexception", this, o, response, e);
58397                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58398                                 return;
58399                             }
58400                             Roo.log("ready...");        
58401                             // loop through result.records;
58402                             // and set this.tdate[date] = [] << array of records..
58403                             _this.tdata  = {};
58404                             Roo.each(result.records, function(r){
58405                                 //Roo.log(r.data);
58406                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
58407                                     _this.tdata[r.data.when_dt.format('j')] = [];
58408                                 }
58409                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
58410                             });
58411                             
58412                             //Roo.log(_this.tdata);
58413                             
58414                             result.records = [];
58415                             result.totalRecords = 6;
58416                     
58417                             // let's generate some duumy records for the rows.
58418                             //var st = _this.dateField.getValue();
58419                             
58420                             // work out monday..
58421                             //st = st.add(Date.DAY, -1 * st.format('w'));
58422                             
58423                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58424                             
58425                             var firstOfMonth = date.getFirstDayOfMonth();
58426                             var days = date.getDaysInMonth();
58427                             var d = 1;
58428                             var firstAdded = false;
58429                             for (var i = 0; i < result.totalRecords ; i++) {
58430                                 //var d= st.add(Date.DAY, i);
58431                                 var row = {};
58432                                 var added = 0;
58433                                 for(var w = 0 ; w < 7 ; w++){
58434                                     if(!firstAdded && firstOfMonth != w){
58435                                         continue;
58436                                     }
58437                                     if(d > days){
58438                                         continue;
58439                                     }
58440                                     firstAdded = true;
58441                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
58442                                     row['weekday'+w] = String.format(
58443                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
58444                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
58445                                                     d,
58446                                                     date.format('Y-m-')+dd
58447                                                 );
58448                                     added++;
58449                                     if(typeof(_this.tdata[d]) != 'undefined'){
58450                                         Roo.each(_this.tdata[d], function(r){
58451                                             var is_sub = '';
58452                                             var deactive = '';
58453                                             var id = r.id;
58454                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
58455                                             if(r.parent_id*1>0){
58456                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
58457                                                 id = r.parent_id;
58458                                             }
58459                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
58460                                                 deactive = 'de-act-link';
58461                                             }
58462                                             
58463                                             row['weekday'+w] += String.format(
58464                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
58465                                                     id, //0
58466                                                     r.product_id_name, //1
58467                                                     r.when_dt.format('h:ia'), //2
58468                                                     is_sub, //3
58469                                                     deactive, //4
58470                                                     desc // 5
58471                                             );
58472                                         });
58473                                     }
58474                                     d++;
58475                                 }
58476                                 
58477                                 // only do this if something added..
58478                                 if(added > 0){ 
58479                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
58480                                 }
58481                                 
58482                                 
58483                                 // push it twice. (second one with an hour..
58484                                 
58485                             }
58486                             //Roo.log(result);
58487                             this.fireEvent("load", this, o, o.request.arg);
58488                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
58489                         },
58490                     sortInfo : {field: 'when_dt', direction : 'ASC' },
58491                     proxy : {
58492                         xtype: 'HttpProxy',
58493                         xns: Roo.data,
58494                         method : 'GET',
58495                         url : baseURL + '/Roo/Shop_course.php'
58496                     },
58497                     reader : {
58498                         xtype: 'JsonReader',
58499                         xns: Roo.data,
58500                         id : 'id',
58501                         fields : [
58502                             {
58503                                 'name': 'id',
58504                                 'type': 'int'
58505                             },
58506                             {
58507                                 'name': 'when_dt',
58508                                 'type': 'string'
58509                             },
58510                             {
58511                                 'name': 'end_dt',
58512                                 'type': 'string'
58513                             },
58514                             {
58515                                 'name': 'parent_id',
58516                                 'type': 'int'
58517                             },
58518                             {
58519                                 'name': 'product_id',
58520                                 'type': 'int'
58521                             },
58522                             {
58523                                 'name': 'productitem_id',
58524                                 'type': 'int'
58525                             },
58526                             {
58527                                 'name': 'guid',
58528                                 'type': 'int'
58529                             }
58530                         ]
58531                     }
58532                 },
58533                 toolbar : {
58534                     xtype: 'Toolbar',
58535                     xns: Roo,
58536                     items : [
58537                         {
58538                             xtype: 'Button',
58539                             xns: Roo.Toolbar,
58540                             listeners : {
58541                                 click : function (_self, e)
58542                                 {
58543                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58544                                     sd.setMonth(sd.getMonth()-1);
58545                                     _this.monthField.setValue(sd.format('Y-m-d'));
58546                                     _this.grid.ds.load({});
58547                                 }
58548                             },
58549                             text : "Back"
58550                         },
58551                         {
58552                             xtype: 'Separator',
58553                             xns: Roo.Toolbar
58554                         },
58555                         {
58556                             xtype: 'MonthField',
58557                             xns: Roo.form,
58558                             listeners : {
58559                                 render : function (_self)
58560                                 {
58561                                     _this.monthField = _self;
58562                                    // _this.monthField.set  today
58563                                 },
58564                                 select : function (combo, date)
58565                                 {
58566                                     _this.grid.ds.load({});
58567                                 }
58568                             },
58569                             value : (function() { return new Date(); })()
58570                         },
58571                         {
58572                             xtype: 'Separator',
58573                             xns: Roo.Toolbar
58574                         },
58575                         {
58576                             xtype: 'TextItem',
58577                             xns: Roo.Toolbar,
58578                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
58579                         },
58580                         {
58581                             xtype: 'Fill',
58582                             xns: Roo.Toolbar
58583                         },
58584                         {
58585                             xtype: 'Button',
58586                             xns: Roo.Toolbar,
58587                             listeners : {
58588                                 click : function (_self, e)
58589                                 {
58590                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58591                                     sd.setMonth(sd.getMonth()+1);
58592                                     _this.monthField.setValue(sd.format('Y-m-d'));
58593                                     _this.grid.ds.load({});
58594                                 }
58595                             },
58596                             text : "Next"
58597                         }
58598                     ]
58599                 },
58600                  
58601             }
58602         };
58603         
58604         *//*
58605  * Based on:
58606  * Ext JS Library 1.1.1
58607  * Copyright(c) 2006-2007, Ext JS, LLC.
58608  *
58609  * Originally Released Under LGPL - original licence link has changed is not relivant.
58610  *
58611  * Fork - LGPL
58612  * <script type="text/javascript">
58613  */
58614  
58615 /**
58616  * @class Roo.LoadMask
58617  * A simple utility class for generically masking elements while loading data.  If the element being masked has
58618  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
58619  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
58620  * element's UpdateManager load indicator and will be destroyed after the initial load.
58621  * @constructor
58622  * Create a new LoadMask
58623  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
58624  * @param {Object} config The config object
58625  */
58626 Roo.LoadMask = function(el, config){
58627     this.el = Roo.get(el);
58628     Roo.apply(this, config);
58629     if(this.store){
58630         this.store.on('beforeload', this.onBeforeLoad, this);
58631         this.store.on('load', this.onLoad, this);
58632         this.store.on('loadexception', this.onLoadException, this);
58633         this.removeMask = false;
58634     }else{
58635         var um = this.el.getUpdateManager();
58636         um.showLoadIndicator = false; // disable the default indicator
58637         um.on('beforeupdate', this.onBeforeLoad, this);
58638         um.on('update', this.onLoad, this);
58639         um.on('failure', this.onLoad, this);
58640         this.removeMask = true;
58641     }
58642 };
58643
58644 Roo.LoadMask.prototype = {
58645     /**
58646      * @cfg {Boolean} removeMask
58647      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
58648      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
58649      */
58650     /**
58651      * @cfg {String} msg
58652      * The text to display in a centered loading message box (defaults to 'Loading...')
58653      */
58654     msg : 'Loading...',
58655     /**
58656      * @cfg {String} msgCls
58657      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
58658      */
58659     msgCls : 'x-mask-loading',
58660
58661     /**
58662      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
58663      * @type Boolean
58664      */
58665     disabled: false,
58666
58667     /**
58668      * Disables the mask to prevent it from being displayed
58669      */
58670     disable : function(){
58671        this.disabled = true;
58672     },
58673
58674     /**
58675      * Enables the mask so that it can be displayed
58676      */
58677     enable : function(){
58678         this.disabled = false;
58679     },
58680     
58681     onLoadException : function()
58682     {
58683         Roo.log(arguments);
58684         
58685         if (typeof(arguments[3]) != 'undefined') {
58686             Roo.MessageBox.alert("Error loading",arguments[3]);
58687         } 
58688         /*
58689         try {
58690             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
58691                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
58692             }   
58693         } catch(e) {
58694             
58695         }
58696         */
58697     
58698         
58699         
58700         this.el.unmask(this.removeMask);
58701     },
58702     // private
58703     onLoad : function()
58704     {
58705         this.el.unmask(this.removeMask);
58706     },
58707
58708     // private
58709     onBeforeLoad : function(){
58710         if(!this.disabled){
58711             this.el.mask(this.msg, this.msgCls);
58712         }
58713     },
58714
58715     // private
58716     destroy : function(){
58717         if(this.store){
58718             this.store.un('beforeload', this.onBeforeLoad, this);
58719             this.store.un('load', this.onLoad, this);
58720             this.store.un('loadexception', this.onLoadException, this);
58721         }else{
58722             var um = this.el.getUpdateManager();
58723             um.un('beforeupdate', this.onBeforeLoad, this);
58724             um.un('update', this.onLoad, this);
58725             um.un('failure', this.onLoad, this);
58726         }
58727     }
58728 };/*
58729  * Based on:
58730  * Ext JS Library 1.1.1
58731  * Copyright(c) 2006-2007, Ext JS, LLC.
58732  *
58733  * Originally Released Under LGPL - original licence link has changed is not relivant.
58734  *
58735  * Fork - LGPL
58736  * <script type="text/javascript">
58737  */
58738
58739
58740 /**
58741  * @class Roo.XTemplate
58742  * @extends Roo.Template
58743  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
58744 <pre><code>
58745 var t = new Roo.XTemplate(
58746         '&lt;select name="{name}"&gt;',
58747                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
58748         '&lt;/select&gt;'
58749 );
58750  
58751 // then append, applying the master template values
58752  </code></pre>
58753  *
58754  * Supported features:
58755  *
58756  *  Tags:
58757
58758 <pre><code>
58759       {a_variable} - output encoded.
58760       {a_variable.format:("Y-m-d")} - call a method on the variable
58761       {a_variable:raw} - unencoded output
58762       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
58763       {a_variable:this.method_on_template(...)} - call a method on the template object.
58764  
58765 </code></pre>
58766  *  The tpl tag:
58767 <pre><code>
58768         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
58769         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
58770         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
58771         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
58772   
58773         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
58774         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
58775 </code></pre>
58776  *      
58777  */
58778 Roo.XTemplate = function()
58779 {
58780     Roo.XTemplate.superclass.constructor.apply(this, arguments);
58781     if (this.html) {
58782         this.compile();
58783     }
58784 };
58785
58786
58787 Roo.extend(Roo.XTemplate, Roo.Template, {
58788
58789     /**
58790      * The various sub templates
58791      */
58792     tpls : false,
58793     /**
58794      *
58795      * basic tag replacing syntax
58796      * WORD:WORD()
58797      *
58798      * // you can fake an object call by doing this
58799      *  x.t:(test,tesT) 
58800      * 
58801      */
58802     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
58803
58804     /**
58805      * compile the template
58806      *
58807      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
58808      *
58809      */
58810     compile: function()
58811     {
58812         var s = this.html;
58813      
58814         s = ['<tpl>', s, '</tpl>'].join('');
58815     
58816         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
58817             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
58818             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
58819             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
58820             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
58821             m,
58822             id     = 0,
58823             tpls   = [];
58824     
58825         while(true == !!(m = s.match(re))){
58826             var forMatch   = m[0].match(nameRe),
58827                 ifMatch   = m[0].match(ifRe),
58828                 execMatch   = m[0].match(execRe),
58829                 namedMatch   = m[0].match(namedRe),
58830                 
58831                 exp  = null, 
58832                 fn   = null,
58833                 exec = null,
58834                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58835                 
58836             if (ifMatch) {
58837                 // if - puts fn into test..
58838                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58839                 if(exp){
58840                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58841                 }
58842             }
58843             
58844             if (execMatch) {
58845                 // exec - calls a function... returns empty if true is  returned.
58846                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58847                 if(exp){
58848                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58849                 }
58850             }
58851             
58852             
58853             if (name) {
58854                 // for = 
58855                 switch(name){
58856                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58857                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58858                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58859                 }
58860             }
58861             var uid = namedMatch ? namedMatch[1] : id;
58862             
58863             
58864             tpls.push({
58865                 id:     namedMatch ? namedMatch[1] : id,
58866                 target: name,
58867                 exec:   exec,
58868                 test:   fn,
58869                 body:   m[1] || ''
58870             });
58871             if (namedMatch) {
58872                 s = s.replace(m[0], '');
58873             } else { 
58874                 s = s.replace(m[0], '{xtpl'+ id + '}');
58875             }
58876             ++id;
58877         }
58878         this.tpls = [];
58879         for(var i = tpls.length-1; i >= 0; --i){
58880             this.compileTpl(tpls[i]);
58881             this.tpls[tpls[i].id] = tpls[i];
58882         }
58883         this.master = tpls[tpls.length-1];
58884         return this;
58885     },
58886     /**
58887      * same as applyTemplate, except it's done to one of the subTemplates
58888      * when using named templates, you can do:
58889      *
58890      * var str = pl.applySubTemplate('your-name', values);
58891      *
58892      * 
58893      * @param {Number} id of the template
58894      * @param {Object} values to apply to template
58895      * @param {Object} parent (normaly the instance of this object)
58896      */
58897     applySubTemplate : function(id, values, parent)
58898     {
58899         
58900         
58901         var t = this.tpls[id];
58902         
58903         
58904         try { 
58905             if(t.test && !t.test.call(this, values, parent)){
58906                 return '';
58907             }
58908         } catch(e) {
58909             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58910             Roo.log(e.toString());
58911             Roo.log(t.test);
58912             return ''
58913         }
58914         try { 
58915             
58916             if(t.exec && t.exec.call(this, values, parent)){
58917                 return '';
58918             }
58919         } catch(e) {
58920             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58921             Roo.log(e.toString());
58922             Roo.log(t.exec);
58923             return ''
58924         }
58925         try {
58926             var vs = t.target ? t.target.call(this, values, parent) : values;
58927             parent = t.target ? values : parent;
58928             if(t.target && vs instanceof Array){
58929                 var buf = [];
58930                 for(var i = 0, len = vs.length; i < len; i++){
58931                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58932                 }
58933                 return buf.join('');
58934             }
58935             return t.compiled.call(this, vs, parent);
58936         } catch (e) {
58937             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58938             Roo.log(e.toString());
58939             Roo.log(t.compiled);
58940             return '';
58941         }
58942     },
58943
58944     compileTpl : function(tpl)
58945     {
58946         var fm = Roo.util.Format;
58947         var useF = this.disableFormats !== true;
58948         var sep = Roo.isGecko ? "+" : ",";
58949         var undef = function(str) {
58950             Roo.log("Property not found :"  + str);
58951             return '';
58952         };
58953         
58954         var fn = function(m, name, format, args)
58955         {
58956             //Roo.log(arguments);
58957             args = args ? args.replace(/\\'/g,"'") : args;
58958             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58959             if (typeof(format) == 'undefined') {
58960                 format= 'htmlEncode';
58961             }
58962             if (format == 'raw' ) {
58963                 format = false;
58964             }
58965             
58966             if(name.substr(0, 4) == 'xtpl'){
58967                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58968             }
58969             
58970             // build an array of options to determine if value is undefined..
58971             
58972             // basically get 'xxxx.yyyy' then do
58973             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58974             //    (function () { Roo.log("Property not found"); return ''; })() :
58975             //    ......
58976             
58977             var udef_ar = [];
58978             var lookfor = '';
58979             Roo.each(name.split('.'), function(st) {
58980                 lookfor += (lookfor.length ? '.': '') + st;
58981                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58982             });
58983             
58984             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58985             
58986             
58987             if(format && useF){
58988                 
58989                 args = args ? ',' + args : "";
58990                  
58991                 if(format.substr(0, 5) != "this."){
58992                     format = "fm." + format + '(';
58993                 }else{
58994                     format = 'this.call("'+ format.substr(5) + '", ';
58995                     args = ", values";
58996                 }
58997                 
58998                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58999             }
59000              
59001             if (args.length) {
59002                 // called with xxyx.yuu:(test,test)
59003                 // change to ()
59004                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
59005             }
59006             // raw.. - :raw modifier..
59007             return "'"+ sep + udef_st  + name + ")"+sep+"'";
59008             
59009         };
59010         var body;
59011         // branched to use + in gecko and [].join() in others
59012         if(Roo.isGecko){
59013             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
59014                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
59015                     "';};};";
59016         }else{
59017             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
59018             body.push(tpl.body.replace(/(\r\n|\n)/g,
59019                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
59020             body.push("'].join('');};};");
59021             body = body.join('');
59022         }
59023         
59024         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
59025        
59026         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
59027         eval(body);
59028         
59029         return this;
59030     },
59031
59032     applyTemplate : function(values){
59033         return this.master.compiled.call(this, values, {});
59034         //var s = this.subs;
59035     },
59036
59037     apply : function(){
59038         return this.applyTemplate.apply(this, arguments);
59039     }
59040
59041  });
59042
59043 Roo.XTemplate.from = function(el){
59044     el = Roo.getDom(el);
59045     return new Roo.XTemplate(el.value || el.innerHTML);
59046 };