sync
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isGecko = !isSafari && ua.indexOf("gecko") > -1,
61         isBorderBox = isIE && !isStrict,
62         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
63         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
64         isLinux = (ua.indexOf("linux") != -1),
65         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
66         isIOS = /iphone|ipad/.test(ua),
67         isTouch =  (function() {
68             try {  
69                 document.createEvent("TouchEvent");  
70                 return true;  
71             } catch (e) {  
72                 return false;  
73             } 
74             
75         })();
76     // remove css image flicker
77         if(isIE && !isIE7){
78         try{
79             document.execCommand("BackgroundImageCache", false, true);
80         }catch(e){}
81     }
82     
83     Roo.apply(Roo, {
84         /**
85          * True if the browser is in strict mode
86          * @type Boolean
87          */
88         isStrict : isStrict,
89         /**
90          * True if the page is running over SSL
91          * @type Boolean
92          */
93         isSecure : isSecure,
94         /**
95          * True when the document is fully initialized and ready for action
96          * @type Boolean
97          */
98         isReady : false,
99         /**
100          * Turn on debugging output (currently only the factory uses this)
101          * @type Boolean
102          */
103         
104         debug: false,
105
106         /**
107          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
108          * @type Boolean
109          */
110         enableGarbageCollector : true,
111
112         /**
113          * True to automatically purge event listeners after uncaching an element (defaults to false).
114          * Note: this only happens if enableGarbageCollector is true.
115          * @type Boolean
116          */
117         enableListenerCollection:false,
118
119         /**
120          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
121          * the IE insecure content warning (defaults to javascript:false).
122          * @type String
123          */
124         SSL_SECURE_URL : "javascript:false",
125
126         /**
127          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
128          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
129          * @type String
130          */
131         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
132
133         emptyFn : function(){},
134         
135         /**
136          * Copies all the properties of config to obj if they don't already exist.
137          * @param {Object} obj The receiver of the properties
138          * @param {Object} config The source of the properties
139          * @return {Object} returns obj
140          */
141         applyIf : function(o, c){
142             if(o && c){
143                 for(var p in c){
144                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
145                 }
146             }
147             return o;
148         },
149
150         /**
151          * Applies event listeners to elements by selectors when the document is ready.
152          * The event name is specified with an @ suffix.
153 <pre><code>
154 Roo.addBehaviors({
155    // add a listener for click on all anchors in element with id foo
156    '#foo a@click' : function(e, t){
157        // do something
158    },
159
160    // add the same listener to multiple selectors (separated by comma BEFORE the @)
161    '#foo a, #bar span.some-class@mouseover' : function(){
162        // do something
163    }
164 });
165 </code></pre>
166          * @param {Object} obj The list of behaviors to apply
167          */
168         addBehaviors : function(o){
169             if(!Roo.isReady){
170                 Roo.onReady(function(){
171                     Roo.addBehaviors(o);
172                 });
173                 return;
174             }
175             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
176             for(var b in o){
177                 var parts = b.split('@');
178                 if(parts[1]){ // for Object prototype breakers
179                     var s = parts[0];
180                     if(!cache[s]){
181                         cache[s] = Roo.select(s);
182                     }
183                     cache[s].on(parts[1], o[b]);
184                 }
185             }
186             cache = null;
187         },
188
189         /**
190          * Generates unique ids. If the element already has an id, it is unchanged
191          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
192          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
193          * @return {String} The generated Id.
194          */
195         id : function(el, prefix){
196             prefix = prefix || "roo-gen";
197             el = Roo.getDom(el);
198             var id = prefix + (++idSeed);
199             return el ? (el.id ? el.id : (el.id = id)) : id;
200         },
201          
202        
203         /**
204          * Extends one class with another class and optionally overrides members with the passed literal. This class
205          * also adds the function "override()" to the class that can be used to override
206          * members on an instance.
207          * @param {Object} subclass The class inheriting the functionality
208          * @param {Object} superclass The class being extended
209          * @param {Object} overrides (optional) A literal with members
210          * @method extend
211          */
212         extend : function(){
213             // inline overrides
214             var io = function(o){
215                 for(var m in o){
216                     this[m] = o[m];
217                 }
218             };
219             return function(sb, sp, overrides){
220                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
221                     overrides = sp;
222                     sp = sb;
223                     sb = function(){sp.apply(this, arguments);};
224                 }
225                 var F = function(){}, sbp, spp = sp.prototype;
226                 F.prototype = spp;
227                 sbp = sb.prototype = new F();
228                 sbp.constructor=sb;
229                 sb.superclass=spp;
230                 
231                 if(spp.constructor == Object.prototype.constructor){
232                     spp.constructor=sp;
233                    
234                 }
235                 
236                 sb.override = function(o){
237                     Roo.override(sb, o);
238                 };
239                 sbp.override = io;
240                 Roo.override(sb, overrides);
241                 return sb;
242             };
243         }(),
244
245         /**
246          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
247          * Usage:<pre><code>
248 Roo.override(MyClass, {
249     newMethod1: function(){
250         // etc.
251     },
252     newMethod2: function(foo){
253         // etc.
254     }
255 });
256  </code></pre>
257          * @param {Object} origclass The class to override
258          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
259          * containing one or more methods.
260          * @method override
261          */
262         override : function(origclass, overrides){
263             if(overrides){
264                 var p = origclass.prototype;
265                 for(var method in overrides){
266                     p[method] = overrides[method];
267                 }
268             }
269         },
270         /**
271          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
272          * <pre><code>
273 Roo.namespace('Company', 'Company.data');
274 Company.Widget = function() { ... }
275 Company.data.CustomStore = function(config) { ... }
276 </code></pre>
277          * @param {String} namespace1
278          * @param {String} namespace2
279          * @param {String} etc
280          * @method namespace
281          */
282         namespace : function(){
283             var a=arguments, o=null, i, j, d, rt;
284             for (i=0; i<a.length; ++i) {
285                 d=a[i].split(".");
286                 rt = d[0];
287                 /** eval:var:o */
288                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
289                 for (j=1; j<d.length; ++j) {
290                     o[d[j]]=o[d[j]] || {};
291                     o=o[d[j]];
292                 }
293             }
294         },
295         /**
296          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
297          * <pre><code>
298 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
299 Roo.factory(conf, Roo.data);
300 </code></pre>
301          * @param {String} classname
302          * @param {String} namespace (optional)
303          * @method factory
304          */
305          
306         factory : function(c, ns)
307         {
308             // no xtype, no ns or c.xns - or forced off by c.xns
309             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
310                 return c;
311             }
312             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
313             if (c.constructor == ns[c.xtype]) {// already created...
314                 return c;
315             }
316             if (ns[c.xtype]) {
317                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
318                 var ret = new ns[c.xtype](c);
319                 ret.xns = false;
320                 return ret;
321             }
322             c.xns = false; // prevent recursion..
323             return c;
324         },
325          /**
326          * Logs to console if it can.
327          *
328          * @param {String|Object} string
329          * @method log
330          */
331         log : function(s)
332         {
333             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
334                 return; // alerT?
335             }
336             console.log(s);
337             
338         },
339         /**
340          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
341          * @param {Object} o
342          * @return {String}
343          */
344         urlEncode : function(o){
345             if(!o){
346                 return "";
347             }
348             var buf = [];
349             for(var key in o){
350                 var ov = o[key], k = Roo.encodeURIComponent(key);
351                 var type = typeof ov;
352                 if(type == 'undefined'){
353                     buf.push(k, "=&");
354                 }else if(type != "function" && type != "object"){
355                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
356                 }else if(ov instanceof Array){
357                     if (ov.length) {
358                             for(var i = 0, len = ov.length; i < len; i++) {
359                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
360                             }
361                         } else {
362                             buf.push(k, "=&");
363                         }
364                 }
365             }
366             buf.pop();
367             return buf.join("");
368         },
369          /**
370          * Safe version of encodeURIComponent
371          * @param {String} data 
372          * @return {String} 
373          */
374         
375         encodeURIComponent : function (data)
376         {
377             try {
378                 return encodeURIComponent(data);
379             } catch(e) {} // should be an uri encode error.
380             
381             if (data == '' || data == null){
382                return '';
383             }
384             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
385             function nibble_to_hex(nibble){
386                 var chars = '0123456789ABCDEF';
387                 return chars.charAt(nibble);
388             }
389             data = data.toString();
390             var buffer = '';
391             for(var i=0; i<data.length; i++){
392                 var c = data.charCodeAt(i);
393                 var bs = new Array();
394                 if (c > 0x10000){
395                         // 4 bytes
396                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
397                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
398                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
399                     bs[3] = 0x80 | (c & 0x3F);
400                 }else if (c > 0x800){
401                          // 3 bytes
402                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
403                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
404                     bs[2] = 0x80 | (c & 0x3F);
405                 }else if (c > 0x80){
406                        // 2 bytes
407                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
408                     bs[1] = 0x80 | (c & 0x3F);
409                 }else{
410                         // 1 byte
411                     bs[0] = c;
412                 }
413                 for(var j=0; j<bs.length; j++){
414                     var b = bs[j];
415                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
416                             + nibble_to_hex(b &0x0F);
417                     buffer += '%'+hex;
418                }
419             }
420             return buffer;    
421              
422         },
423
424         /**
425          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
426          * @param {String} string
427          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
428          * @return {Object} A literal with members
429          */
430         urlDecode : function(string, overwrite){
431             if(!string || !string.length){
432                 return {};
433             }
434             var obj = {};
435             var pairs = string.split('&');
436             var pair, name, value;
437             for(var i = 0, len = pairs.length; i < len; i++){
438                 pair = pairs[i].split('=');
439                 name = decodeURIComponent(pair[0]);
440                 value = decodeURIComponent(pair[1]);
441                 if(overwrite !== true){
442                     if(typeof obj[name] == "undefined"){
443                         obj[name] = value;
444                     }else if(typeof obj[name] == "string"){
445                         obj[name] = [obj[name]];
446                         obj[name].push(value);
447                     }else{
448                         obj[name].push(value);
449                     }
450                 }else{
451                     obj[name] = value;
452                 }
453             }
454             return obj;
455         },
456
457         /**
458          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
459          * passed array is not really an array, your function is called once with it.
460          * The supplied function is called with (Object item, Number index, Array allItems).
461          * @param {Array/NodeList/Mixed} array
462          * @param {Function} fn
463          * @param {Object} scope
464          */
465         each : function(array, fn, scope){
466             if(typeof array.length == "undefined" || typeof array == "string"){
467                 array = [array];
468             }
469             for(var i = 0, len = array.length; i < len; i++){
470                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
471             }
472         },
473
474         // deprecated
475         combine : function(){
476             var as = arguments, l = as.length, r = [];
477             for(var i = 0; i < l; i++){
478                 var a = as[i];
479                 if(a instanceof Array){
480                     r = r.concat(a);
481                 }else if(a.length !== undefined && !a.substr){
482                     r = r.concat(Array.prototype.slice.call(a, 0));
483                 }else{
484                     r.push(a);
485                 }
486             }
487             return r;
488         },
489
490         /**
491          * Escapes the passed string for use in a regular expression
492          * @param {String} str
493          * @return {String}
494          */
495         escapeRe : function(s) {
496             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
497         },
498
499         // internal
500         callback : function(cb, scope, args, delay){
501             if(typeof cb == "function"){
502                 if(delay){
503                     cb.defer(delay, scope, args || []);
504                 }else{
505                     cb.apply(scope, args || []);
506                 }
507             }
508         },
509
510         /**
511          * Return the dom node for the passed string (id), dom node, or Roo.Element
512          * @param {String/HTMLElement/Roo.Element} el
513          * @return HTMLElement
514          */
515         getDom : function(el){
516             if(!el){
517                 return null;
518             }
519             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
520         },
521
522         /**
523         * Shorthand for {@link Roo.ComponentMgr#get}
524         * @param {String} id
525         * @return Roo.Component
526         */
527         getCmp : function(id){
528             return Roo.ComponentMgr.get(id);
529         },
530          
531         num : function(v, defaultValue){
532             if(typeof v != 'number'){
533                 return defaultValue;
534             }
535             return v;
536         },
537
538         destroy : function(){
539             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
540                 var as = a[i];
541                 if(as){
542                     if(as.dom){
543                         as.removeAllListeners();
544                         as.remove();
545                         continue;
546                     }
547                     if(typeof as.purgeListeners == 'function'){
548                         as.purgeListeners();
549                     }
550                     if(typeof as.destroy == 'function'){
551                         as.destroy();
552                     }
553                 }
554             }
555         },
556
557         // inpired by a similar function in mootools library
558         /**
559          * Returns the type of object that is passed in. If the object passed in is null or undefined it
560          * return false otherwise it returns one of the following values:<ul>
561          * <li><b>string</b>: If the object passed is a string</li>
562          * <li><b>number</b>: If the object passed is a number</li>
563          * <li><b>boolean</b>: If the object passed is a boolean value</li>
564          * <li><b>function</b>: If the object passed is a function reference</li>
565          * <li><b>object</b>: If the object passed is an object</li>
566          * <li><b>array</b>: If the object passed is an array</li>
567          * <li><b>regexp</b>: If the object passed is a regular expression</li>
568          * <li><b>element</b>: If the object passed is a DOM Element</li>
569          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
570          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
571          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
572          * @param {Mixed} object
573          * @return {String}
574          */
575         type : function(o){
576             if(o === undefined || o === null){
577                 return false;
578             }
579             if(o.htmlElement){
580                 return 'element';
581             }
582             var t = typeof o;
583             if(t == 'object' && o.nodeName) {
584                 switch(o.nodeType) {
585                     case 1: return 'element';
586                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
587                 }
588             }
589             if(t == 'object' || t == 'function') {
590                 switch(o.constructor) {
591                     case Array: return 'array';
592                     case RegExp: return 'regexp';
593                 }
594                 if(typeof o.length == 'number' && typeof o.item == 'function') {
595                     return 'nodelist';
596                 }
597             }
598             return t;
599         },
600
601         /**
602          * Returns true if the passed value is null, undefined or an empty string (optional).
603          * @param {Mixed} value The value to test
604          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
605          * @return {Boolean}
606          */
607         isEmpty : function(v, allowBlank){
608             return v === null || v === undefined || (!allowBlank ? v === '' : false);
609         },
610         
611         /** @type Boolean */
612         isOpera : isOpera,
613         /** @type Boolean */
614         isSafari : isSafari,
615         /** @type Boolean */
616         isFirefox : isFirefox,
617         /** @type Boolean */
618         isIE : isIE,
619         /** @type Boolean */
620         isIE7 : isIE7,
621         /** @type Boolean */
622         isIE11 : isIE11,
623         /** @type Boolean */
624         isGecko : isGecko,
625         /** @type Boolean */
626         isBorderBox : isBorderBox,
627         /** @type Boolean */
628         isWindows : isWindows,
629         /** @type Boolean */
630         isLinux : isLinux,
631         /** @type Boolean */
632         isMac : isMac,
633         /** @type Boolean */
634         isIOS : isIOS,
635         /** @type Boolean */
636         isTouch : isTouch,
637
638         /**
639          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
640          * you may want to set this to true.
641          * @type Boolean
642          */
643         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
644         
645         
646                 
647         /**
648          * Selects a single element as a Roo Element
649          * This is about as close as you can get to jQuery's $('do crazy stuff')
650          * @param {String} selector The selector/xpath query
651          * @param {Node} root (optional) The start of the query (defaults to document).
652          * @return {Roo.Element}
653          */
654         selectNode : function(selector, root) 
655         {
656             var node = Roo.DomQuery.selectNode(selector,root);
657             return node ? Roo.get(node) : new Roo.Element(false);
658         }
659         
660     });
661
662
663 })();
664
665 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
666                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
667                 "Roo.app", "Roo.ux",
668                 "Roo.bootstrap",
669                 "Roo.bootstrap.dash");
670 /*
671  * Based on:
672  * Ext JS Library 1.1.1
673  * Copyright(c) 2006-2007, Ext JS, LLC.
674  *
675  * Originally Released Under LGPL - original licence link has changed is not relivant.
676  *
677  * Fork - LGPL
678  * <script type="text/javascript">
679  */
680
681 (function() {    
682     // wrappedn so fnCleanup is not in global scope...
683     if(Roo.isIE) {
684         function fnCleanUp() {
685             var p = Function.prototype;
686             delete p.createSequence;
687             delete p.defer;
688             delete p.createDelegate;
689             delete p.createCallback;
690             delete p.createInterceptor;
691
692             window.detachEvent("onunload", fnCleanUp);
693         }
694         window.attachEvent("onunload", fnCleanUp);
695     }
696 })();
697
698
699 /**
700  * @class Function
701  * These functions are available on every Function object (any JavaScript function).
702  */
703 Roo.apply(Function.prototype, {
704      /**
705      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
706      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
707      * Will create a function that is bound to those 2 args.
708      * @return {Function} The new function
709     */
710     createCallback : function(/*args...*/){
711         // make args available, in function below
712         var args = arguments;
713         var method = this;
714         return function() {
715             return method.apply(window, args);
716         };
717     },
718
719     /**
720      * Creates a delegate (callback) that sets the scope to obj.
721      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
722      * Will create a function that is automatically scoped to this.
723      * @param {Object} obj (optional) The object for which the scope is set
724      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
725      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
726      *                                             if a number the args are inserted at the specified position
727      * @return {Function} The new function
728      */
729     createDelegate : function(obj, args, appendArgs){
730         var method = this;
731         return function() {
732             var callArgs = args || arguments;
733             if(appendArgs === true){
734                 callArgs = Array.prototype.slice.call(arguments, 0);
735                 callArgs = callArgs.concat(args);
736             }else if(typeof appendArgs == "number"){
737                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
738                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
739                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
740             }
741             return method.apply(obj || window, callArgs);
742         };
743     },
744
745     /**
746      * Calls this function after the number of millseconds specified.
747      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
748      * @param {Object} obj (optional) The object for which the scope is set
749      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
750      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
751      *                                             if a number the args are inserted at the specified position
752      * @return {Number} The timeout id that can be used with clearTimeout
753      */
754     defer : function(millis, obj, args, appendArgs){
755         var fn = this.createDelegate(obj, args, appendArgs);
756         if(millis){
757             return setTimeout(fn, millis);
758         }
759         fn();
760         return 0;
761     },
762     /**
763      * Create a combined function call sequence of the original function + the passed function.
764      * The resulting function returns the results of the original function.
765      * The passed fcn is called with the parameters of the original function
766      * @param {Function} fcn The function to sequence
767      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
768      * @return {Function} The new function
769      */
770     createSequence : function(fcn, scope){
771         if(typeof fcn != "function"){
772             return this;
773         }
774         var method = this;
775         return function() {
776             var retval = method.apply(this || window, arguments);
777             fcn.apply(scope || this || window, arguments);
778             return retval;
779         };
780     },
781
782     /**
783      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
784      * The resulting function returns the results of the original function.
785      * The passed fcn is called with the parameters of the original function.
786      * @addon
787      * @param {Function} fcn The function to call before the original
788      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
789      * @return {Function} The new function
790      */
791     createInterceptor : function(fcn, scope){
792         if(typeof fcn != "function"){
793             return this;
794         }
795         var method = this;
796         return function() {
797             fcn.target = this;
798             fcn.method = method;
799             if(fcn.apply(scope || this || window, arguments) === false){
800                 return;
801             }
802             return method.apply(this || window, arguments);
803         };
804     }
805 });
806 /*
807  * Based on:
808  * Ext JS Library 1.1.1
809  * Copyright(c) 2006-2007, Ext JS, LLC.
810  *
811  * Originally Released Under LGPL - original licence link has changed is not relivant.
812  *
813  * Fork - LGPL
814  * <script type="text/javascript">
815  */
816
817 Roo.applyIf(String, {
818     
819     /** @scope String */
820     
821     /**
822      * Escapes the passed string for ' and \
823      * @param {String} string The string to escape
824      * @return {String} The escaped string
825      * @static
826      */
827     escape : function(string) {
828         return string.replace(/('|\\)/g, "\\$1");
829     },
830
831     /**
832      * Pads the left side of a string with a specified character.  This is especially useful
833      * for normalizing number and date strings.  Example usage:
834      * <pre><code>
835 var s = String.leftPad('123', 5, '0');
836 // s now contains the string: '00123'
837 </code></pre>
838      * @param {String} string The original string
839      * @param {Number} size The total length of the output string
840      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
841      * @return {String} The padded string
842      * @static
843      */
844     leftPad : function (val, size, ch) {
845         var result = new String(val);
846         if(ch === null || ch === undefined || ch === '') {
847             ch = " ";
848         }
849         while (result.length < size) {
850             result = ch + result;
851         }
852         return result;
853     },
854
855     /**
856      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
857      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
858      * <pre><code>
859 var cls = 'my-class', text = 'Some text';
860 var s = String.format('<div class="{0}">{1}</div>', cls, text);
861 // s now contains the string: '<div class="my-class">Some text</div>'
862 </code></pre>
863      * @param {String} string The tokenized string to be formatted
864      * @param {String} value1 The value to replace token {0}
865      * @param {String} value2 Etc...
866      * @return {String} The formatted string
867      * @static
868      */
869     format : function(format){
870         var args = Array.prototype.slice.call(arguments, 1);
871         return format.replace(/\{(\d+)\}/g, function(m, i){
872             return Roo.util.Format.htmlEncode(args[i]);
873         });
874     }
875 });
876
877 /**
878  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
879  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
880  * they are already different, the first value passed in is returned.  Note that this method returns the new value
881  * but does not change the current string.
882  * <pre><code>
883 // alternate sort directions
884 sort = sort.toggle('ASC', 'DESC');
885
886 // instead of conditional logic:
887 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
888 </code></pre>
889  * @param {String} value The value to compare to the current string
890  * @param {String} other The new value to use if the string already equals the first value passed in
891  * @return {String} The new value
892  */
893  
894 String.prototype.toggle = function(value, other){
895     return this == value ? other : value;
896 };/*
897  * Based on:
898  * Ext JS Library 1.1.1
899  * Copyright(c) 2006-2007, Ext JS, LLC.
900  *
901  * Originally Released Under LGPL - original licence link has changed is not relivant.
902  *
903  * Fork - LGPL
904  * <script type="text/javascript">
905  */
906
907  /**
908  * @class Number
909  */
910 Roo.applyIf(Number.prototype, {
911     /**
912      * Checks whether or not the current number is within a desired range.  If the number is already within the
913      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
914      * exceeded.  Note that this method returns the constrained value but does not change the current number.
915      * @param {Number} min The minimum number in the range
916      * @param {Number} max The maximum number in the range
917      * @return {Number} The constrained value if outside the range, otherwise the current value
918      */
919     constrain : function(min, max){
920         return Math.min(Math.max(this, min), max);
921     }
922 });/*
923  * Based on:
924  * Ext JS Library 1.1.1
925  * Copyright(c) 2006-2007, Ext JS, LLC.
926  *
927  * Originally Released Under LGPL - original licence link has changed is not relivant.
928  *
929  * Fork - LGPL
930  * <script type="text/javascript">
931  */
932  /**
933  * @class Array
934  */
935 Roo.applyIf(Array.prototype, {
936     /**
937      * 
938      * Checks whether or not the specified object exists in the array.
939      * @param {Object} o The object to check for
940      * @return {Number} The index of o in the array (or -1 if it is not found)
941      */
942     indexOf : function(o){
943        for (var i = 0, len = this.length; i < len; i++){
944               if(this[i] == o) return i;
945        }
946            return -1;
947     },
948
949     /**
950      * Removes the specified object from the array.  If the object is not found nothing happens.
951      * @param {Object} o The object to remove
952      */
953     remove : function(o){
954        var index = this.indexOf(o);
955        if(index != -1){
956            this.splice(index, 1);
957        }
958     },
959     /**
960      * Map (JS 1.6 compatibility)
961      * @param {Function} function  to call
962      */
963     map : function(fun )
964     {
965         var len = this.length >>> 0;
966         if (typeof fun != "function")
967             throw new TypeError();
968
969         var res = new Array(len);
970         var thisp = arguments[1];
971         for (var i = 0; i < len; i++)
972         {
973             if (i in this)
974                 res[i] = fun.call(thisp, this[i], i, this);
975         }
976
977         return res;
978     }
979     
980 });
981
982
983  /*
984  * Based on:
985  * Ext JS Library 1.1.1
986  * Copyright(c) 2006-2007, Ext JS, LLC.
987  *
988  * Originally Released Under LGPL - original licence link has changed is not relivant.
989  *
990  * Fork - LGPL
991  * <script type="text/javascript">
992  */
993
994 /**
995  * @class Date
996  *
997  * The date parsing and format syntax is a subset of
998  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
999  * supported will provide results equivalent to their PHP versions.
1000  *
1001  * Following is the list of all currently supported formats:
1002  *<pre>
1003 Sample date:
1004 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1005
1006 Format  Output      Description
1007 ------  ----------  --------------------------------------------------------------
1008   d      10         Day of the month, 2 digits with leading zeros
1009   D      Wed        A textual representation of a day, three letters
1010   j      10         Day of the month without leading zeros
1011   l      Wednesday  A full textual representation of the day of the week
1012   S      th         English ordinal day of month suffix, 2 chars (use with j)
1013   w      3          Numeric representation of the day of the week
1014   z      9          The julian date, or day of the year (0-365)
1015   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1016   F      January    A full textual representation of the month
1017   m      01         Numeric representation of a month, with leading zeros
1018   M      Jan        Month name abbreviation, three letters
1019   n      1          Numeric representation of a month, without leading zeros
1020   t      31         Number of days in the given month
1021   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1022   Y      2007       A full numeric representation of a year, 4 digits
1023   y      07         A two digit representation of a year
1024   a      pm         Lowercase Ante meridiem and Post meridiem
1025   A      PM         Uppercase Ante meridiem and Post meridiem
1026   g      3          12-hour format of an hour without leading zeros
1027   G      15         24-hour format of an hour without leading zeros
1028   h      03         12-hour format of an hour with leading zeros
1029   H      15         24-hour format of an hour with leading zeros
1030   i      05         Minutes with leading zeros
1031   s      01         Seconds, with leading zeros
1032   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1033   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1034   T      CST        Timezone setting of the machine running the code
1035   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1036 </pre>
1037  *
1038  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1039  * <pre><code>
1040 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1041 document.write(dt.format('Y-m-d'));                         //2007-01-10
1042 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1043 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1044  </code></pre>
1045  *
1046  * Here are some standard date/time patterns that you might find helpful.  They
1047  * are not part of the source of Date.js, but to use them you can simply copy this
1048  * block of code into any script that is included after Date.js and they will also become
1049  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1050  * <pre><code>
1051 Date.patterns = {
1052     ISO8601Long:"Y-m-d H:i:s",
1053     ISO8601Short:"Y-m-d",
1054     ShortDate: "n/j/Y",
1055     LongDate: "l, F d, Y",
1056     FullDateTime: "l, F d, Y g:i:s A",
1057     MonthDay: "F d",
1058     ShortTime: "g:i A",
1059     LongTime: "g:i:s A",
1060     SortableDateTime: "Y-m-d\\TH:i:s",
1061     UniversalSortableDateTime: "Y-m-d H:i:sO",
1062     YearMonth: "F, Y"
1063 };
1064 </code></pre>
1065  *
1066  * Example usage:
1067  * <pre><code>
1068 var dt = new Date();
1069 document.write(dt.format(Date.patterns.ShortDate));
1070  </code></pre>
1071  */
1072
1073 /*
1074  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1075  * They generate precompiled functions from date formats instead of parsing and
1076  * processing the pattern every time you format a date.  These functions are available
1077  * on every Date object (any javascript function).
1078  *
1079  * The original article and download are here:
1080  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1081  *
1082  */
1083  
1084  
1085  // was in core
1086 /**
1087  Returns the number of milliseconds between this date and date
1088  @param {Date} date (optional) Defaults to now
1089  @return {Number} The diff in milliseconds
1090  @member Date getElapsed
1091  */
1092 Date.prototype.getElapsed = function(date) {
1093         return Math.abs((date || new Date()).getTime()-this.getTime());
1094 };
1095 // was in date file..
1096
1097
1098 // private
1099 Date.parseFunctions = {count:0};
1100 // private
1101 Date.parseRegexes = [];
1102 // private
1103 Date.formatFunctions = {count:0};
1104
1105 // private
1106 Date.prototype.dateFormat = function(format) {
1107     if (Date.formatFunctions[format] == null) {
1108         Date.createNewFormat(format);
1109     }
1110     var func = Date.formatFunctions[format];
1111     return this[func]();
1112 };
1113
1114
1115 /**
1116  * Formats a date given the supplied format string
1117  * @param {String} format The format string
1118  * @return {String} The formatted date
1119  * @method
1120  */
1121 Date.prototype.format = Date.prototype.dateFormat;
1122
1123 // private
1124 Date.createNewFormat = function(format) {
1125     var funcName = "format" + Date.formatFunctions.count++;
1126     Date.formatFunctions[format] = funcName;
1127     var code = "Date.prototype." + funcName + " = function(){return ";
1128     var special = false;
1129     var ch = '';
1130     for (var i = 0; i < format.length; ++i) {
1131         ch = format.charAt(i);
1132         if (!special && ch == "\\") {
1133             special = true;
1134         }
1135         else if (special) {
1136             special = false;
1137             code += "'" + String.escape(ch) + "' + ";
1138         }
1139         else {
1140             code += Date.getFormatCode(ch);
1141         }
1142     }
1143     /** eval:var:zzzzzzzzzzzzz */
1144     eval(code.substring(0, code.length - 3) + ";}");
1145 };
1146
1147 // private
1148 Date.getFormatCode = function(character) {
1149     switch (character) {
1150     case "d":
1151         return "String.leftPad(this.getDate(), 2, '0') + ";
1152     case "D":
1153         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1154     case "j":
1155         return "this.getDate() + ";
1156     case "l":
1157         return "Date.dayNames[this.getDay()] + ";
1158     case "S":
1159         return "this.getSuffix() + ";
1160     case "w":
1161         return "this.getDay() + ";
1162     case "z":
1163         return "this.getDayOfYear() + ";
1164     case "W":
1165         return "this.getWeekOfYear() + ";
1166     case "F":
1167         return "Date.monthNames[this.getMonth()] + ";
1168     case "m":
1169         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1170     case "M":
1171         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1172     case "n":
1173         return "(this.getMonth() + 1) + ";
1174     case "t":
1175         return "this.getDaysInMonth() + ";
1176     case "L":
1177         return "(this.isLeapYear() ? 1 : 0) + ";
1178     case "Y":
1179         return "this.getFullYear() + ";
1180     case "y":
1181         return "('' + this.getFullYear()).substring(2, 4) + ";
1182     case "a":
1183         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1184     case "A":
1185         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1186     case "g":
1187         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1188     case "G":
1189         return "this.getHours() + ";
1190     case "h":
1191         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1192     case "H":
1193         return "String.leftPad(this.getHours(), 2, '0') + ";
1194     case "i":
1195         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1196     case "s":
1197         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1198     case "O":
1199         return "this.getGMTOffset() + ";
1200     case "P":
1201         return "this.getGMTColonOffset() + ";
1202     case "T":
1203         return "this.getTimezone() + ";
1204     case "Z":
1205         return "(this.getTimezoneOffset() * -60) + ";
1206     default:
1207         return "'" + String.escape(character) + "' + ";
1208     }
1209 };
1210
1211 /**
1212  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1213  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1214  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1215  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1216  * string or the parse operation will fail.
1217  * Example Usage:
1218 <pre><code>
1219 //dt = Fri May 25 2007 (current date)
1220 var dt = new Date();
1221
1222 //dt = Thu May 25 2006 (today's month/day in 2006)
1223 dt = Date.parseDate("2006", "Y");
1224
1225 //dt = Sun Jan 15 2006 (all date parts specified)
1226 dt = Date.parseDate("2006-1-15", "Y-m-d");
1227
1228 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1229 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1230 </code></pre>
1231  * @param {String} input The unparsed date as a string
1232  * @param {String} format The format the date is in
1233  * @return {Date} The parsed date
1234  * @static
1235  */
1236 Date.parseDate = function(input, format) {
1237     if (Date.parseFunctions[format] == null) {
1238         Date.createParser(format);
1239     }
1240     var func = Date.parseFunctions[format];
1241     return Date[func](input);
1242 };
1243 /**
1244  * @private
1245  */
1246
1247 Date.createParser = function(format) {
1248     var funcName = "parse" + Date.parseFunctions.count++;
1249     var regexNum = Date.parseRegexes.length;
1250     var currentGroup = 1;
1251     Date.parseFunctions[format] = funcName;
1252
1253     var code = "Date." + funcName + " = function(input){\n"
1254         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1255         + "var d = new Date();\n"
1256         + "y = d.getFullYear();\n"
1257         + "m = d.getMonth();\n"
1258         + "d = d.getDate();\n"
1259         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1260         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1261         + "if (results && results.length > 0) {";
1262     var regex = "";
1263
1264     var special = false;
1265     var ch = '';
1266     for (var i = 0; i < format.length; ++i) {
1267         ch = format.charAt(i);
1268         if (!special && ch == "\\") {
1269             special = true;
1270         }
1271         else if (special) {
1272             special = false;
1273             regex += String.escape(ch);
1274         }
1275         else {
1276             var obj = Date.formatCodeToRegex(ch, currentGroup);
1277             currentGroup += obj.g;
1278             regex += obj.s;
1279             if (obj.g && obj.c) {
1280                 code += obj.c;
1281             }
1282         }
1283     }
1284
1285     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1286         + "{v = new Date(y, m, d, h, i, s);}\n"
1287         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1288         + "{v = new Date(y, m, d, h, i);}\n"
1289         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1290         + "{v = new Date(y, m, d, h);}\n"
1291         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1292         + "{v = new Date(y, m, d);}\n"
1293         + "else if (y >= 0 && m >= 0)\n"
1294         + "{v = new Date(y, m);}\n"
1295         + "else if (y >= 0)\n"
1296         + "{v = new Date(y);}\n"
1297         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1298         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1299         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1300         + ";}";
1301
1302     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1303     /** eval:var:zzzzzzzzzzzzz */
1304     eval(code);
1305 };
1306
1307 // private
1308 Date.formatCodeToRegex = function(character, currentGroup) {
1309     switch (character) {
1310     case "D":
1311         return {g:0,
1312         c:null,
1313         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1314     case "j":
1315         return {g:1,
1316             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1317             s:"(\\d{1,2})"}; // day of month without leading zeroes
1318     case "d":
1319         return {g:1,
1320             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1321             s:"(\\d{2})"}; // day of month with leading zeroes
1322     case "l":
1323         return {g:0,
1324             c:null,
1325             s:"(?:" + Date.dayNames.join("|") + ")"};
1326     case "S":
1327         return {g:0,
1328             c:null,
1329             s:"(?:st|nd|rd|th)"};
1330     case "w":
1331         return {g:0,
1332             c:null,
1333             s:"\\d"};
1334     case "z":
1335         return {g:0,
1336             c:null,
1337             s:"(?:\\d{1,3})"};
1338     case "W":
1339         return {g:0,
1340             c:null,
1341             s:"(?:\\d{2})"};
1342     case "F":
1343         return {g:1,
1344             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1345             s:"(" + Date.monthNames.join("|") + ")"};
1346     case "M":
1347         return {g:1,
1348             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1349             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1350     case "n":
1351         return {g:1,
1352             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1353             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1354     case "m":
1355         return {g:1,
1356             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1357             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1358     case "t":
1359         return {g:0,
1360             c:null,
1361             s:"\\d{1,2}"};
1362     case "L":
1363         return {g:0,
1364             c:null,
1365             s:"(?:1|0)"};
1366     case "Y":
1367         return {g:1,
1368             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1369             s:"(\\d{4})"};
1370     case "y":
1371         return {g:1,
1372             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1373                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1374             s:"(\\d{1,2})"};
1375     case "a":
1376         return {g:1,
1377             c:"if (results[" + currentGroup + "] == 'am') {\n"
1378                 + "if (h == 12) { h = 0; }\n"
1379                 + "} else { if (h < 12) { h += 12; }}",
1380             s:"(am|pm)"};
1381     case "A":
1382         return {g:1,
1383             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1384                 + "if (h == 12) { h = 0; }\n"
1385                 + "} else { if (h < 12) { h += 12; }}",
1386             s:"(AM|PM)"};
1387     case "g":
1388     case "G":
1389         return {g:1,
1390             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1391             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1392     case "h":
1393     case "H":
1394         return {g:1,
1395             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1396             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1397     case "i":
1398         return {g:1,
1399             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1400             s:"(\\d{2})"};
1401     case "s":
1402         return {g:1,
1403             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1404             s:"(\\d{2})"};
1405     case "O":
1406         return {g:1,
1407             c:[
1408                 "o = results[", currentGroup, "];\n",
1409                 "var sn = o.substring(0,1);\n", // get + / - sign
1410                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1411                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1412                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1413                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1414             ].join(""),
1415             s:"([+\-]\\d{2,4})"};
1416     
1417     
1418     case "P":
1419         return {g:1,
1420                 c:[
1421                    "o = results[", currentGroup, "];\n",
1422                    "var sn = o.substring(0,1);\n",
1423                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1424                    "var mn = o.substring(4,6) % 60;\n",
1425                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1426                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1427             ].join(""),
1428             s:"([+\-]\\d{4})"};
1429     case "T":
1430         return {g:0,
1431             c:null,
1432             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1433     case "Z":
1434         return {g:1,
1435             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1436                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1437             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1438     default:
1439         return {g:0,
1440             c:null,
1441             s:String.escape(character)};
1442     }
1443 };
1444
1445 /**
1446  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1447  * @return {String} The abbreviated timezone name (e.g. 'CST')
1448  */
1449 Date.prototype.getTimezone = function() {
1450     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1451 };
1452
1453 /**
1454  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1455  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1456  */
1457 Date.prototype.getGMTOffset = function() {
1458     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1459         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1460         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1461 };
1462
1463 /**
1464  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1465  * @return {String} 2-characters representing hours and 2-characters representing minutes
1466  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1467  */
1468 Date.prototype.getGMTColonOffset = function() {
1469         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1470                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1471                 + ":"
1472                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1473 }
1474
1475 /**
1476  * Get the numeric day number of the year, adjusted for leap year.
1477  * @return {Number} 0 through 364 (365 in leap years)
1478  */
1479 Date.prototype.getDayOfYear = function() {
1480     var num = 0;
1481     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1482     for (var i = 0; i < this.getMonth(); ++i) {
1483         num += Date.daysInMonth[i];
1484     }
1485     return num + this.getDate() - 1;
1486 };
1487
1488 /**
1489  * Get the string representation of the numeric week number of the year
1490  * (equivalent to the format specifier 'W').
1491  * @return {String} '00' through '52'
1492  */
1493 Date.prototype.getWeekOfYear = function() {
1494     // Skip to Thursday of this week
1495     var now = this.getDayOfYear() + (4 - this.getDay());
1496     // Find the first Thursday of the year
1497     var jan1 = new Date(this.getFullYear(), 0, 1);
1498     var then = (7 - jan1.getDay() + 4);
1499     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1500 };
1501
1502 /**
1503  * Whether or not the current date is in a leap year.
1504  * @return {Boolean} True if the current date is in a leap year, else false
1505  */
1506 Date.prototype.isLeapYear = function() {
1507     var year = this.getFullYear();
1508     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1509 };
1510
1511 /**
1512  * Get the first day of the current month, adjusted for leap year.  The returned value
1513  * is the numeric day index within the week (0-6) which can be used in conjunction with
1514  * the {@link #monthNames} array to retrieve the textual day name.
1515  * Example:
1516  *<pre><code>
1517 var dt = new Date('1/10/2007');
1518 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1519 </code></pre>
1520  * @return {Number} The day number (0-6)
1521  */
1522 Date.prototype.getFirstDayOfMonth = function() {
1523     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1524     return (day < 0) ? (day + 7) : day;
1525 };
1526
1527 /**
1528  * Get the last day of the current month, adjusted for leap year.  The returned value
1529  * is the numeric day index within the week (0-6) which can be used in conjunction with
1530  * the {@link #monthNames} array to retrieve the textual day name.
1531  * Example:
1532  *<pre><code>
1533 var dt = new Date('1/10/2007');
1534 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1535 </code></pre>
1536  * @return {Number} The day number (0-6)
1537  */
1538 Date.prototype.getLastDayOfMonth = function() {
1539     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1540     return (day < 0) ? (day + 7) : day;
1541 };
1542
1543
1544 /**
1545  * Get the first date of this date's month
1546  * @return {Date}
1547  */
1548 Date.prototype.getFirstDateOfMonth = function() {
1549     return new Date(this.getFullYear(), this.getMonth(), 1);
1550 };
1551
1552 /**
1553  * Get the last date of this date's month
1554  * @return {Date}
1555  */
1556 Date.prototype.getLastDateOfMonth = function() {
1557     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1558 };
1559 /**
1560  * Get the number of days in the current month, adjusted for leap year.
1561  * @return {Number} The number of days in the month
1562  */
1563 Date.prototype.getDaysInMonth = function() {
1564     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1565     return Date.daysInMonth[this.getMonth()];
1566 };
1567
1568 /**
1569  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1570  * @return {String} 'st, 'nd', 'rd' or 'th'
1571  */
1572 Date.prototype.getSuffix = function() {
1573     switch (this.getDate()) {
1574         case 1:
1575         case 21:
1576         case 31:
1577             return "st";
1578         case 2:
1579         case 22:
1580             return "nd";
1581         case 3:
1582         case 23:
1583             return "rd";
1584         default:
1585             return "th";
1586     }
1587 };
1588
1589 // private
1590 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1591
1592 /**
1593  * An array of textual month names.
1594  * Override these values for international dates, for example...
1595  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1596  * @type Array
1597  * @static
1598  */
1599 Date.monthNames =
1600    ["January",
1601     "February",
1602     "March",
1603     "April",
1604     "May",
1605     "June",
1606     "July",
1607     "August",
1608     "September",
1609     "October",
1610     "November",
1611     "December"];
1612
1613 /**
1614  * An array of textual day names.
1615  * Override these values for international dates, for example...
1616  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1617  * @type Array
1618  * @static
1619  */
1620 Date.dayNames =
1621    ["Sunday",
1622     "Monday",
1623     "Tuesday",
1624     "Wednesday",
1625     "Thursday",
1626     "Friday",
1627     "Saturday"];
1628
1629 // private
1630 Date.y2kYear = 50;
1631 // private
1632 Date.monthNumbers = {
1633     Jan:0,
1634     Feb:1,
1635     Mar:2,
1636     Apr:3,
1637     May:4,
1638     Jun:5,
1639     Jul:6,
1640     Aug:7,
1641     Sep:8,
1642     Oct:9,
1643     Nov:10,
1644     Dec:11};
1645
1646 /**
1647  * Creates and returns a new Date instance with the exact same date value as the called instance.
1648  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1649  * variable will also be changed.  When the intention is to create a new variable that will not
1650  * modify the original instance, you should create a clone.
1651  *
1652  * Example of correctly cloning a date:
1653  * <pre><code>
1654 //wrong way:
1655 var orig = new Date('10/1/2006');
1656 var copy = orig;
1657 copy.setDate(5);
1658 document.write(orig);  //returns 'Thu Oct 05 2006'!
1659
1660 //correct way:
1661 var orig = new Date('10/1/2006');
1662 var copy = orig.clone();
1663 copy.setDate(5);
1664 document.write(orig);  //returns 'Thu Oct 01 2006'
1665 </code></pre>
1666  * @return {Date} The new Date instance
1667  */
1668 Date.prototype.clone = function() {
1669         return new Date(this.getTime());
1670 };
1671
1672 /**
1673  * Clears any time information from this date
1674  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1675  @return {Date} this or the clone
1676  */
1677 Date.prototype.clearTime = function(clone){
1678     if(clone){
1679         return this.clone().clearTime();
1680     }
1681     this.setHours(0);
1682     this.setMinutes(0);
1683     this.setSeconds(0);
1684     this.setMilliseconds(0);
1685     return this;
1686 };
1687
1688 // private
1689 // safari setMonth is broken
1690 if(Roo.isSafari){
1691     Date.brokenSetMonth = Date.prototype.setMonth;
1692         Date.prototype.setMonth = function(num){
1693                 if(num <= -1){
1694                         var n = Math.ceil(-num);
1695                         var back_year = Math.ceil(n/12);
1696                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1697                         this.setFullYear(this.getFullYear() - back_year);
1698                         return Date.brokenSetMonth.call(this, month);
1699                 } else {
1700                         return Date.brokenSetMonth.apply(this, arguments);
1701                 }
1702         };
1703 }
1704
1705 /** Date interval constant 
1706 * @static 
1707 * @type String */
1708 Date.MILLI = "ms";
1709 /** Date interval constant 
1710 * @static 
1711 * @type String */
1712 Date.SECOND = "s";
1713 /** Date interval constant 
1714 * @static 
1715 * @type String */
1716 Date.MINUTE = "mi";
1717 /** Date interval constant 
1718 * @static 
1719 * @type String */
1720 Date.HOUR = "h";
1721 /** Date interval constant 
1722 * @static 
1723 * @type String */
1724 Date.DAY = "d";
1725 /** Date interval constant 
1726 * @static 
1727 * @type String */
1728 Date.MONTH = "mo";
1729 /** Date interval constant 
1730 * @static 
1731 * @type String */
1732 Date.YEAR = "y";
1733
1734 /**
1735  * Provides a convenient method of performing basic date arithmetic.  This method
1736  * does not modify the Date instance being called - it creates and returns
1737  * a new Date instance containing the resulting date value.
1738  *
1739  * Examples:
1740  * <pre><code>
1741 //Basic usage:
1742 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1743 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1744
1745 //Negative values will subtract correctly:
1746 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1747 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1748
1749 //You can even chain several calls together in one line!
1750 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1751 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1752  </code></pre>
1753  *
1754  * @param {String} interval   A valid date interval enum value
1755  * @param {Number} value      The amount to add to the current date
1756  * @return {Date} The new Date instance
1757  */
1758 Date.prototype.add = function(interval, value){
1759   var d = this.clone();
1760   if (!interval || value === 0) return d;
1761   switch(interval.toLowerCase()){
1762     case Date.MILLI:
1763       d.setMilliseconds(this.getMilliseconds() + value);
1764       break;
1765     case Date.SECOND:
1766       d.setSeconds(this.getSeconds() + value);
1767       break;
1768     case Date.MINUTE:
1769       d.setMinutes(this.getMinutes() + value);
1770       break;
1771     case Date.HOUR:
1772       d.setHours(this.getHours() + value);
1773       break;
1774     case Date.DAY:
1775       d.setDate(this.getDate() + value);
1776       break;
1777     case Date.MONTH:
1778       var day = this.getDate();
1779       if(day > 28){
1780           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1781       }
1782       d.setDate(day);
1783       d.setMonth(this.getMonth() + value);
1784       break;
1785     case Date.YEAR:
1786       d.setFullYear(this.getFullYear() + value);
1787       break;
1788   }
1789   return d;
1790 };
1791 /*
1792  * Based on:
1793  * Ext JS Library 1.1.1
1794  * Copyright(c) 2006-2007, Ext JS, LLC.
1795  *
1796  * Originally Released Under LGPL - original licence link has changed is not relivant.
1797  *
1798  * Fork - LGPL
1799  * <script type="text/javascript">
1800  */
1801
1802 /**
1803  * @class Roo.lib.Dom
1804  * @static
1805  * 
1806  * Dom utils (from YIU afaik)
1807  * 
1808  **/
1809 Roo.lib.Dom = {
1810     /**
1811      * Get the view width
1812      * @param {Boolean} full True will get the full document, otherwise it's the view width
1813      * @return {Number} The width
1814      */
1815      
1816     getViewWidth : function(full) {
1817         return full ? this.getDocumentWidth() : this.getViewportWidth();
1818     },
1819     /**
1820      * Get the view height
1821      * @param {Boolean} full True will get the full document, otherwise it's the view height
1822      * @return {Number} The height
1823      */
1824     getViewHeight : function(full) {
1825         return full ? this.getDocumentHeight() : this.getViewportHeight();
1826     },
1827
1828     getDocumentHeight: function() {
1829         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1830         return Math.max(scrollHeight, this.getViewportHeight());
1831     },
1832
1833     getDocumentWidth: function() {
1834         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1835         return Math.max(scrollWidth, this.getViewportWidth());
1836     },
1837
1838     getViewportHeight: function() {
1839         var height = self.innerHeight;
1840         var mode = document.compatMode;
1841
1842         if ((mode || Roo.isIE) && !Roo.isOpera) {
1843             height = (mode == "CSS1Compat") ?
1844                      document.documentElement.clientHeight :
1845                      document.body.clientHeight;
1846         }
1847
1848         return height;
1849     },
1850
1851     getViewportWidth: function() {
1852         var width = self.innerWidth;
1853         var mode = document.compatMode;
1854
1855         if (mode || Roo.isIE) {
1856             width = (mode == "CSS1Compat") ?
1857                     document.documentElement.clientWidth :
1858                     document.body.clientWidth;
1859         }
1860         return width;
1861     },
1862
1863     isAncestor : function(p, c) {
1864         p = Roo.getDom(p);
1865         c = Roo.getDom(c);
1866         if (!p || !c) {
1867             return false;
1868         }
1869
1870         if (p.contains && !Roo.isSafari) {
1871             return p.contains(c);
1872         } else if (p.compareDocumentPosition) {
1873             return !!(p.compareDocumentPosition(c) & 16);
1874         } else {
1875             var parent = c.parentNode;
1876             while (parent) {
1877                 if (parent == p) {
1878                     return true;
1879                 }
1880                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1881                     return false;
1882                 }
1883                 parent = parent.parentNode;
1884             }
1885             return false;
1886         }
1887     },
1888
1889     getRegion : function(el) {
1890         return Roo.lib.Region.getRegion(el);
1891     },
1892
1893     getY : function(el) {
1894         return this.getXY(el)[1];
1895     },
1896
1897     getX : function(el) {
1898         return this.getXY(el)[0];
1899     },
1900
1901     getXY : function(el) {
1902         var p, pe, b, scroll, bd = document.body;
1903         el = Roo.getDom(el);
1904         var fly = Roo.lib.AnimBase.fly;
1905         if (el.getBoundingClientRect) {
1906             b = el.getBoundingClientRect();
1907             scroll = fly(document).getScroll();
1908             return [b.left + scroll.left, b.top + scroll.top];
1909         }
1910         var x = 0, y = 0;
1911
1912         p = el;
1913
1914         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1915
1916         while (p) {
1917
1918             x += p.offsetLeft;
1919             y += p.offsetTop;
1920
1921             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1922                 hasAbsolute = true;
1923             }
1924
1925             if (Roo.isGecko) {
1926                 pe = fly(p);
1927
1928                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1929                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1930
1931
1932                 x += bl;
1933                 y += bt;
1934
1935
1936                 if (p != el && pe.getStyle('overflow') != 'visible') {
1937                     x += bl;
1938                     y += bt;
1939                 }
1940             }
1941             p = p.offsetParent;
1942         }
1943
1944         if (Roo.isSafari && hasAbsolute) {
1945             x -= bd.offsetLeft;
1946             y -= bd.offsetTop;
1947         }
1948
1949         if (Roo.isGecko && !hasAbsolute) {
1950             var dbd = fly(bd);
1951             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1952             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1953         }
1954
1955         p = el.parentNode;
1956         while (p && p != bd) {
1957             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1958                 x -= p.scrollLeft;
1959                 y -= p.scrollTop;
1960             }
1961             p = p.parentNode;
1962         }
1963         return [x, y];
1964     },
1965  
1966   
1967
1968
1969     setXY : function(el, xy) {
1970         el = Roo.fly(el, '_setXY');
1971         el.position();
1972         var pts = el.translatePoints(xy);
1973         if (xy[0] !== false) {
1974             el.dom.style.left = pts.left + "px";
1975         }
1976         if (xy[1] !== false) {
1977             el.dom.style.top = pts.top + "px";
1978         }
1979     },
1980
1981     setX : function(el, x) {
1982         this.setXY(el, [x, false]);
1983     },
1984
1985     setY : function(el, y) {
1986         this.setXY(el, [false, y]);
1987     }
1988 };
1989 /*
1990  * Portions of this file are based on pieces of Yahoo User Interface Library
1991  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1992  * YUI licensed under the BSD License:
1993  * http://developer.yahoo.net/yui/license.txt
1994  * <script type="text/javascript">
1995  *
1996  */
1997
1998 Roo.lib.Event = function() {
1999     var loadComplete = false;
2000     var listeners = [];
2001     var unloadListeners = [];
2002     var retryCount = 0;
2003     var onAvailStack = [];
2004     var counter = 0;
2005     var lastError = null;
2006
2007     return {
2008         POLL_RETRYS: 200,
2009         POLL_INTERVAL: 20,
2010         EL: 0,
2011         TYPE: 1,
2012         FN: 2,
2013         WFN: 3,
2014         OBJ: 3,
2015         ADJ_SCOPE: 4,
2016         _interval: null,
2017
2018         startInterval: function() {
2019             if (!this._interval) {
2020                 var self = this;
2021                 var callback = function() {
2022                     self._tryPreloadAttach();
2023                 };
2024                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2025
2026             }
2027         },
2028
2029         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2030             onAvailStack.push({ id:         p_id,
2031                 fn:         p_fn,
2032                 obj:        p_obj,
2033                 override:   p_override,
2034                 checkReady: false    });
2035
2036             retryCount = this.POLL_RETRYS;
2037             this.startInterval();
2038         },
2039
2040
2041         addListener: function(el, eventName, fn) {
2042             el = Roo.getDom(el);
2043             if (!el || !fn) {
2044                 return false;
2045             }
2046
2047             if ("unload" == eventName) {
2048                 unloadListeners[unloadListeners.length] =
2049                 [el, eventName, fn];
2050                 return true;
2051             }
2052
2053             var wrappedFn = function(e) {
2054                 return fn(Roo.lib.Event.getEvent(e));
2055             };
2056
2057             var li = [el, eventName, fn, wrappedFn];
2058
2059             var index = listeners.length;
2060             listeners[index] = li;
2061
2062             this.doAdd(el, eventName, wrappedFn, false);
2063             return true;
2064
2065         },
2066
2067
2068         removeListener: function(el, eventName, fn) {
2069             var i, len;
2070
2071             el = Roo.getDom(el);
2072
2073             if(!fn) {
2074                 return this.purgeElement(el, false, eventName);
2075             }
2076
2077
2078             if ("unload" == eventName) {
2079
2080                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2081                     var li = unloadListeners[i];
2082                     if (li &&
2083                         li[0] == el &&
2084                         li[1] == eventName &&
2085                         li[2] == fn) {
2086                         unloadListeners.splice(i, 1);
2087                         return true;
2088                     }
2089                 }
2090
2091                 return false;
2092             }
2093
2094             var cacheItem = null;
2095
2096
2097             var index = arguments[3];
2098
2099             if ("undefined" == typeof index) {
2100                 index = this._getCacheIndex(el, eventName, fn);
2101             }
2102
2103             if (index >= 0) {
2104                 cacheItem = listeners[index];
2105             }
2106
2107             if (!el || !cacheItem) {
2108                 return false;
2109             }
2110
2111             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2112
2113             delete listeners[index][this.WFN];
2114             delete listeners[index][this.FN];
2115             listeners.splice(index, 1);
2116
2117             return true;
2118
2119         },
2120
2121
2122         getTarget: function(ev, resolveTextNode) {
2123             ev = ev.browserEvent || ev;
2124             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2125             var t = ev.target || ev.srcElement;
2126             return this.resolveTextNode(t);
2127         },
2128
2129
2130         resolveTextNode: function(node) {
2131             if (Roo.isSafari && node && 3 == node.nodeType) {
2132                 return node.parentNode;
2133             } else {
2134                 return node;
2135             }
2136         },
2137
2138
2139         getPageX: function(ev) {
2140             ev = ev.browserEvent || ev;
2141             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2142             var x = ev.pageX;
2143             if (!x && 0 !== x) {
2144                 x = ev.clientX || 0;
2145
2146                 if (Roo.isIE) {
2147                     x += this.getScroll()[1];
2148                 }
2149             }
2150
2151             return x;
2152         },
2153
2154
2155         getPageY: function(ev) {
2156             ev = ev.browserEvent || ev;
2157             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2158             var y = ev.pageY;
2159             if (!y && 0 !== y) {
2160                 y = ev.clientY || 0;
2161
2162                 if (Roo.isIE) {
2163                     y += this.getScroll()[0];
2164                 }
2165             }
2166
2167
2168             return y;
2169         },
2170
2171
2172         getXY: function(ev) {
2173             ev = ev.browserEvent || ev;
2174             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2175             return [this.getPageX(ev), this.getPageY(ev)];
2176         },
2177
2178
2179         getRelatedTarget: function(ev) {
2180             ev = ev.browserEvent || ev;
2181             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2182             var t = ev.relatedTarget;
2183             if (!t) {
2184                 if (ev.type == "mouseout") {
2185                     t = ev.toElement;
2186                 } else if (ev.type == "mouseover") {
2187                     t = ev.fromElement;
2188                 }
2189             }
2190
2191             return this.resolveTextNode(t);
2192         },
2193
2194
2195         getTime: function(ev) {
2196             ev = ev.browserEvent || ev;
2197             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2198             if (!ev.time) {
2199                 var t = new Date().getTime();
2200                 try {
2201                     ev.time = t;
2202                 } catch(ex) {
2203                     this.lastError = ex;
2204                     return t;
2205                 }
2206             }
2207
2208             return ev.time;
2209         },
2210
2211
2212         stopEvent: function(ev) {
2213             this.stopPropagation(ev);
2214             this.preventDefault(ev);
2215         },
2216
2217
2218         stopPropagation: function(ev) {
2219             ev = ev.browserEvent || ev;
2220             if (ev.stopPropagation) {
2221                 ev.stopPropagation();
2222             } else {
2223                 ev.cancelBubble = true;
2224             }
2225         },
2226
2227
2228         preventDefault: function(ev) {
2229             ev = ev.browserEvent || ev;
2230             if(ev.preventDefault) {
2231                 ev.preventDefault();
2232             } else {
2233                 ev.returnValue = false;
2234             }
2235         },
2236
2237
2238         getEvent: function(e) {
2239             var ev = e || window.event;
2240             if (!ev) {
2241                 var c = this.getEvent.caller;
2242                 while (c) {
2243                     ev = c.arguments[0];
2244                     if (ev && Event == ev.constructor) {
2245                         break;
2246                     }
2247                     c = c.caller;
2248                 }
2249             }
2250             return ev;
2251         },
2252
2253
2254         getCharCode: function(ev) {
2255             ev = ev.browserEvent || ev;
2256             return ev.charCode || ev.keyCode || 0;
2257         },
2258
2259
2260         _getCacheIndex: function(el, eventName, fn) {
2261             for (var i = 0,len = listeners.length; i < len; ++i) {
2262                 var li = listeners[i];
2263                 if (li &&
2264                     li[this.FN] == fn &&
2265                     li[this.EL] == el &&
2266                     li[this.TYPE] == eventName) {
2267                     return i;
2268                 }
2269             }
2270
2271             return -1;
2272         },
2273
2274
2275         elCache: {},
2276
2277
2278         getEl: function(id) {
2279             return document.getElementById(id);
2280         },
2281
2282
2283         clearCache: function() {
2284         },
2285
2286
2287         _load: function(e) {
2288             loadComplete = true;
2289             var EU = Roo.lib.Event;
2290
2291
2292             if (Roo.isIE) {
2293                 EU.doRemove(window, "load", EU._load);
2294             }
2295         },
2296
2297
2298         _tryPreloadAttach: function() {
2299
2300             if (this.locked) {
2301                 return false;
2302             }
2303
2304             this.locked = true;
2305
2306
2307             var tryAgain = !loadComplete;
2308             if (!tryAgain) {
2309                 tryAgain = (retryCount > 0);
2310             }
2311
2312
2313             var notAvail = [];
2314             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2315                 var item = onAvailStack[i];
2316                 if (item) {
2317                     var el = this.getEl(item.id);
2318
2319                     if (el) {
2320                         if (!item.checkReady ||
2321                             loadComplete ||
2322                             el.nextSibling ||
2323                             (document && document.body)) {
2324
2325                             var scope = el;
2326                             if (item.override) {
2327                                 if (item.override === true) {
2328                                     scope = item.obj;
2329                                 } else {
2330                                     scope = item.override;
2331                                 }
2332                             }
2333                             item.fn.call(scope, item.obj);
2334                             onAvailStack[i] = null;
2335                         }
2336                     } else {
2337                         notAvail.push(item);
2338                     }
2339                 }
2340             }
2341
2342             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2343
2344             if (tryAgain) {
2345
2346                 this.startInterval();
2347             } else {
2348                 clearInterval(this._interval);
2349                 this._interval = null;
2350             }
2351
2352             this.locked = false;
2353
2354             return true;
2355
2356         },
2357
2358
2359         purgeElement: function(el, recurse, eventName) {
2360             var elListeners = this.getListeners(el, eventName);
2361             if (elListeners) {
2362                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2363                     var l = elListeners[i];
2364                     this.removeListener(el, l.type, l.fn);
2365                 }
2366             }
2367
2368             if (recurse && el && el.childNodes) {
2369                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2370                     this.purgeElement(el.childNodes[i], recurse, eventName);
2371                 }
2372             }
2373         },
2374
2375
2376         getListeners: function(el, eventName) {
2377             var results = [], searchLists;
2378             if (!eventName) {
2379                 searchLists = [listeners, unloadListeners];
2380             } else if (eventName == "unload") {
2381                 searchLists = [unloadListeners];
2382             } else {
2383                 searchLists = [listeners];
2384             }
2385
2386             for (var j = 0; j < searchLists.length; ++j) {
2387                 var searchList = searchLists[j];
2388                 if (searchList && searchList.length > 0) {
2389                     for (var i = 0,len = searchList.length; i < len; ++i) {
2390                         var l = searchList[i];
2391                         if (l && l[this.EL] === el &&
2392                             (!eventName || eventName === l[this.TYPE])) {
2393                             results.push({
2394                                 type:   l[this.TYPE],
2395                                 fn:     l[this.FN],
2396                                 obj:    l[this.OBJ],
2397                                 adjust: l[this.ADJ_SCOPE],
2398                                 index:  i
2399                             });
2400                         }
2401                     }
2402                 }
2403             }
2404
2405             return (results.length) ? results : null;
2406         },
2407
2408
2409         _unload: function(e) {
2410
2411             var EU = Roo.lib.Event, i, j, l, len, index;
2412
2413             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2414                 l = unloadListeners[i];
2415                 if (l) {
2416                     var scope = window;
2417                     if (l[EU.ADJ_SCOPE]) {
2418                         if (l[EU.ADJ_SCOPE] === true) {
2419                             scope = l[EU.OBJ];
2420                         } else {
2421                             scope = l[EU.ADJ_SCOPE];
2422                         }
2423                     }
2424                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2425                     unloadListeners[i] = null;
2426                     l = null;
2427                     scope = null;
2428                 }
2429             }
2430
2431             unloadListeners = null;
2432
2433             if (listeners && listeners.length > 0) {
2434                 j = listeners.length;
2435                 while (j) {
2436                     index = j - 1;
2437                     l = listeners[index];
2438                     if (l) {
2439                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2440                                 l[EU.FN], index);
2441                     }
2442                     j = j - 1;
2443                 }
2444                 l = null;
2445
2446                 EU.clearCache();
2447             }
2448
2449             EU.doRemove(window, "unload", EU._unload);
2450
2451         },
2452
2453
2454         getScroll: function() {
2455             var dd = document.documentElement, db = document.body;
2456             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2457                 return [dd.scrollTop, dd.scrollLeft];
2458             } else if (db) {
2459                 return [db.scrollTop, db.scrollLeft];
2460             } else {
2461                 return [0, 0];
2462             }
2463         },
2464
2465
2466         doAdd: function () {
2467             if (window.addEventListener) {
2468                 return function(el, eventName, fn, capture) {
2469                     el.addEventListener(eventName, fn, (capture));
2470                 };
2471             } else if (window.attachEvent) {
2472                 return function(el, eventName, fn, capture) {
2473                     el.attachEvent("on" + eventName, fn);
2474                 };
2475             } else {
2476                 return function() {
2477                 };
2478             }
2479         }(),
2480
2481
2482         doRemove: function() {
2483             if (window.removeEventListener) {
2484                 return function (el, eventName, fn, capture) {
2485                     el.removeEventListener(eventName, fn, (capture));
2486                 };
2487             } else if (window.detachEvent) {
2488                 return function (el, eventName, fn) {
2489                     el.detachEvent("on" + eventName, fn);
2490                 };
2491             } else {
2492                 return function() {
2493                 };
2494             }
2495         }()
2496     };
2497     
2498 }();
2499 (function() {     
2500    
2501     var E = Roo.lib.Event;
2502     E.on = E.addListener;
2503     E.un = E.removeListener;
2504
2505     if (document && document.body) {
2506         E._load();
2507     } else {
2508         E.doAdd(window, "load", E._load);
2509     }
2510     E.doAdd(window, "unload", E._unload);
2511     E._tryPreloadAttach();
2512 })();
2513
2514 /*
2515  * Portions of this file are based on pieces of Yahoo User Interface Library
2516  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2517  * YUI licensed under the BSD License:
2518  * http://developer.yahoo.net/yui/license.txt
2519  * <script type="text/javascript">
2520  *
2521  */
2522
2523 (function() {
2524     /**
2525      * @class Roo.lib.Ajax
2526      *
2527      */
2528     Roo.lib.Ajax = {
2529         /**
2530          * @static 
2531          */
2532         request : function(method, uri, cb, data, options) {
2533             if(options){
2534                 var hs = options.headers;
2535                 if(hs){
2536                     for(var h in hs){
2537                         if(hs.hasOwnProperty(h)){
2538                             this.initHeader(h, hs[h], false);
2539                         }
2540                     }
2541                 }
2542                 if(options.xmlData){
2543                     this.initHeader('Content-Type', 'text/xml', false);
2544                     method = 'POST';
2545                     data = options.xmlData;
2546                 }
2547             }
2548
2549             return this.asyncRequest(method, uri, cb, data);
2550         },
2551
2552         serializeForm : function(form) {
2553             if(typeof form == 'string') {
2554                 form = (document.getElementById(form) || document.forms[form]);
2555             }
2556
2557             var el, name, val, disabled, data = '', hasSubmit = false;
2558             for (var i = 0; i < form.elements.length; i++) {
2559                 el = form.elements[i];
2560                 disabled = form.elements[i].disabled;
2561                 name = form.elements[i].name;
2562                 val = form.elements[i].value;
2563
2564                 if (!disabled && name){
2565                     switch (el.type)
2566                             {
2567                         case 'select-one':
2568                         case 'select-multiple':
2569                             for (var j = 0; j < el.options.length; j++) {
2570                                 if (el.options[j].selected) {
2571                                     if (Roo.isIE) {
2572                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2573                                     }
2574                                     else {
2575                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2576                                     }
2577                                 }
2578                             }
2579                             break;
2580                         case 'radio':
2581                         case 'checkbox':
2582                             if (el.checked) {
2583                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2584                             }
2585                             break;
2586                         case 'file':
2587
2588                         case undefined:
2589
2590                         case 'reset':
2591
2592                         case 'button':
2593
2594                             break;
2595                         case 'submit':
2596                             if(hasSubmit == false) {
2597                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2598                                 hasSubmit = true;
2599                             }
2600                             break;
2601                         default:
2602                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2603                             break;
2604                     }
2605                 }
2606             }
2607             data = data.substr(0, data.length - 1);
2608             return data;
2609         },
2610
2611         headers:{},
2612
2613         hasHeaders:false,
2614
2615         useDefaultHeader:true,
2616
2617         defaultPostHeader:'application/x-www-form-urlencoded',
2618
2619         useDefaultXhrHeader:true,
2620
2621         defaultXhrHeader:'XMLHttpRequest',
2622
2623         hasDefaultHeaders:true,
2624
2625         defaultHeaders:{},
2626
2627         poll:{},
2628
2629         timeout:{},
2630
2631         pollInterval:50,
2632
2633         transactionId:0,
2634
2635         setProgId:function(id)
2636         {
2637             this.activeX.unshift(id);
2638         },
2639
2640         setDefaultPostHeader:function(b)
2641         {
2642             this.useDefaultHeader = b;
2643         },
2644
2645         setDefaultXhrHeader:function(b)
2646         {
2647             this.useDefaultXhrHeader = b;
2648         },
2649
2650         setPollingInterval:function(i)
2651         {
2652             if (typeof i == 'number' && isFinite(i)) {
2653                 this.pollInterval = i;
2654             }
2655         },
2656
2657         createXhrObject:function(transactionId)
2658         {
2659             var obj,http;
2660             try
2661             {
2662
2663                 http = new XMLHttpRequest();
2664
2665                 obj = { conn:http, tId:transactionId };
2666             }
2667             catch(e)
2668             {
2669                 for (var i = 0; i < this.activeX.length; ++i) {
2670                     try
2671                     {
2672
2673                         http = new ActiveXObject(this.activeX[i]);
2674
2675                         obj = { conn:http, tId:transactionId };
2676                         break;
2677                     }
2678                     catch(e) {
2679                     }
2680                 }
2681             }
2682             finally
2683             {
2684                 return obj;
2685             }
2686         },
2687
2688         getConnectionObject:function()
2689         {
2690             var o;
2691             var tId = this.transactionId;
2692
2693             try
2694             {
2695                 o = this.createXhrObject(tId);
2696                 if (o) {
2697                     this.transactionId++;
2698                 }
2699             }
2700             catch(e) {
2701             }
2702             finally
2703             {
2704                 return o;
2705             }
2706         },
2707
2708         asyncRequest:function(method, uri, callback, postData)
2709         {
2710             var o = this.getConnectionObject();
2711
2712             if (!o) {
2713                 return null;
2714             }
2715             else {
2716                 o.conn.open(method, uri, true);
2717
2718                 if (this.useDefaultXhrHeader) {
2719                     if (!this.defaultHeaders['X-Requested-With']) {
2720                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2721                     }
2722                 }
2723
2724                 if(postData && this.useDefaultHeader){
2725                     this.initHeader('Content-Type', this.defaultPostHeader);
2726                 }
2727
2728                  if (this.hasDefaultHeaders || this.hasHeaders) {
2729                     this.setHeader(o);
2730                 }
2731
2732                 this.handleReadyState(o, callback);
2733                 o.conn.send(postData || null);
2734
2735                 return o;
2736             }
2737         },
2738
2739         handleReadyState:function(o, callback)
2740         {
2741             var oConn = this;
2742
2743             if (callback && callback.timeout) {
2744                 
2745                 this.timeout[o.tId] = window.setTimeout(function() {
2746                     oConn.abort(o, callback, true);
2747                 }, callback.timeout);
2748             }
2749
2750             this.poll[o.tId] = window.setInterval(
2751                     function() {
2752                         if (o.conn && o.conn.readyState == 4) {
2753                             window.clearInterval(oConn.poll[o.tId]);
2754                             delete oConn.poll[o.tId];
2755
2756                             if(callback && callback.timeout) {
2757                                 window.clearTimeout(oConn.timeout[o.tId]);
2758                                 delete oConn.timeout[o.tId];
2759                             }
2760
2761                             oConn.handleTransactionResponse(o, callback);
2762                         }
2763                     }
2764                     , this.pollInterval);
2765         },
2766
2767         handleTransactionResponse:function(o, callback, isAbort)
2768         {
2769
2770             if (!callback) {
2771                 this.releaseObject(o);
2772                 return;
2773             }
2774
2775             var httpStatus, responseObject;
2776
2777             try
2778             {
2779                 if (o.conn.status !== undefined && o.conn.status != 0) {
2780                     httpStatus = o.conn.status;
2781                 }
2782                 else {
2783                     httpStatus = 13030;
2784                 }
2785             }
2786             catch(e) {
2787
2788
2789                 httpStatus = 13030;
2790             }
2791
2792             if (httpStatus >= 200 && httpStatus < 300) {
2793                 responseObject = this.createResponseObject(o, callback.argument);
2794                 if (callback.success) {
2795                     if (!callback.scope) {
2796                         callback.success(responseObject);
2797                     }
2798                     else {
2799
2800
2801                         callback.success.apply(callback.scope, [responseObject]);
2802                     }
2803                 }
2804             }
2805             else {
2806                 switch (httpStatus) {
2807
2808                     case 12002:
2809                     case 12029:
2810                     case 12030:
2811                     case 12031:
2812                     case 12152:
2813                     case 13030:
2814                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2815                         if (callback.failure) {
2816                             if (!callback.scope) {
2817                                 callback.failure(responseObject);
2818                             }
2819                             else {
2820                                 callback.failure.apply(callback.scope, [responseObject]);
2821                             }
2822                         }
2823                         break;
2824                     default:
2825                         responseObject = this.createResponseObject(o, callback.argument);
2826                         if (callback.failure) {
2827                             if (!callback.scope) {
2828                                 callback.failure(responseObject);
2829                             }
2830                             else {
2831                                 callback.failure.apply(callback.scope, [responseObject]);
2832                             }
2833                         }
2834                 }
2835             }
2836
2837             this.releaseObject(o);
2838             responseObject = null;
2839         },
2840
2841         createResponseObject:function(o, callbackArg)
2842         {
2843             var obj = {};
2844             var headerObj = {};
2845
2846             try
2847             {
2848                 var headerStr = o.conn.getAllResponseHeaders();
2849                 var header = headerStr.split('\n');
2850                 for (var i = 0; i < header.length; i++) {
2851                     var delimitPos = header[i].indexOf(':');
2852                     if (delimitPos != -1) {
2853                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2854                     }
2855                 }
2856             }
2857             catch(e) {
2858             }
2859
2860             obj.tId = o.tId;
2861             obj.status = o.conn.status;
2862             obj.statusText = o.conn.statusText;
2863             obj.getResponseHeader = headerObj;
2864             obj.getAllResponseHeaders = headerStr;
2865             obj.responseText = o.conn.responseText;
2866             obj.responseXML = o.conn.responseXML;
2867
2868             if (typeof callbackArg !== undefined) {
2869                 obj.argument = callbackArg;
2870             }
2871
2872             return obj;
2873         },
2874
2875         createExceptionObject:function(tId, callbackArg, isAbort)
2876         {
2877             var COMM_CODE = 0;
2878             var COMM_ERROR = 'communication failure';
2879             var ABORT_CODE = -1;
2880             var ABORT_ERROR = 'transaction aborted';
2881
2882             var obj = {};
2883
2884             obj.tId = tId;
2885             if (isAbort) {
2886                 obj.status = ABORT_CODE;
2887                 obj.statusText = ABORT_ERROR;
2888             }
2889             else {
2890                 obj.status = COMM_CODE;
2891                 obj.statusText = COMM_ERROR;
2892             }
2893
2894             if (callbackArg) {
2895                 obj.argument = callbackArg;
2896             }
2897
2898             return obj;
2899         },
2900
2901         initHeader:function(label, value, isDefault)
2902         {
2903             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2904
2905             if (headerObj[label] === undefined) {
2906                 headerObj[label] = value;
2907             }
2908             else {
2909
2910
2911                 headerObj[label] = value + "," + headerObj[label];
2912             }
2913
2914             if (isDefault) {
2915                 this.hasDefaultHeaders = true;
2916             }
2917             else {
2918                 this.hasHeaders = true;
2919             }
2920         },
2921
2922
2923         setHeader:function(o)
2924         {
2925             if (this.hasDefaultHeaders) {
2926                 for (var prop in this.defaultHeaders) {
2927                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2928                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2929                     }
2930                 }
2931             }
2932
2933             if (this.hasHeaders) {
2934                 for (var prop in this.headers) {
2935                     if (this.headers.hasOwnProperty(prop)) {
2936                         o.conn.setRequestHeader(prop, this.headers[prop]);
2937                     }
2938                 }
2939                 this.headers = {};
2940                 this.hasHeaders = false;
2941             }
2942         },
2943
2944         resetDefaultHeaders:function() {
2945             delete this.defaultHeaders;
2946             this.defaultHeaders = {};
2947             this.hasDefaultHeaders = false;
2948         },
2949
2950         abort:function(o, callback, isTimeout)
2951         {
2952             if(this.isCallInProgress(o)) {
2953                 o.conn.abort();
2954                 window.clearInterval(this.poll[o.tId]);
2955                 delete this.poll[o.tId];
2956                 if (isTimeout) {
2957                     delete this.timeout[o.tId];
2958                 }
2959
2960                 this.handleTransactionResponse(o, callback, true);
2961
2962                 return true;
2963             }
2964             else {
2965                 return false;
2966             }
2967         },
2968
2969
2970         isCallInProgress:function(o)
2971         {
2972             if (o && o.conn) {
2973                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2974             }
2975             else {
2976
2977                 return false;
2978             }
2979         },
2980
2981
2982         releaseObject:function(o)
2983         {
2984
2985             o.conn = null;
2986
2987             o = null;
2988         },
2989
2990         activeX:[
2991         'MSXML2.XMLHTTP.3.0',
2992         'MSXML2.XMLHTTP',
2993         'Microsoft.XMLHTTP'
2994         ]
2995
2996
2997     };
2998 })();/*
2999  * Portions of this file are based on pieces of Yahoo User Interface Library
3000  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3001  * YUI licensed under the BSD License:
3002  * http://developer.yahoo.net/yui/license.txt
3003  * <script type="text/javascript">
3004  *
3005  */
3006
3007 Roo.lib.Region = function(t, r, b, l) {
3008     this.top = t;
3009     this[1] = t;
3010     this.right = r;
3011     this.bottom = b;
3012     this.left = l;
3013     this[0] = l;
3014 };
3015
3016
3017 Roo.lib.Region.prototype = {
3018     contains : function(region) {
3019         return ( region.left >= this.left &&
3020                  region.right <= this.right &&
3021                  region.top >= this.top &&
3022                  region.bottom <= this.bottom    );
3023
3024     },
3025
3026     getArea : function() {
3027         return ( (this.bottom - this.top) * (this.right - this.left) );
3028     },
3029
3030     intersect : function(region) {
3031         var t = Math.max(this.top, region.top);
3032         var r = Math.min(this.right, region.right);
3033         var b = Math.min(this.bottom, region.bottom);
3034         var l = Math.max(this.left, region.left);
3035
3036         if (b >= t && r >= l) {
3037             return new Roo.lib.Region(t, r, b, l);
3038         } else {
3039             return null;
3040         }
3041     },
3042     union : function(region) {
3043         var t = Math.min(this.top, region.top);
3044         var r = Math.max(this.right, region.right);
3045         var b = Math.max(this.bottom, region.bottom);
3046         var l = Math.min(this.left, region.left);
3047
3048         return new Roo.lib.Region(t, r, b, l);
3049     },
3050
3051     adjust : function(t, l, b, r) {
3052         this.top += t;
3053         this.left += l;
3054         this.right += r;
3055         this.bottom += b;
3056         return this;
3057     }
3058 };
3059
3060 Roo.lib.Region.getRegion = function(el) {
3061     var p = Roo.lib.Dom.getXY(el);
3062
3063     var t = p[1];
3064     var r = p[0] + el.offsetWidth;
3065     var b = p[1] + el.offsetHeight;
3066     var l = p[0];
3067
3068     return new Roo.lib.Region(t, r, b, l);
3069 };
3070 /*
3071  * Portions of this file are based on pieces of Yahoo User Interface Library
3072  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3073  * YUI licensed under the BSD License:
3074  * http://developer.yahoo.net/yui/license.txt
3075  * <script type="text/javascript">
3076  *
3077  */
3078 //@@dep Roo.lib.Region
3079
3080
3081 Roo.lib.Point = function(x, y) {
3082     if (x instanceof Array) {
3083         y = x[1];
3084         x = x[0];
3085     }
3086     this.x = this.right = this.left = this[0] = x;
3087     this.y = this.top = this.bottom = this[1] = y;
3088 };
3089
3090 Roo.lib.Point.prototype = new Roo.lib.Region();
3091 /*
3092  * Portions of this file are based on pieces of Yahoo User Interface Library
3093  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3094  * YUI licensed under the BSD License:
3095  * http://developer.yahoo.net/yui/license.txt
3096  * <script type="text/javascript">
3097  *
3098  */
3099  
3100 (function() {   
3101
3102     Roo.lib.Anim = {
3103         scroll : function(el, args, duration, easing, cb, scope) {
3104             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3105         },
3106
3107         motion : function(el, args, duration, easing, cb, scope) {
3108             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3109         },
3110
3111         color : function(el, args, duration, easing, cb, scope) {
3112             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3113         },
3114
3115         run : function(el, args, duration, easing, cb, scope, type) {
3116             type = type || Roo.lib.AnimBase;
3117             if (typeof easing == "string") {
3118                 easing = Roo.lib.Easing[easing];
3119             }
3120             var anim = new type(el, args, duration, easing);
3121             anim.animateX(function() {
3122                 Roo.callback(cb, scope);
3123             });
3124             return anim;
3125         }
3126     };
3127 })();/*
3128  * Portions of this file are based on pieces of Yahoo User Interface Library
3129  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3130  * YUI licensed under the BSD License:
3131  * http://developer.yahoo.net/yui/license.txt
3132  * <script type="text/javascript">
3133  *
3134  */
3135
3136 (function() {    
3137     var libFlyweight;
3138     
3139     function fly(el) {
3140         if (!libFlyweight) {
3141             libFlyweight = new Roo.Element.Flyweight();
3142         }
3143         libFlyweight.dom = el;
3144         return libFlyweight;
3145     }
3146
3147     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3148     
3149    
3150     
3151     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3152         if (el) {
3153             this.init(el, attributes, duration, method);
3154         }
3155     };
3156
3157     Roo.lib.AnimBase.fly = fly;
3158     
3159     
3160     
3161     Roo.lib.AnimBase.prototype = {
3162
3163         toString: function() {
3164             var el = this.getEl();
3165             var id = el.id || el.tagName;
3166             return ("Anim " + id);
3167         },
3168
3169         patterns: {
3170             noNegatives:        /width|height|opacity|padding/i,
3171             offsetAttribute:  /^((width|height)|(top|left))$/,
3172             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3173             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3174         },
3175
3176
3177         doMethod: function(attr, start, end) {
3178             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3179         },
3180
3181
3182         setAttribute: function(attr, val, unit) {
3183             if (this.patterns.noNegatives.test(attr)) {
3184                 val = (val > 0) ? val : 0;
3185             }
3186
3187             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3188         },
3189
3190
3191         getAttribute: function(attr) {
3192             var el = this.getEl();
3193             var val = fly(el).getStyle(attr);
3194
3195             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3196                 return parseFloat(val);
3197             }
3198
3199             var a = this.patterns.offsetAttribute.exec(attr) || [];
3200             var pos = !!( a[3] );
3201             var box = !!( a[2] );
3202
3203
3204             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3205                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3206             } else {
3207                 val = 0;
3208             }
3209
3210             return val;
3211         },
3212
3213
3214         getDefaultUnit: function(attr) {
3215             if (this.patterns.defaultUnit.test(attr)) {
3216                 return 'px';
3217             }
3218
3219             return '';
3220         },
3221
3222         animateX : function(callback, scope) {
3223             var f = function() {
3224                 this.onComplete.removeListener(f);
3225                 if (typeof callback == "function") {
3226                     callback.call(scope || this, this);
3227                 }
3228             };
3229             this.onComplete.addListener(f, this);
3230             this.animate();
3231         },
3232
3233
3234         setRuntimeAttribute: function(attr) {
3235             var start;
3236             var end;
3237             var attributes = this.attributes;
3238
3239             this.runtimeAttributes[attr] = {};
3240
3241             var isset = function(prop) {
3242                 return (typeof prop !== 'undefined');
3243             };
3244
3245             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3246                 return false;
3247             }
3248
3249             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3250
3251
3252             if (isset(attributes[attr]['to'])) {
3253                 end = attributes[attr]['to'];
3254             } else if (isset(attributes[attr]['by'])) {
3255                 if (start.constructor == Array) {
3256                     end = [];
3257                     for (var i = 0, len = start.length; i < len; ++i) {
3258                         end[i] = start[i] + attributes[attr]['by'][i];
3259                     }
3260                 } else {
3261                     end = start + attributes[attr]['by'];
3262                 }
3263             }
3264
3265             this.runtimeAttributes[attr].start = start;
3266             this.runtimeAttributes[attr].end = end;
3267
3268
3269             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3270         },
3271
3272
3273         init: function(el, attributes, duration, method) {
3274
3275             var isAnimated = false;
3276
3277
3278             var startTime = null;
3279
3280
3281             var actualFrames = 0;
3282
3283
3284             el = Roo.getDom(el);
3285
3286
3287             this.attributes = attributes || {};
3288
3289
3290             this.duration = duration || 1;
3291
3292
3293             this.method = method || Roo.lib.Easing.easeNone;
3294
3295
3296             this.useSeconds = true;
3297
3298
3299             this.currentFrame = 0;
3300
3301
3302             this.totalFrames = Roo.lib.AnimMgr.fps;
3303
3304
3305             this.getEl = function() {
3306                 return el;
3307             };
3308
3309
3310             this.isAnimated = function() {
3311                 return isAnimated;
3312             };
3313
3314
3315             this.getStartTime = function() {
3316                 return startTime;
3317             };
3318
3319             this.runtimeAttributes = {};
3320
3321
3322             this.animate = function() {
3323                 if (this.isAnimated()) {
3324                     return false;
3325                 }
3326
3327                 this.currentFrame = 0;
3328
3329                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3330
3331                 Roo.lib.AnimMgr.registerElement(this);
3332             };
3333
3334
3335             this.stop = function(finish) {
3336                 if (finish) {
3337                     this.currentFrame = this.totalFrames;
3338                     this._onTween.fire();
3339                 }
3340                 Roo.lib.AnimMgr.stop(this);
3341             };
3342
3343             var onStart = function() {
3344                 this.onStart.fire();
3345
3346                 this.runtimeAttributes = {};
3347                 for (var attr in this.attributes) {
3348                     this.setRuntimeAttribute(attr);
3349                 }
3350
3351                 isAnimated = true;
3352                 actualFrames = 0;
3353                 startTime = new Date();
3354             };
3355
3356
3357             var onTween = function() {
3358                 var data = {
3359                     duration: new Date() - this.getStartTime(),
3360                     currentFrame: this.currentFrame
3361                 };
3362
3363                 data.toString = function() {
3364                     return (
3365                             'duration: ' + data.duration +
3366                             ', currentFrame: ' + data.currentFrame
3367                             );
3368                 };
3369
3370                 this.onTween.fire(data);
3371
3372                 var runtimeAttributes = this.runtimeAttributes;
3373
3374                 for (var attr in runtimeAttributes) {
3375                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3376                 }
3377
3378                 actualFrames += 1;
3379             };
3380
3381             var onComplete = function() {
3382                 var actual_duration = (new Date() - startTime) / 1000 ;
3383
3384                 var data = {
3385                     duration: actual_duration,
3386                     frames: actualFrames,
3387                     fps: actualFrames / actual_duration
3388                 };
3389
3390                 data.toString = function() {
3391                     return (
3392                             'duration: ' + data.duration +
3393                             ', frames: ' + data.frames +
3394                             ', fps: ' + data.fps
3395                             );
3396                 };
3397
3398                 isAnimated = false;
3399                 actualFrames = 0;
3400                 this.onComplete.fire(data);
3401             };
3402
3403
3404             this._onStart = new Roo.util.Event(this);
3405             this.onStart = new Roo.util.Event(this);
3406             this.onTween = new Roo.util.Event(this);
3407             this._onTween = new Roo.util.Event(this);
3408             this.onComplete = new Roo.util.Event(this);
3409             this._onComplete = new Roo.util.Event(this);
3410             this._onStart.addListener(onStart);
3411             this._onTween.addListener(onTween);
3412             this._onComplete.addListener(onComplete);
3413         }
3414     };
3415 })();
3416 /*
3417  * Portions of this file are based on pieces of Yahoo User Interface Library
3418  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3419  * YUI licensed under the BSD License:
3420  * http://developer.yahoo.net/yui/license.txt
3421  * <script type="text/javascript">
3422  *
3423  */
3424
3425 Roo.lib.AnimMgr = new function() {
3426
3427     var thread = null;
3428
3429
3430     var queue = [];
3431
3432
3433     var tweenCount = 0;
3434
3435
3436     this.fps = 1000;
3437
3438
3439     this.delay = 1;
3440
3441
3442     this.registerElement = function(tween) {
3443         queue[queue.length] = tween;
3444         tweenCount += 1;
3445         tween._onStart.fire();
3446         this.start();
3447     };
3448
3449
3450     this.unRegister = function(tween, index) {
3451         tween._onComplete.fire();
3452         index = index || getIndex(tween);
3453         if (index != -1) {
3454             queue.splice(index, 1);
3455         }
3456
3457         tweenCount -= 1;
3458         if (tweenCount <= 0) {
3459             this.stop();
3460         }
3461     };
3462
3463
3464     this.start = function() {
3465         if (thread === null) {
3466             thread = setInterval(this.run, this.delay);
3467         }
3468     };
3469
3470
3471     this.stop = function(tween) {
3472         if (!tween) {
3473             clearInterval(thread);
3474
3475             for (var i = 0, len = queue.length; i < len; ++i) {
3476                 if (queue[0].isAnimated()) {
3477                     this.unRegister(queue[0], 0);
3478                 }
3479             }
3480
3481             queue = [];
3482             thread = null;
3483             tweenCount = 0;
3484         }
3485         else {
3486             this.unRegister(tween);
3487         }
3488     };
3489
3490
3491     this.run = function() {
3492         for (var i = 0, len = queue.length; i < len; ++i) {
3493             var tween = queue[i];
3494             if (!tween || !tween.isAnimated()) {
3495                 continue;
3496             }
3497
3498             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3499             {
3500                 tween.currentFrame += 1;
3501
3502                 if (tween.useSeconds) {
3503                     correctFrame(tween);
3504                 }
3505                 tween._onTween.fire();
3506             }
3507             else {
3508                 Roo.lib.AnimMgr.stop(tween, i);
3509             }
3510         }
3511     };
3512
3513     var getIndex = function(anim) {
3514         for (var i = 0, len = queue.length; i < len; ++i) {
3515             if (queue[i] == anim) {
3516                 return i;
3517             }
3518         }
3519         return -1;
3520     };
3521
3522
3523     var correctFrame = function(tween) {
3524         var frames = tween.totalFrames;
3525         var frame = tween.currentFrame;
3526         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3527         var elapsed = (new Date() - tween.getStartTime());
3528         var tweak = 0;
3529
3530         if (elapsed < tween.duration * 1000) {
3531             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3532         } else {
3533             tweak = frames - (frame + 1);
3534         }
3535         if (tweak > 0 && isFinite(tweak)) {
3536             if (tween.currentFrame + tweak >= frames) {
3537                 tweak = frames - (frame + 1);
3538             }
3539
3540             tween.currentFrame += tweak;
3541         }
3542     };
3543 };
3544
3545     /*
3546  * Portions of this file are based on pieces of Yahoo User Interface Library
3547  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3548  * YUI licensed under the BSD License:
3549  * http://developer.yahoo.net/yui/license.txt
3550  * <script type="text/javascript">
3551  *
3552  */
3553 Roo.lib.Bezier = new function() {
3554
3555         this.getPosition = function(points, t) {
3556             var n = points.length;
3557             var tmp = [];
3558
3559             for (var i = 0; i < n; ++i) {
3560                 tmp[i] = [points[i][0], points[i][1]];
3561             }
3562
3563             for (var j = 1; j < n; ++j) {
3564                 for (i = 0; i < n - j; ++i) {
3565                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3566                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3567                 }
3568             }
3569
3570             return [ tmp[0][0], tmp[0][1] ];
3571
3572         };
3573     };/*
3574  * Portions of this file are based on pieces of Yahoo User Interface Library
3575  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3576  * YUI licensed under the BSD License:
3577  * http://developer.yahoo.net/yui/license.txt
3578  * <script type="text/javascript">
3579  *
3580  */
3581 (function() {
3582
3583     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3584         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3585     };
3586
3587     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3588
3589     var fly = Roo.lib.AnimBase.fly;
3590     var Y = Roo.lib;
3591     var superclass = Y.ColorAnim.superclass;
3592     var proto = Y.ColorAnim.prototype;
3593
3594     proto.toString = function() {
3595         var el = this.getEl();
3596         var id = el.id || el.tagName;
3597         return ("ColorAnim " + id);
3598     };
3599
3600     proto.patterns.color = /color$/i;
3601     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3602     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3603     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3604     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3605
3606
3607     proto.parseColor = function(s) {
3608         if (s.length == 3) {
3609             return s;
3610         }
3611
3612         var c = this.patterns.hex.exec(s);
3613         if (c && c.length == 4) {
3614             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3615         }
3616
3617         c = this.patterns.rgb.exec(s);
3618         if (c && c.length == 4) {
3619             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3620         }
3621
3622         c = this.patterns.hex3.exec(s);
3623         if (c && c.length == 4) {
3624             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3625         }
3626
3627         return null;
3628     };
3629     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3630     proto.getAttribute = function(attr) {
3631         var el = this.getEl();
3632         if (this.patterns.color.test(attr)) {
3633             var val = fly(el).getStyle(attr);
3634
3635             if (this.patterns.transparent.test(val)) {
3636                 var parent = el.parentNode;
3637                 val = fly(parent).getStyle(attr);
3638
3639                 while (parent && this.patterns.transparent.test(val)) {
3640                     parent = parent.parentNode;
3641                     val = fly(parent).getStyle(attr);
3642                     if (parent.tagName.toUpperCase() == 'HTML') {
3643                         val = '#fff';
3644                     }
3645                 }
3646             }
3647         } else {
3648             val = superclass.getAttribute.call(this, attr);
3649         }
3650
3651         return val;
3652     };
3653     proto.getAttribute = function(attr) {
3654         var el = this.getEl();
3655         if (this.patterns.color.test(attr)) {
3656             var val = fly(el).getStyle(attr);
3657
3658             if (this.patterns.transparent.test(val)) {
3659                 var parent = el.parentNode;
3660                 val = fly(parent).getStyle(attr);
3661
3662                 while (parent && this.patterns.transparent.test(val)) {
3663                     parent = parent.parentNode;
3664                     val = fly(parent).getStyle(attr);
3665                     if (parent.tagName.toUpperCase() == 'HTML') {
3666                         val = '#fff';
3667                     }
3668                 }
3669             }
3670         } else {
3671             val = superclass.getAttribute.call(this, attr);
3672         }
3673
3674         return val;
3675     };
3676
3677     proto.doMethod = function(attr, start, end) {
3678         var val;
3679
3680         if (this.patterns.color.test(attr)) {
3681             val = [];
3682             for (var i = 0, len = start.length; i < len; ++i) {
3683                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3684             }
3685
3686             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3687         }
3688         else {
3689             val = superclass.doMethod.call(this, attr, start, end);
3690         }
3691
3692         return val;
3693     };
3694
3695     proto.setRuntimeAttribute = function(attr) {
3696         superclass.setRuntimeAttribute.call(this, attr);
3697
3698         if (this.patterns.color.test(attr)) {
3699             var attributes = this.attributes;
3700             var start = this.parseColor(this.runtimeAttributes[attr].start);
3701             var end = this.parseColor(this.runtimeAttributes[attr].end);
3702
3703             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3704                 end = this.parseColor(attributes[attr].by);
3705
3706                 for (var i = 0, len = start.length; i < len; ++i) {
3707                     end[i] = start[i] + end[i];
3708                 }
3709             }
3710
3711             this.runtimeAttributes[attr].start = start;
3712             this.runtimeAttributes[attr].end = end;
3713         }
3714     };
3715 })();
3716
3717 /*
3718  * Portions of this file are based on pieces of Yahoo User Interface Library
3719  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3720  * YUI licensed under the BSD License:
3721  * http://developer.yahoo.net/yui/license.txt
3722  * <script type="text/javascript">
3723  *
3724  */
3725 Roo.lib.Easing = {
3726
3727
3728     easeNone: function (t, b, c, d) {
3729         return c * t / d + b;
3730     },
3731
3732
3733     easeIn: function (t, b, c, d) {
3734         return c * (t /= d) * t + b;
3735     },
3736
3737
3738     easeOut: function (t, b, c, d) {
3739         return -c * (t /= d) * (t - 2) + b;
3740     },
3741
3742
3743     easeBoth: function (t, b, c, d) {
3744         if ((t /= d / 2) < 1) {
3745             return c / 2 * t * t + b;
3746         }
3747
3748         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3749     },
3750
3751
3752     easeInStrong: function (t, b, c, d) {
3753         return c * (t /= d) * t * t * t + b;
3754     },
3755
3756
3757     easeOutStrong: function (t, b, c, d) {
3758         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3759     },
3760
3761
3762     easeBothStrong: function (t, b, c, d) {
3763         if ((t /= d / 2) < 1) {
3764             return c / 2 * t * t * t * t + b;
3765         }
3766
3767         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3768     },
3769
3770
3771
3772     elasticIn: function (t, b, c, d, a, p) {
3773         if (t == 0) {
3774             return b;
3775         }
3776         if ((t /= d) == 1) {
3777             return b + c;
3778         }
3779         if (!p) {
3780             p = d * .3;
3781         }
3782
3783         if (!a || a < Math.abs(c)) {
3784             a = c;
3785             var s = p / 4;
3786         }
3787         else {
3788             var s = p / (2 * Math.PI) * Math.asin(c / a);
3789         }
3790
3791         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3792     },
3793
3794
3795     elasticOut: function (t, b, c, d, a, p) {
3796         if (t == 0) {
3797             return b;
3798         }
3799         if ((t /= d) == 1) {
3800             return b + c;
3801         }
3802         if (!p) {
3803             p = d * .3;
3804         }
3805
3806         if (!a || a < Math.abs(c)) {
3807             a = c;
3808             var s = p / 4;
3809         }
3810         else {
3811             var s = p / (2 * Math.PI) * Math.asin(c / a);
3812         }
3813
3814         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3815     },
3816
3817
3818     elasticBoth: function (t, b, c, d, a, p) {
3819         if (t == 0) {
3820             return b;
3821         }
3822
3823         if ((t /= d / 2) == 2) {
3824             return b + c;
3825         }
3826
3827         if (!p) {
3828             p = d * (.3 * 1.5);
3829         }
3830
3831         if (!a || a < Math.abs(c)) {
3832             a = c;
3833             var s = p / 4;
3834         }
3835         else {
3836             var s = p / (2 * Math.PI) * Math.asin(c / a);
3837         }
3838
3839         if (t < 1) {
3840             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3841                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3842         }
3843         return a * Math.pow(2, -10 * (t -= 1)) *
3844                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3845     },
3846
3847
3848
3849     backIn: function (t, b, c, d, s) {
3850         if (typeof s == 'undefined') {
3851             s = 1.70158;
3852         }
3853         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3854     },
3855
3856
3857     backOut: function (t, b, c, d, s) {
3858         if (typeof s == 'undefined') {
3859             s = 1.70158;
3860         }
3861         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3862     },
3863
3864
3865     backBoth: function (t, b, c, d, s) {
3866         if (typeof s == 'undefined') {
3867             s = 1.70158;
3868         }
3869
3870         if ((t /= d / 2 ) < 1) {
3871             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3872         }
3873         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3874     },
3875
3876
3877     bounceIn: function (t, b, c, d) {
3878         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3879     },
3880
3881
3882     bounceOut: function (t, b, c, d) {
3883         if ((t /= d) < (1 / 2.75)) {
3884             return c * (7.5625 * t * t) + b;
3885         } else if (t < (2 / 2.75)) {
3886             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3887         } else if (t < (2.5 / 2.75)) {
3888             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3889         }
3890         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3891     },
3892
3893
3894     bounceBoth: function (t, b, c, d) {
3895         if (t < d / 2) {
3896             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3897         }
3898         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3899     }
3900 };/*
3901  * Portions of this file are based on pieces of Yahoo User Interface Library
3902  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3903  * YUI licensed under the BSD License:
3904  * http://developer.yahoo.net/yui/license.txt
3905  * <script type="text/javascript">
3906  *
3907  */
3908     (function() {
3909         Roo.lib.Motion = function(el, attributes, duration, method) {
3910             if (el) {
3911                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3912             }
3913         };
3914
3915         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3916
3917
3918         var Y = Roo.lib;
3919         var superclass = Y.Motion.superclass;
3920         var proto = Y.Motion.prototype;
3921
3922         proto.toString = function() {
3923             var el = this.getEl();
3924             var id = el.id || el.tagName;
3925             return ("Motion " + id);
3926         };
3927
3928         proto.patterns.points = /^points$/i;
3929
3930         proto.setAttribute = function(attr, val, unit) {
3931             if (this.patterns.points.test(attr)) {
3932                 unit = unit || 'px';
3933                 superclass.setAttribute.call(this, 'left', val[0], unit);
3934                 superclass.setAttribute.call(this, 'top', val[1], unit);
3935             } else {
3936                 superclass.setAttribute.call(this, attr, val, unit);
3937             }
3938         };
3939
3940         proto.getAttribute = function(attr) {
3941             if (this.patterns.points.test(attr)) {
3942                 var val = [
3943                         superclass.getAttribute.call(this, 'left'),
3944                         superclass.getAttribute.call(this, 'top')
3945                         ];
3946             } else {
3947                 val = superclass.getAttribute.call(this, attr);
3948             }
3949
3950             return val;
3951         };
3952
3953         proto.doMethod = function(attr, start, end) {
3954             var val = null;
3955
3956             if (this.patterns.points.test(attr)) {
3957                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3958                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3959             } else {
3960                 val = superclass.doMethod.call(this, attr, start, end);
3961             }
3962             return val;
3963         };
3964
3965         proto.setRuntimeAttribute = function(attr) {
3966             if (this.patterns.points.test(attr)) {
3967                 var el = this.getEl();
3968                 var attributes = this.attributes;
3969                 var start;
3970                 var control = attributes['points']['control'] || [];
3971                 var end;
3972                 var i, len;
3973
3974                 if (control.length > 0 && !(control[0] instanceof Array)) {
3975                     control = [control];
3976                 } else {
3977                     var tmp = [];
3978                     for (i = 0,len = control.length; i < len; ++i) {
3979                         tmp[i] = control[i];
3980                     }
3981                     control = tmp;
3982                 }
3983
3984                 Roo.fly(el).position();
3985
3986                 if (isset(attributes['points']['from'])) {
3987                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3988                 }
3989                 else {
3990                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3991                 }
3992
3993                 start = this.getAttribute('points');
3994
3995
3996                 if (isset(attributes['points']['to'])) {
3997                     end = translateValues.call(this, attributes['points']['to'], start);
3998
3999                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4000                     for (i = 0,len = control.length; i < len; ++i) {
4001                         control[i] = translateValues.call(this, control[i], start);
4002                     }
4003
4004
4005                 } else if (isset(attributes['points']['by'])) {
4006                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4007
4008                     for (i = 0,len = control.length; i < len; ++i) {
4009                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4010                     }
4011                 }
4012
4013                 this.runtimeAttributes[attr] = [start];
4014
4015                 if (control.length > 0) {
4016                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4017                 }
4018
4019                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4020             }
4021             else {
4022                 superclass.setRuntimeAttribute.call(this, attr);
4023             }
4024         };
4025
4026         var translateValues = function(val, start) {
4027             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4028             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4029
4030             return val;
4031         };
4032
4033         var isset = function(prop) {
4034             return (typeof prop !== 'undefined');
4035         };
4036     })();
4037 /*
4038  * Portions of this file are based on pieces of Yahoo User Interface Library
4039  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4040  * YUI licensed under the BSD License:
4041  * http://developer.yahoo.net/yui/license.txt
4042  * <script type="text/javascript">
4043  *
4044  */
4045     (function() {
4046         Roo.lib.Scroll = function(el, attributes, duration, method) {
4047             if (el) {
4048                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4049             }
4050         };
4051
4052         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4053
4054
4055         var Y = Roo.lib;
4056         var superclass = Y.Scroll.superclass;
4057         var proto = Y.Scroll.prototype;
4058
4059         proto.toString = function() {
4060             var el = this.getEl();
4061             var id = el.id || el.tagName;
4062             return ("Scroll " + id);
4063         };
4064
4065         proto.doMethod = function(attr, start, end) {
4066             var val = null;
4067
4068             if (attr == 'scroll') {
4069                 val = [
4070                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4071                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4072                         ];
4073
4074             } else {
4075                 val = superclass.doMethod.call(this, attr, start, end);
4076             }
4077             return val;
4078         };
4079
4080         proto.getAttribute = function(attr) {
4081             var val = null;
4082             var el = this.getEl();
4083
4084             if (attr == 'scroll') {
4085                 val = [ el.scrollLeft, el.scrollTop ];
4086             } else {
4087                 val = superclass.getAttribute.call(this, attr);
4088             }
4089
4090             return val;
4091         };
4092
4093         proto.setAttribute = function(attr, val, unit) {
4094             var el = this.getEl();
4095
4096             if (attr == 'scroll') {
4097                 el.scrollLeft = val[0];
4098                 el.scrollTop = val[1];
4099             } else {
4100                 superclass.setAttribute.call(this, attr, val, unit);
4101             }
4102         };
4103     })();
4104 /*
4105  * Based on:
4106  * Ext JS Library 1.1.1
4107  * Copyright(c) 2006-2007, Ext JS, LLC.
4108  *
4109  * Originally Released Under LGPL - original licence link has changed is not relivant.
4110  *
4111  * Fork - LGPL
4112  * <script type="text/javascript">
4113  */
4114
4115
4116 // nasty IE9 hack - what a pile of crap that is..
4117
4118  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4119     Range.prototype.createContextualFragment = function (html) {
4120         var doc = window.document;
4121         var container = doc.createElement("div");
4122         container.innerHTML = html;
4123         var frag = doc.createDocumentFragment(), n;
4124         while ((n = container.firstChild)) {
4125             frag.appendChild(n);
4126         }
4127         return frag;
4128     };
4129 }
4130
4131 /**
4132  * @class Roo.DomHelper
4133  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4134  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4135  * @singleton
4136  */
4137 Roo.DomHelper = function(){
4138     var tempTableEl = null;
4139     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4140     var tableRe = /^table|tbody|tr|td$/i;
4141     var xmlns = {};
4142     // build as innerHTML where available
4143     /** @ignore */
4144     var createHtml = function(o){
4145         if(typeof o == 'string'){
4146             return o;
4147         }
4148         var b = "";
4149         if(!o.tag){
4150             o.tag = "div";
4151         }
4152         b += "<" + o.tag;
4153         for(var attr in o){
4154             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4155             if(attr == "style"){
4156                 var s = o["style"];
4157                 if(typeof s == "function"){
4158                     s = s.call();
4159                 }
4160                 if(typeof s == "string"){
4161                     b += ' style="' + s + '"';
4162                 }else if(typeof s == "object"){
4163                     b += ' style="';
4164                     for(var key in s){
4165                         if(typeof s[key] != "function"){
4166                             b += key + ":" + s[key] + ";";
4167                         }
4168                     }
4169                     b += '"';
4170                 }
4171             }else{
4172                 if(attr == "cls"){
4173                     b += ' class="' + o["cls"] + '"';
4174                 }else if(attr == "htmlFor"){
4175                     b += ' for="' + o["htmlFor"] + '"';
4176                 }else{
4177                     b += " " + attr + '="' + o[attr] + '"';
4178                 }
4179             }
4180         }
4181         if(emptyTags.test(o.tag)){
4182             b += "/>";
4183         }else{
4184             b += ">";
4185             var cn = o.children || o.cn;
4186             if(cn){
4187                 //http://bugs.kde.org/show_bug.cgi?id=71506
4188                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4189                     for(var i = 0, len = cn.length; i < len; i++) {
4190                         b += createHtml(cn[i], b);
4191                     }
4192                 }else{
4193                     b += createHtml(cn, b);
4194                 }
4195             }
4196             if(o.html){
4197                 b += o.html;
4198             }
4199             b += "</" + o.tag + ">";
4200         }
4201         return b;
4202     };
4203
4204     // build as dom
4205     /** @ignore */
4206     var createDom = function(o, parentNode){
4207          
4208         // defininition craeted..
4209         var ns = false;
4210         if (o.ns && o.ns != 'html') {
4211                
4212             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4213                 xmlns[o.ns] = o.xmlns;
4214                 ns = o.xmlns;
4215             }
4216             if (typeof(xmlns[o.ns]) == 'undefined') {
4217                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4218             }
4219             ns = xmlns[o.ns];
4220         }
4221         
4222         
4223         if (typeof(o) == 'string') {
4224             return parentNode.appendChild(document.createTextNode(o));
4225         }
4226         o.tag = o.tag || div;
4227         if (o.ns && Roo.isIE) {
4228             ns = false;
4229             o.tag = o.ns + ':' + o.tag;
4230             
4231         }
4232         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4233         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4234         for(var attr in o){
4235             
4236             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4237                     attr == "style" || typeof o[attr] == "function") continue;
4238                     
4239             if(attr=="cls" && Roo.isIE){
4240                 el.className = o["cls"];
4241             }else{
4242                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4243                 else { 
4244                     el[attr] = o[attr];
4245                 }
4246             }
4247         }
4248         Roo.DomHelper.applyStyles(el, o.style);
4249         var cn = o.children || o.cn;
4250         if(cn){
4251             //http://bugs.kde.org/show_bug.cgi?id=71506
4252              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4253                 for(var i = 0, len = cn.length; i < len; i++) {
4254                     createDom(cn[i], el);
4255                 }
4256             }else{
4257                 createDom(cn, el);
4258             }
4259         }
4260         if(o.html){
4261             el.innerHTML = o.html;
4262         }
4263         if(parentNode){
4264            parentNode.appendChild(el);
4265         }
4266         return el;
4267     };
4268
4269     var ieTable = function(depth, s, h, e){
4270         tempTableEl.innerHTML = [s, h, e].join('');
4271         var i = -1, el = tempTableEl;
4272         while(++i < depth){
4273             el = el.firstChild;
4274         }
4275         return el;
4276     };
4277
4278     // kill repeat to save bytes
4279     var ts = '<table>',
4280         te = '</table>',
4281         tbs = ts+'<tbody>',
4282         tbe = '</tbody>'+te,
4283         trs = tbs + '<tr>',
4284         tre = '</tr>'+tbe;
4285
4286     /**
4287      * @ignore
4288      * Nasty code for IE's broken table implementation
4289      */
4290     var insertIntoTable = function(tag, where, el, html){
4291         if(!tempTableEl){
4292             tempTableEl = document.createElement('div');
4293         }
4294         var node;
4295         var before = null;
4296         if(tag == 'td'){
4297             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4298                 return;
4299             }
4300             if(where == 'beforebegin'){
4301                 before = el;
4302                 el = el.parentNode;
4303             } else{
4304                 before = el.nextSibling;
4305                 el = el.parentNode;
4306             }
4307             node = ieTable(4, trs, html, tre);
4308         }
4309         else if(tag == 'tr'){
4310             if(where == 'beforebegin'){
4311                 before = el;
4312                 el = el.parentNode;
4313                 node = ieTable(3, tbs, html, tbe);
4314             } else if(where == 'afterend'){
4315                 before = el.nextSibling;
4316                 el = el.parentNode;
4317                 node = ieTable(3, tbs, html, tbe);
4318             } else{ // INTO a TR
4319                 if(where == 'afterbegin'){
4320                     before = el.firstChild;
4321                 }
4322                 node = ieTable(4, trs, html, tre);
4323             }
4324         } else if(tag == 'tbody'){
4325             if(where == 'beforebegin'){
4326                 before = el;
4327                 el = el.parentNode;
4328                 node = ieTable(2, ts, html, te);
4329             } else if(where == 'afterend'){
4330                 before = el.nextSibling;
4331                 el = el.parentNode;
4332                 node = ieTable(2, ts, html, te);
4333             } else{
4334                 if(where == 'afterbegin'){
4335                     before = el.firstChild;
4336                 }
4337                 node = ieTable(3, tbs, html, tbe);
4338             }
4339         } else{ // TABLE
4340             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4341                 return;
4342             }
4343             if(where == 'afterbegin'){
4344                 before = el.firstChild;
4345             }
4346             node = ieTable(2, ts, html, te);
4347         }
4348         el.insertBefore(node, before);
4349         return node;
4350     };
4351
4352     return {
4353     /** True to force the use of DOM instead of html fragments @type Boolean */
4354     useDom : false,
4355
4356     /**
4357      * Returns the markup for the passed Element(s) config
4358      * @param {Object} o The Dom object spec (and children)
4359      * @return {String}
4360      */
4361     markup : function(o){
4362         return createHtml(o);
4363     },
4364
4365     /**
4366      * Applies a style specification to an element
4367      * @param {String/HTMLElement} el The element to apply styles to
4368      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4369      * a function which returns such a specification.
4370      */
4371     applyStyles : function(el, styles){
4372         if(styles){
4373            el = Roo.fly(el);
4374            if(typeof styles == "string"){
4375                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4376                var matches;
4377                while ((matches = re.exec(styles)) != null){
4378                    el.setStyle(matches[1], matches[2]);
4379                }
4380            }else if (typeof styles == "object"){
4381                for (var style in styles){
4382                   el.setStyle(style, styles[style]);
4383                }
4384            }else if (typeof styles == "function"){
4385                 Roo.DomHelper.applyStyles(el, styles.call());
4386            }
4387         }
4388     },
4389
4390     /**
4391      * Inserts an HTML fragment into the Dom
4392      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4393      * @param {HTMLElement} el The context element
4394      * @param {String} html The HTML fragmenet
4395      * @return {HTMLElement} The new node
4396      */
4397     insertHtml : function(where, el, html){
4398         where = where.toLowerCase();
4399         if(el.insertAdjacentHTML){
4400             if(tableRe.test(el.tagName)){
4401                 var rs;
4402                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4403                     return rs;
4404                 }
4405             }
4406             switch(where){
4407                 case "beforebegin":
4408                     el.insertAdjacentHTML('BeforeBegin', html);
4409                     return el.previousSibling;
4410                 case "afterbegin":
4411                     el.insertAdjacentHTML('AfterBegin', html);
4412                     return el.firstChild;
4413                 case "beforeend":
4414                     el.insertAdjacentHTML('BeforeEnd', html);
4415                     return el.lastChild;
4416                 case "afterend":
4417                     el.insertAdjacentHTML('AfterEnd', html);
4418                     return el.nextSibling;
4419             }
4420             throw 'Illegal insertion point -> "' + where + '"';
4421         }
4422         var range = el.ownerDocument.createRange();
4423         var frag;
4424         switch(where){
4425              case "beforebegin":
4426                 range.setStartBefore(el);
4427                 frag = range.createContextualFragment(html);
4428                 el.parentNode.insertBefore(frag, el);
4429                 return el.previousSibling;
4430              case "afterbegin":
4431                 if(el.firstChild){
4432                     range.setStartBefore(el.firstChild);
4433                     frag = range.createContextualFragment(html);
4434                     el.insertBefore(frag, el.firstChild);
4435                     return el.firstChild;
4436                 }else{
4437                     el.innerHTML = html;
4438                     return el.firstChild;
4439                 }
4440             case "beforeend":
4441                 if(el.lastChild){
4442                     range.setStartAfter(el.lastChild);
4443                     frag = range.createContextualFragment(html);
4444                     el.appendChild(frag);
4445                     return el.lastChild;
4446                 }else{
4447                     el.innerHTML = html;
4448                     return el.lastChild;
4449                 }
4450             case "afterend":
4451                 range.setStartAfter(el);
4452                 frag = range.createContextualFragment(html);
4453                 el.parentNode.insertBefore(frag, el.nextSibling);
4454                 return el.nextSibling;
4455             }
4456             throw 'Illegal insertion point -> "' + where + '"';
4457     },
4458
4459     /**
4460      * Creates new Dom element(s) and inserts them before el
4461      * @param {String/HTMLElement/Element} el The context element
4462      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4463      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4464      * @return {HTMLElement/Roo.Element} The new node
4465      */
4466     insertBefore : function(el, o, returnElement){
4467         return this.doInsert(el, o, returnElement, "beforeBegin");
4468     },
4469
4470     /**
4471      * Creates new Dom element(s) and inserts them after el
4472      * @param {String/HTMLElement/Element} el The context element
4473      * @param {Object} o The Dom object spec (and children)
4474      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4475      * @return {HTMLElement/Roo.Element} The new node
4476      */
4477     insertAfter : function(el, o, returnElement){
4478         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4479     },
4480
4481     /**
4482      * Creates new Dom element(s) and inserts them as the first child of el
4483      * @param {String/HTMLElement/Element} el The context element
4484      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4485      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4486      * @return {HTMLElement/Roo.Element} The new node
4487      */
4488     insertFirst : function(el, o, returnElement){
4489         return this.doInsert(el, o, returnElement, "afterBegin");
4490     },
4491
4492     // private
4493     doInsert : function(el, o, returnElement, pos, sibling){
4494         el = Roo.getDom(el);
4495         var newNode;
4496         if(this.useDom || o.ns){
4497             newNode = createDom(o, null);
4498             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4499         }else{
4500             var html = createHtml(o);
4501             newNode = this.insertHtml(pos, el, html);
4502         }
4503         return returnElement ? Roo.get(newNode, true) : newNode;
4504     },
4505
4506     /**
4507      * Creates new Dom element(s) and appends them to el
4508      * @param {String/HTMLElement/Element} el The context element
4509      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4510      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4511      * @return {HTMLElement/Roo.Element} The new node
4512      */
4513     append : function(el, o, returnElement){
4514         el = Roo.getDom(el);
4515         var newNode;
4516         if(this.useDom || o.ns){
4517             newNode = createDom(o, null);
4518             el.appendChild(newNode);
4519         }else{
4520             var html = createHtml(o);
4521             newNode = this.insertHtml("beforeEnd", el, html);
4522         }
4523         return returnElement ? Roo.get(newNode, true) : newNode;
4524     },
4525
4526     /**
4527      * Creates new Dom element(s) and overwrites the contents of el with them
4528      * @param {String/HTMLElement/Element} el The context element
4529      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4530      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4531      * @return {HTMLElement/Roo.Element} The new node
4532      */
4533     overwrite : function(el, o, returnElement){
4534         el = Roo.getDom(el);
4535         if (o.ns) {
4536           
4537             while (el.childNodes.length) {
4538                 el.removeChild(el.firstChild);
4539             }
4540             createDom(o, el);
4541         } else {
4542             el.innerHTML = createHtml(o);   
4543         }
4544         
4545         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4546     },
4547
4548     /**
4549      * Creates a new Roo.DomHelper.Template from the Dom object spec
4550      * @param {Object} o The Dom object spec (and children)
4551      * @return {Roo.DomHelper.Template} The new template
4552      */
4553     createTemplate : function(o){
4554         var html = createHtml(o);
4555         return new Roo.Template(html);
4556     }
4557     };
4558 }();
4559 /*
4560  * Based on:
4561  * Ext JS Library 1.1.1
4562  * Copyright(c) 2006-2007, Ext JS, LLC.
4563  *
4564  * Originally Released Under LGPL - original licence link has changed is not relivant.
4565  *
4566  * Fork - LGPL
4567  * <script type="text/javascript">
4568  */
4569  
4570 /**
4571 * @class Roo.Template
4572 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4573 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4574 * Usage:
4575 <pre><code>
4576 var t = new Roo.Template({
4577     html :  '&lt;div name="{id}"&gt;' + 
4578         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4579         '&lt;/div&gt;',
4580     myformat: function (value, allValues) {
4581         return 'XX' + value;
4582     }
4583 });
4584 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4585 </code></pre>
4586 * For more information see this blog post with examples:
4587 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4588      - Create Elements using DOM, HTML fragments and Templates</a>. 
4589 * @constructor
4590 * @param {Object} cfg - Configuration object.
4591 */
4592 Roo.Template = function(cfg){
4593     // BC!
4594     if(cfg instanceof Array){
4595         cfg = cfg.join("");
4596     }else if(arguments.length > 1){
4597         cfg = Array.prototype.join.call(arguments, "");
4598     }
4599     
4600     
4601     if (typeof(cfg) == 'object') {
4602         Roo.apply(this,cfg)
4603     } else {
4604         // bc
4605         this.html = cfg;
4606     }
4607     if (this.url) {
4608         this.load();
4609     }
4610     
4611 };
4612 Roo.Template.prototype = {
4613     
4614     /**
4615      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4616      *                    it should be fixed so that template is observable...
4617      */
4618     url : false,
4619     /**
4620      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4621      */
4622     html : '',
4623     /**
4624      * Returns an HTML fragment of this template with the specified values applied.
4625      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4626      * @return {String} The HTML fragment
4627      */
4628     applyTemplate : function(values){
4629         try {
4630            
4631             if(this.compiled){
4632                 return this.compiled(values);
4633             }
4634             var useF = this.disableFormats !== true;
4635             var fm = Roo.util.Format, tpl = this;
4636             var fn = function(m, name, format, args){
4637                 if(format && useF){
4638                     if(format.substr(0, 5) == "this."){
4639                         return tpl.call(format.substr(5), values[name], values);
4640                     }else{
4641                         if(args){
4642                             // quoted values are required for strings in compiled templates, 
4643                             // but for non compiled we need to strip them
4644                             // quoted reversed for jsmin
4645                             var re = /^\s*['"](.*)["']\s*$/;
4646                             args = args.split(',');
4647                             for(var i = 0, len = args.length; i < len; i++){
4648                                 args[i] = args[i].replace(re, "$1");
4649                             }
4650                             args = [values[name]].concat(args);
4651                         }else{
4652                             args = [values[name]];
4653                         }
4654                         return fm[format].apply(fm, args);
4655                     }
4656                 }else{
4657                     return values[name] !== undefined ? values[name] : "";
4658                 }
4659             };
4660             return this.html.replace(this.re, fn);
4661         } catch (e) {
4662             Roo.log(e);
4663             throw e;
4664         }
4665          
4666     },
4667     
4668     loading : false,
4669       
4670     load : function ()
4671     {
4672          
4673         if (this.loading) {
4674             return;
4675         }
4676         var _t = this;
4677         
4678         this.loading = true;
4679         this.compiled = false;
4680         
4681         var cx = new Roo.data.Connection();
4682         cx.request({
4683             url : this.url,
4684             method : 'GET',
4685             success : function (response) {
4686                 _t.loading = false;
4687                 _t.html = response.responseText;
4688                 _t.url = false;
4689                 _t.compile();
4690              },
4691             failure : function(response) {
4692                 Roo.log("Template failed to load from " + _t.url);
4693                 _t.loading = false;
4694             }
4695         });
4696     },
4697
4698     /**
4699      * Sets the HTML used as the template and optionally compiles it.
4700      * @param {String} html
4701      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4702      * @return {Roo.Template} this
4703      */
4704     set : function(html, compile){
4705         this.html = html;
4706         this.compiled = null;
4707         if(compile){
4708             this.compile();
4709         }
4710         return this;
4711     },
4712     
4713     /**
4714      * True to disable format functions (defaults to false)
4715      * @type Boolean
4716      */
4717     disableFormats : false,
4718     
4719     /**
4720     * The regular expression used to match template variables 
4721     * @type RegExp
4722     * @property 
4723     */
4724     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4725     
4726     /**
4727      * Compiles the template into an internal function, eliminating the RegEx overhead.
4728      * @return {Roo.Template} this
4729      */
4730     compile : function(){
4731         var fm = Roo.util.Format;
4732         var useF = this.disableFormats !== true;
4733         var sep = Roo.isGecko ? "+" : ",";
4734         var fn = function(m, name, format, args){
4735             if(format && useF){
4736                 args = args ? ',' + args : "";
4737                 if(format.substr(0, 5) != "this."){
4738                     format = "fm." + format + '(';
4739                 }else{
4740                     format = 'this.call("'+ format.substr(5) + '", ';
4741                     args = ", values";
4742                 }
4743             }else{
4744                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4745             }
4746             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4747         };
4748         var body;
4749         // branched to use + in gecko and [].join() in others
4750         if(Roo.isGecko){
4751             body = "this.compiled = function(values){ return '" +
4752                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4753                     "';};";
4754         }else{
4755             body = ["this.compiled = function(values){ return ['"];
4756             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4757             body.push("'].join('');};");
4758             body = body.join('');
4759         }
4760         /**
4761          * eval:var:values
4762          * eval:var:fm
4763          */
4764         eval(body);
4765         return this;
4766     },
4767     
4768     // private function used to call members
4769     call : function(fnName, value, allValues){
4770         return this[fnName](value, allValues);
4771     },
4772     
4773     /**
4774      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4775      * @param {String/HTMLElement/Roo.Element} el The context element
4776      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4777      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4778      * @return {HTMLElement/Roo.Element} The new node or Element
4779      */
4780     insertFirst: function(el, values, returnElement){
4781         return this.doInsert('afterBegin', el, values, returnElement);
4782     },
4783
4784     /**
4785      * Applies the supplied values to the template and inserts the new node(s) before el.
4786      * @param {String/HTMLElement/Roo.Element} el The context element
4787      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4788      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4789      * @return {HTMLElement/Roo.Element} The new node or Element
4790      */
4791     insertBefore: function(el, values, returnElement){
4792         return this.doInsert('beforeBegin', el, values, returnElement);
4793     },
4794
4795     /**
4796      * Applies the supplied values to the template and inserts the new node(s) after el.
4797      * @param {String/HTMLElement/Roo.Element} el The context element
4798      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4799      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4800      * @return {HTMLElement/Roo.Element} The new node or Element
4801      */
4802     insertAfter : function(el, values, returnElement){
4803         return this.doInsert('afterEnd', el, values, returnElement);
4804     },
4805     
4806     /**
4807      * Applies the supplied values to the template and appends the new node(s) to el.
4808      * @param {String/HTMLElement/Roo.Element} el The context element
4809      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4810      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4811      * @return {HTMLElement/Roo.Element} The new node or Element
4812      */
4813     append : function(el, values, returnElement){
4814         return this.doInsert('beforeEnd', el, values, returnElement);
4815     },
4816
4817     doInsert : function(where, el, values, returnEl){
4818         el = Roo.getDom(el);
4819         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4820         return returnEl ? Roo.get(newNode, true) : newNode;
4821     },
4822
4823     /**
4824      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4825      * @param {String/HTMLElement/Roo.Element} el The context element
4826      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4827      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4828      * @return {HTMLElement/Roo.Element} The new node or Element
4829      */
4830     overwrite : function(el, values, returnElement){
4831         el = Roo.getDom(el);
4832         el.innerHTML = this.applyTemplate(values);
4833         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4834     }
4835 };
4836 /**
4837  * Alias for {@link #applyTemplate}
4838  * @method
4839  */
4840 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4841
4842 // backwards compat
4843 Roo.DomHelper.Template = Roo.Template;
4844
4845 /**
4846  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4847  * @param {String/HTMLElement} el A DOM element or its id
4848  * @returns {Roo.Template} The created template
4849  * @static
4850  */
4851 Roo.Template.from = function(el){
4852     el = Roo.getDom(el);
4853     return new Roo.Template(el.value || el.innerHTML);
4854 };/*
4855  * Based on:
4856  * Ext JS Library 1.1.1
4857  * Copyright(c) 2006-2007, Ext JS, LLC.
4858  *
4859  * Originally Released Under LGPL - original licence link has changed is not relivant.
4860  *
4861  * Fork - LGPL
4862  * <script type="text/javascript">
4863  */
4864  
4865
4866 /*
4867  * This is code is also distributed under MIT license for use
4868  * with jQuery and prototype JavaScript libraries.
4869  */
4870 /**
4871  * @class Roo.DomQuery
4872 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4873 <p>
4874 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4875
4876 <p>
4877 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4878 </p>
4879 <h4>Element Selectors:</h4>
4880 <ul class="list">
4881     <li> <b>*</b> any element</li>
4882     <li> <b>E</b> an element with the tag E</li>
4883     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4884     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4885     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4886     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4887 </ul>
4888 <h4>Attribute Selectors:</h4>
4889 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4890 <ul class="list">
4891     <li> <b>E[foo]</b> has an attribute "foo"</li>
4892     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4893     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4894     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4895     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4896     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4897     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4898 </ul>
4899 <h4>Pseudo Classes:</h4>
4900 <ul class="list">
4901     <li> <b>E:first-child</b> E is the first child of its parent</li>
4902     <li> <b>E:last-child</b> E is the last child of its parent</li>
4903     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4904     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4905     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4906     <li> <b>E:only-child</b> E is the only child of its parent</li>
4907     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4908     <li> <b>E:first</b> the first E in the resultset</li>
4909     <li> <b>E:last</b> the last E in the resultset</li>
4910     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4911     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4912     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4913     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4914     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4915     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4916     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4917     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4918     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4919 </ul>
4920 <h4>CSS Value Selectors:</h4>
4921 <ul class="list">
4922     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4923     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4924     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4925     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4926     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4927     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4928 </ul>
4929  * @singleton
4930  */
4931 Roo.DomQuery = function(){
4932     var cache = {}, simpleCache = {}, valueCache = {};
4933     var nonSpace = /\S/;
4934     var trimRe = /^\s+|\s+$/g;
4935     var tplRe = /\{(\d+)\}/g;
4936     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4937     var tagTokenRe = /^(#)?([\w-\*]+)/;
4938     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4939
4940     function child(p, index){
4941         var i = 0;
4942         var n = p.firstChild;
4943         while(n){
4944             if(n.nodeType == 1){
4945                if(++i == index){
4946                    return n;
4947                }
4948             }
4949             n = n.nextSibling;
4950         }
4951         return null;
4952     };
4953
4954     function next(n){
4955         while((n = n.nextSibling) && n.nodeType != 1);
4956         return n;
4957     };
4958
4959     function prev(n){
4960         while((n = n.previousSibling) && n.nodeType != 1);
4961         return n;
4962     };
4963
4964     function children(d){
4965         var n = d.firstChild, ni = -1;
4966             while(n){
4967                 var nx = n.nextSibling;
4968                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4969                     d.removeChild(n);
4970                 }else{
4971                     n.nodeIndex = ++ni;
4972                 }
4973                 n = nx;
4974             }
4975             return this;
4976         };
4977
4978     function byClassName(c, a, v){
4979         if(!v){
4980             return c;
4981         }
4982         var r = [], ri = -1, cn;
4983         for(var i = 0, ci; ci = c[i]; i++){
4984             if((' '+ci.className+' ').indexOf(v) != -1){
4985                 r[++ri] = ci;
4986             }
4987         }
4988         return r;
4989     };
4990
4991     function attrValue(n, attr){
4992         if(!n.tagName && typeof n.length != "undefined"){
4993             n = n[0];
4994         }
4995         if(!n){
4996             return null;
4997         }
4998         if(attr == "for"){
4999             return n.htmlFor;
5000         }
5001         if(attr == "class" || attr == "className"){
5002             return n.className;
5003         }
5004         return n.getAttribute(attr) || n[attr];
5005
5006     };
5007
5008     function getNodes(ns, mode, tagName){
5009         var result = [], ri = -1, cs;
5010         if(!ns){
5011             return result;
5012         }
5013         tagName = tagName || "*";
5014         if(typeof ns.getElementsByTagName != "undefined"){
5015             ns = [ns];
5016         }
5017         if(!mode){
5018             for(var i = 0, ni; ni = ns[i]; i++){
5019                 cs = ni.getElementsByTagName(tagName);
5020                 for(var j = 0, ci; ci = cs[j]; j++){
5021                     result[++ri] = ci;
5022                 }
5023             }
5024         }else if(mode == "/" || mode == ">"){
5025             var utag = tagName.toUpperCase();
5026             for(var i = 0, ni, cn; ni = ns[i]; i++){
5027                 cn = ni.children || ni.childNodes;
5028                 for(var j = 0, cj; cj = cn[j]; j++){
5029                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5030                         result[++ri] = cj;
5031                     }
5032                 }
5033             }
5034         }else if(mode == "+"){
5035             var utag = tagName.toUpperCase();
5036             for(var i = 0, n; n = ns[i]; i++){
5037                 while((n = n.nextSibling) && n.nodeType != 1);
5038                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5039                     result[++ri] = n;
5040                 }
5041             }
5042         }else if(mode == "~"){
5043             for(var i = 0, n; n = ns[i]; i++){
5044                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5045                 if(n){
5046                     result[++ri] = n;
5047                 }
5048             }
5049         }
5050         return result;
5051     };
5052
5053     function concat(a, b){
5054         if(b.slice){
5055             return a.concat(b);
5056         }
5057         for(var i = 0, l = b.length; i < l; i++){
5058             a[a.length] = b[i];
5059         }
5060         return a;
5061     }
5062
5063     function byTag(cs, tagName){
5064         if(cs.tagName || cs == document){
5065             cs = [cs];
5066         }
5067         if(!tagName){
5068             return cs;
5069         }
5070         var r = [], ri = -1;
5071         tagName = tagName.toLowerCase();
5072         for(var i = 0, ci; ci = cs[i]; i++){
5073             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5074                 r[++ri] = ci;
5075             }
5076         }
5077         return r;
5078     };
5079
5080     function byId(cs, attr, id){
5081         if(cs.tagName || cs == document){
5082             cs = [cs];
5083         }
5084         if(!id){
5085             return cs;
5086         }
5087         var r = [], ri = -1;
5088         for(var i = 0,ci; ci = cs[i]; i++){
5089             if(ci && ci.id == id){
5090                 r[++ri] = ci;
5091                 return r;
5092             }
5093         }
5094         return r;
5095     };
5096
5097     function byAttribute(cs, attr, value, op, custom){
5098         var r = [], ri = -1, st = custom=="{";
5099         var f = Roo.DomQuery.operators[op];
5100         for(var i = 0, ci; ci = cs[i]; i++){
5101             var a;
5102             if(st){
5103                 a = Roo.DomQuery.getStyle(ci, attr);
5104             }
5105             else if(attr == "class" || attr == "className"){
5106                 a = ci.className;
5107             }else if(attr == "for"){
5108                 a = ci.htmlFor;
5109             }else if(attr == "href"){
5110                 a = ci.getAttribute("href", 2);
5111             }else{
5112                 a = ci.getAttribute(attr);
5113             }
5114             if((f && f(a, value)) || (!f && a)){
5115                 r[++ri] = ci;
5116             }
5117         }
5118         return r;
5119     };
5120
5121     function byPseudo(cs, name, value){
5122         return Roo.DomQuery.pseudos[name](cs, value);
5123     };
5124
5125     // This is for IE MSXML which does not support expandos.
5126     // IE runs the same speed using setAttribute, however FF slows way down
5127     // and Safari completely fails so they need to continue to use expandos.
5128     var isIE = window.ActiveXObject ? true : false;
5129
5130     // this eval is stop the compressor from
5131     // renaming the variable to something shorter
5132     
5133     /** eval:var:batch */
5134     var batch = 30803; 
5135
5136     var key = 30803;
5137
5138     function nodupIEXml(cs){
5139         var d = ++key;
5140         cs[0].setAttribute("_nodup", d);
5141         var r = [cs[0]];
5142         for(var i = 1, len = cs.length; i < len; i++){
5143             var c = cs[i];
5144             if(!c.getAttribute("_nodup") != d){
5145                 c.setAttribute("_nodup", d);
5146                 r[r.length] = c;
5147             }
5148         }
5149         for(var i = 0, len = cs.length; i < len; i++){
5150             cs[i].removeAttribute("_nodup");
5151         }
5152         return r;
5153     }
5154
5155     function nodup(cs){
5156         if(!cs){
5157             return [];
5158         }
5159         var len = cs.length, c, i, r = cs, cj, ri = -1;
5160         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5161             return cs;
5162         }
5163         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5164             return nodupIEXml(cs);
5165         }
5166         var d = ++key;
5167         cs[0]._nodup = d;
5168         for(i = 1; c = cs[i]; i++){
5169             if(c._nodup != d){
5170                 c._nodup = d;
5171             }else{
5172                 r = [];
5173                 for(var j = 0; j < i; j++){
5174                     r[++ri] = cs[j];
5175                 }
5176                 for(j = i+1; cj = cs[j]; j++){
5177                     if(cj._nodup != d){
5178                         cj._nodup = d;
5179                         r[++ri] = cj;
5180                     }
5181                 }
5182                 return r;
5183             }
5184         }
5185         return r;
5186     }
5187
5188     function quickDiffIEXml(c1, c2){
5189         var d = ++key;
5190         for(var i = 0, len = c1.length; i < len; i++){
5191             c1[i].setAttribute("_qdiff", d);
5192         }
5193         var r = [];
5194         for(var i = 0, len = c2.length; i < len; i++){
5195             if(c2[i].getAttribute("_qdiff") != d){
5196                 r[r.length] = c2[i];
5197             }
5198         }
5199         for(var i = 0, len = c1.length; i < len; i++){
5200            c1[i].removeAttribute("_qdiff");
5201         }
5202         return r;
5203     }
5204
5205     function quickDiff(c1, c2){
5206         var len1 = c1.length;
5207         if(!len1){
5208             return c2;
5209         }
5210         if(isIE && c1[0].selectSingleNode){
5211             return quickDiffIEXml(c1, c2);
5212         }
5213         var d = ++key;
5214         for(var i = 0; i < len1; i++){
5215             c1[i]._qdiff = d;
5216         }
5217         var r = [];
5218         for(var i = 0, len = c2.length; i < len; i++){
5219             if(c2[i]._qdiff != d){
5220                 r[r.length] = c2[i];
5221             }
5222         }
5223         return r;
5224     }
5225
5226     function quickId(ns, mode, root, id){
5227         if(ns == root){
5228            var d = root.ownerDocument || root;
5229            return d.getElementById(id);
5230         }
5231         ns = getNodes(ns, mode, "*");
5232         return byId(ns, null, id);
5233     }
5234
5235     return {
5236         getStyle : function(el, name){
5237             return Roo.fly(el).getStyle(name);
5238         },
5239         /**
5240          * Compiles a selector/xpath query into a reusable function. The returned function
5241          * takes one parameter "root" (optional), which is the context node from where the query should start.
5242          * @param {String} selector The selector/xpath query
5243          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5244          * @return {Function}
5245          */
5246         compile : function(path, type){
5247             type = type || "select";
5248             
5249             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5250             var q = path, mode, lq;
5251             var tk = Roo.DomQuery.matchers;
5252             var tklen = tk.length;
5253             var mm;
5254
5255             // accept leading mode switch
5256             var lmode = q.match(modeRe);
5257             if(lmode && lmode[1]){
5258                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5259                 q = q.replace(lmode[1], "");
5260             }
5261             // strip leading slashes
5262             while(path.substr(0, 1)=="/"){
5263                 path = path.substr(1);
5264             }
5265
5266             while(q && lq != q){
5267                 lq = q;
5268                 var tm = q.match(tagTokenRe);
5269                 if(type == "select"){
5270                     if(tm){
5271                         if(tm[1] == "#"){
5272                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5273                         }else{
5274                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5275                         }
5276                         q = q.replace(tm[0], "");
5277                     }else if(q.substr(0, 1) != '@'){
5278                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5279                     }
5280                 }else{
5281                     if(tm){
5282                         if(tm[1] == "#"){
5283                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5284                         }else{
5285                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5286                         }
5287                         q = q.replace(tm[0], "");
5288                     }
5289                 }
5290                 while(!(mm = q.match(modeRe))){
5291                     var matched = false;
5292                     for(var j = 0; j < tklen; j++){
5293                         var t = tk[j];
5294                         var m = q.match(t.re);
5295                         if(m){
5296                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5297                                                     return m[i];
5298                                                 });
5299                             q = q.replace(m[0], "");
5300                             matched = true;
5301                             break;
5302                         }
5303                     }
5304                     // prevent infinite loop on bad selector
5305                     if(!matched){
5306                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5307                     }
5308                 }
5309                 if(mm[1]){
5310                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5311                     q = q.replace(mm[1], "");
5312                 }
5313             }
5314             fn[fn.length] = "return nodup(n);\n}";
5315             
5316              /** 
5317               * list of variables that need from compression as they are used by eval.
5318              *  eval:var:batch 
5319              *  eval:var:nodup
5320              *  eval:var:byTag
5321              *  eval:var:ById
5322              *  eval:var:getNodes
5323              *  eval:var:quickId
5324              *  eval:var:mode
5325              *  eval:var:root
5326              *  eval:var:n
5327              *  eval:var:byClassName
5328              *  eval:var:byPseudo
5329              *  eval:var:byAttribute
5330              *  eval:var:attrValue
5331              * 
5332              **/ 
5333             eval(fn.join(""));
5334             return f;
5335         },
5336
5337         /**
5338          * Selects a group of elements.
5339          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5340          * @param {Node} root (optional) The start of the query (defaults to document).
5341          * @return {Array}
5342          */
5343         select : function(path, root, type){
5344             if(!root || root == document){
5345                 root = document;
5346             }
5347             if(typeof root == "string"){
5348                 root = document.getElementById(root);
5349             }
5350             var paths = path.split(",");
5351             var results = [];
5352             for(var i = 0, len = paths.length; i < len; i++){
5353                 var p = paths[i].replace(trimRe, "");
5354                 if(!cache[p]){
5355                     cache[p] = Roo.DomQuery.compile(p);
5356                     if(!cache[p]){
5357                         throw p + " is not a valid selector";
5358                     }
5359                 }
5360                 var result = cache[p](root);
5361                 if(result && result != document){
5362                     results = results.concat(result);
5363                 }
5364             }
5365             if(paths.length > 1){
5366                 return nodup(results);
5367             }
5368             return results;
5369         },
5370
5371         /**
5372          * Selects a single element.
5373          * @param {String} selector The selector/xpath query
5374          * @param {Node} root (optional) The start of the query (defaults to document).
5375          * @return {Element}
5376          */
5377         selectNode : function(path, root){
5378             return Roo.DomQuery.select(path, root)[0];
5379         },
5380
5381         /**
5382          * Selects the value of a node, optionally replacing null with the defaultValue.
5383          * @param {String} selector The selector/xpath query
5384          * @param {Node} root (optional) The start of the query (defaults to document).
5385          * @param {String} defaultValue
5386          */
5387         selectValue : function(path, root, defaultValue){
5388             path = path.replace(trimRe, "");
5389             if(!valueCache[path]){
5390                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5391             }
5392             var n = valueCache[path](root);
5393             n = n[0] ? n[0] : n;
5394             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5395             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5396         },
5397
5398         /**
5399          * Selects the value of a node, parsing integers and floats.
5400          * @param {String} selector The selector/xpath query
5401          * @param {Node} root (optional) The start of the query (defaults to document).
5402          * @param {Number} defaultValue
5403          * @return {Number}
5404          */
5405         selectNumber : function(path, root, defaultValue){
5406             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5407             return parseFloat(v);
5408         },
5409
5410         /**
5411          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5412          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5413          * @param {String} selector The simple selector to test
5414          * @return {Boolean}
5415          */
5416         is : function(el, ss){
5417             if(typeof el == "string"){
5418                 el = document.getElementById(el);
5419             }
5420             var isArray = (el instanceof Array);
5421             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5422             return isArray ? (result.length == el.length) : (result.length > 0);
5423         },
5424
5425         /**
5426          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5427          * @param {Array} el An array of elements to filter
5428          * @param {String} selector The simple selector to test
5429          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5430          * the selector instead of the ones that match
5431          * @return {Array}
5432          */
5433         filter : function(els, ss, nonMatches){
5434             ss = ss.replace(trimRe, "");
5435             if(!simpleCache[ss]){
5436                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5437             }
5438             var result = simpleCache[ss](els);
5439             return nonMatches ? quickDiff(result, els) : result;
5440         },
5441
5442         /**
5443          * Collection of matching regular expressions and code snippets.
5444          */
5445         matchers : [{
5446                 re: /^\.([\w-]+)/,
5447                 select: 'n = byClassName(n, null, " {1} ");'
5448             }, {
5449                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5450                 select: 'n = byPseudo(n, "{1}", "{2}");'
5451             },{
5452                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5453                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5454             }, {
5455                 re: /^#([\w-]+)/,
5456                 select: 'n = byId(n, null, "{1}");'
5457             },{
5458                 re: /^@([\w-]+)/,
5459                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5460             }
5461         ],
5462
5463         /**
5464          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5465          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5466          */
5467         operators : {
5468             "=" : function(a, v){
5469                 return a == v;
5470             },
5471             "!=" : function(a, v){
5472                 return a != v;
5473             },
5474             "^=" : function(a, v){
5475                 return a && a.substr(0, v.length) == v;
5476             },
5477             "$=" : function(a, v){
5478                 return a && a.substr(a.length-v.length) == v;
5479             },
5480             "*=" : function(a, v){
5481                 return a && a.indexOf(v) !== -1;
5482             },
5483             "%=" : function(a, v){
5484                 return (a % v) == 0;
5485             },
5486             "|=" : function(a, v){
5487                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5488             },
5489             "~=" : function(a, v){
5490                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5491             }
5492         },
5493
5494         /**
5495          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5496          * and the argument (if any) supplied in the selector.
5497          */
5498         pseudos : {
5499             "first-child" : function(c){
5500                 var r = [], ri = -1, n;
5501                 for(var i = 0, ci; ci = n = c[i]; i++){
5502                     while((n = n.previousSibling) && n.nodeType != 1);
5503                     if(!n){
5504                         r[++ri] = ci;
5505                     }
5506                 }
5507                 return r;
5508             },
5509
5510             "last-child" : function(c){
5511                 var r = [], ri = -1, n;
5512                 for(var i = 0, ci; ci = n = c[i]; i++){
5513                     while((n = n.nextSibling) && n.nodeType != 1);
5514                     if(!n){
5515                         r[++ri] = ci;
5516                     }
5517                 }
5518                 return r;
5519             },
5520
5521             "nth-child" : function(c, a) {
5522                 var r = [], ri = -1;
5523                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5524                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5525                 for(var i = 0, n; n = c[i]; i++){
5526                     var pn = n.parentNode;
5527                     if (batch != pn._batch) {
5528                         var j = 0;
5529                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5530                             if(cn.nodeType == 1){
5531                                cn.nodeIndex = ++j;
5532                             }
5533                         }
5534                         pn._batch = batch;
5535                     }
5536                     if (f == 1) {
5537                         if (l == 0 || n.nodeIndex == l){
5538                             r[++ri] = n;
5539                         }
5540                     } else if ((n.nodeIndex + l) % f == 0){
5541                         r[++ri] = n;
5542                     }
5543                 }
5544
5545                 return r;
5546             },
5547
5548             "only-child" : function(c){
5549                 var r = [], ri = -1;;
5550                 for(var i = 0, ci; ci = c[i]; i++){
5551                     if(!prev(ci) && !next(ci)){
5552                         r[++ri] = ci;
5553                     }
5554                 }
5555                 return r;
5556             },
5557
5558             "empty" : function(c){
5559                 var r = [], ri = -1;
5560                 for(var i = 0, ci; ci = c[i]; i++){
5561                     var cns = ci.childNodes, j = 0, cn, empty = true;
5562                     while(cn = cns[j]){
5563                         ++j;
5564                         if(cn.nodeType == 1 || cn.nodeType == 3){
5565                             empty = false;
5566                             break;
5567                         }
5568                     }
5569                     if(empty){
5570                         r[++ri] = ci;
5571                     }
5572                 }
5573                 return r;
5574             },
5575
5576             "contains" : function(c, v){
5577                 var r = [], ri = -1;
5578                 for(var i = 0, ci; ci = c[i]; i++){
5579                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5580                         r[++ri] = ci;
5581                     }
5582                 }
5583                 return r;
5584             },
5585
5586             "nodeValue" : function(c, v){
5587                 var r = [], ri = -1;
5588                 for(var i = 0, ci; ci = c[i]; i++){
5589                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5590                         r[++ri] = ci;
5591                     }
5592                 }
5593                 return r;
5594             },
5595
5596             "checked" : function(c){
5597                 var r = [], ri = -1;
5598                 for(var i = 0, ci; ci = c[i]; i++){
5599                     if(ci.checked == true){
5600                         r[++ri] = ci;
5601                     }
5602                 }
5603                 return r;
5604             },
5605
5606             "not" : function(c, ss){
5607                 return Roo.DomQuery.filter(c, ss, true);
5608             },
5609
5610             "odd" : function(c){
5611                 return this["nth-child"](c, "odd");
5612             },
5613
5614             "even" : function(c){
5615                 return this["nth-child"](c, "even");
5616             },
5617
5618             "nth" : function(c, a){
5619                 return c[a-1] || [];
5620             },
5621
5622             "first" : function(c){
5623                 return c[0] || [];
5624             },
5625
5626             "last" : function(c){
5627                 return c[c.length-1] || [];
5628             },
5629
5630             "has" : function(c, ss){
5631                 var s = Roo.DomQuery.select;
5632                 var r = [], ri = -1;
5633                 for(var i = 0, ci; ci = c[i]; i++){
5634                     if(s(ss, ci).length > 0){
5635                         r[++ri] = ci;
5636                     }
5637                 }
5638                 return r;
5639             },
5640
5641             "next" : function(c, ss){
5642                 var is = Roo.DomQuery.is;
5643                 var r = [], ri = -1;
5644                 for(var i = 0, ci; ci = c[i]; i++){
5645                     var n = next(ci);
5646                     if(n && is(n, ss)){
5647                         r[++ri] = ci;
5648                     }
5649                 }
5650                 return r;
5651             },
5652
5653             "prev" : function(c, ss){
5654                 var is = Roo.DomQuery.is;
5655                 var r = [], ri = -1;
5656                 for(var i = 0, ci; ci = c[i]; i++){
5657                     var n = prev(ci);
5658                     if(n && is(n, ss)){
5659                         r[++ri] = ci;
5660                     }
5661                 }
5662                 return r;
5663             }
5664         }
5665     };
5666 }();
5667
5668 /**
5669  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5670  * @param {String} path The selector/xpath query
5671  * @param {Node} root (optional) The start of the query (defaults to document).
5672  * @return {Array}
5673  * @member Roo
5674  * @method query
5675  */
5676 Roo.query = Roo.DomQuery.select;
5677 /*
5678  * Based on:
5679  * Ext JS Library 1.1.1
5680  * Copyright(c) 2006-2007, Ext JS, LLC.
5681  *
5682  * Originally Released Under LGPL - original licence link has changed is not relivant.
5683  *
5684  * Fork - LGPL
5685  * <script type="text/javascript">
5686  */
5687
5688 /**
5689  * @class Roo.util.Observable
5690  * Base class that provides a common interface for publishing events. Subclasses are expected to
5691  * to have a property "events" with all the events defined.<br>
5692  * For example:
5693  * <pre><code>
5694  Employee = function(name){
5695     this.name = name;
5696     this.addEvents({
5697         "fired" : true,
5698         "quit" : true
5699     });
5700  }
5701  Roo.extend(Employee, Roo.util.Observable);
5702 </code></pre>
5703  * @param {Object} config properties to use (incuding events / listeners)
5704  */
5705
5706 Roo.util.Observable = function(cfg){
5707     
5708     cfg = cfg|| {};
5709     this.addEvents(cfg.events || {});
5710     if (cfg.events) {
5711         delete cfg.events; // make sure
5712     }
5713      
5714     Roo.apply(this, cfg);
5715     
5716     if(this.listeners){
5717         this.on(this.listeners);
5718         delete this.listeners;
5719     }
5720 };
5721 Roo.util.Observable.prototype = {
5722     /** 
5723  * @cfg {Object} listeners  list of events and functions to call for this object, 
5724  * For example :
5725  * <pre><code>
5726     listeners :  { 
5727        'click' : function(e) {
5728            ..... 
5729         } ,
5730         .... 
5731     } 
5732   </code></pre>
5733  */
5734     
5735     
5736     /**
5737      * Fires the specified event with the passed parameters (minus the event name).
5738      * @param {String} eventName
5739      * @param {Object...} args Variable number of parameters are passed to handlers
5740      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5741      */
5742     fireEvent : function(){
5743         var ce = this.events[arguments[0].toLowerCase()];
5744         if(typeof ce == "object"){
5745             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5746         }else{
5747             return true;
5748         }
5749     },
5750
5751     // private
5752     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5753
5754     /**
5755      * Appends an event handler to this component
5756      * @param {String}   eventName The type of event to listen for
5757      * @param {Function} handler The method the event invokes
5758      * @param {Object}   scope (optional) The scope in which to execute the handler
5759      * function. The handler function's "this" context.
5760      * @param {Object}   options (optional) An object containing handler configuration
5761      * properties. This may contain any of the following properties:<ul>
5762      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5763      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5764      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5765      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5766      * by the specified number of milliseconds. If the event fires again within that time, the original
5767      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5768      * </ul><br>
5769      * <p>
5770      * <b>Combining Options</b><br>
5771      * Using the options argument, it is possible to combine different types of listeners:<br>
5772      * <br>
5773      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5774                 <pre><code>
5775                 el.on('click', this.onClick, this, {
5776                         single: true,
5777                 delay: 100,
5778                 forumId: 4
5779                 });
5780                 </code></pre>
5781      * <p>
5782      * <b>Attaching multiple handlers in 1 call</b><br>
5783      * The method also allows for a single argument to be passed which is a config object containing properties
5784      * which specify multiple handlers.
5785      * <pre><code>
5786                 el.on({
5787                         'click': {
5788                         fn: this.onClick,
5789                         scope: this,
5790                         delay: 100
5791                 }, 
5792                 'mouseover': {
5793                         fn: this.onMouseOver,
5794                         scope: this
5795                 },
5796                 'mouseout': {
5797                         fn: this.onMouseOut,
5798                         scope: this
5799                 }
5800                 });
5801                 </code></pre>
5802      * <p>
5803      * Or a shorthand syntax which passes the same scope object to all handlers:
5804         <pre><code>
5805                 el.on({
5806                         'click': this.onClick,
5807                 'mouseover': this.onMouseOver,
5808                 'mouseout': this.onMouseOut,
5809                 scope: this
5810                 });
5811                 </code></pre>
5812      */
5813     addListener : function(eventName, fn, scope, o){
5814         if(typeof eventName == "object"){
5815             o = eventName;
5816             for(var e in o){
5817                 if(this.filterOptRe.test(e)){
5818                     continue;
5819                 }
5820                 if(typeof o[e] == "function"){
5821                     // shared options
5822                     this.addListener(e, o[e], o.scope,  o);
5823                 }else{
5824                     // individual options
5825                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5826                 }
5827             }
5828             return;
5829         }
5830         o = (!o || typeof o == "boolean") ? {} : o;
5831         eventName = eventName.toLowerCase();
5832         var ce = this.events[eventName] || true;
5833         if(typeof ce == "boolean"){
5834             ce = new Roo.util.Event(this, eventName);
5835             this.events[eventName] = ce;
5836         }
5837         ce.addListener(fn, scope, o);
5838     },
5839
5840     /**
5841      * Removes a listener
5842      * @param {String}   eventName     The type of event to listen for
5843      * @param {Function} handler        The handler to remove
5844      * @param {Object}   scope  (optional) The scope (this object) for the handler
5845      */
5846     removeListener : function(eventName, fn, scope){
5847         var ce = this.events[eventName.toLowerCase()];
5848         if(typeof ce == "object"){
5849             ce.removeListener(fn, scope);
5850         }
5851     },
5852
5853     /**
5854      * Removes all listeners for this object
5855      */
5856     purgeListeners : function(){
5857         for(var evt in this.events){
5858             if(typeof this.events[evt] == "object"){
5859                  this.events[evt].clearListeners();
5860             }
5861         }
5862     },
5863
5864     relayEvents : function(o, events){
5865         var createHandler = function(ename){
5866             return function(){
5867                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5868             };
5869         };
5870         for(var i = 0, len = events.length; i < len; i++){
5871             var ename = events[i];
5872             if(!this.events[ename]){ this.events[ename] = true; };
5873             o.on(ename, createHandler(ename), this);
5874         }
5875     },
5876
5877     /**
5878      * Used to define events on this Observable
5879      * @param {Object} object The object with the events defined
5880      */
5881     addEvents : function(o){
5882         if(!this.events){
5883             this.events = {};
5884         }
5885         Roo.applyIf(this.events, o);
5886     },
5887
5888     /**
5889      * Checks to see if this object has any listeners for a specified event
5890      * @param {String} eventName The name of the event to check for
5891      * @return {Boolean} True if the event is being listened for, else false
5892      */
5893     hasListener : function(eventName){
5894         var e = this.events[eventName];
5895         return typeof e == "object" && e.listeners.length > 0;
5896     }
5897 };
5898 /**
5899  * Appends an event handler to this element (shorthand for addListener)
5900  * @param {String}   eventName     The type of event to listen for
5901  * @param {Function} handler        The method the event invokes
5902  * @param {Object}   scope (optional) The scope in which to execute the handler
5903  * function. The handler function's "this" context.
5904  * @param {Object}   options  (optional)
5905  * @method
5906  */
5907 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5908 /**
5909  * Removes a listener (shorthand for removeListener)
5910  * @param {String}   eventName     The type of event to listen for
5911  * @param {Function} handler        The handler to remove
5912  * @param {Object}   scope  (optional) The scope (this object) for the handler
5913  * @method
5914  */
5915 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5916
5917 /**
5918  * Starts capture on the specified Observable. All events will be passed
5919  * to the supplied function with the event name + standard signature of the event
5920  * <b>before</b> the event is fired. If the supplied function returns false,
5921  * the event will not fire.
5922  * @param {Observable} o The Observable to capture
5923  * @param {Function} fn The function to call
5924  * @param {Object} scope (optional) The scope (this object) for the fn
5925  * @static
5926  */
5927 Roo.util.Observable.capture = function(o, fn, scope){
5928     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5929 };
5930
5931 /**
5932  * Removes <b>all</b> added captures from the Observable.
5933  * @param {Observable} o The Observable to release
5934  * @static
5935  */
5936 Roo.util.Observable.releaseCapture = function(o){
5937     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5938 };
5939
5940 (function(){
5941
5942     var createBuffered = function(h, o, scope){
5943         var task = new Roo.util.DelayedTask();
5944         return function(){
5945             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5946         };
5947     };
5948
5949     var createSingle = function(h, e, fn, scope){
5950         return function(){
5951             e.removeListener(fn, scope);
5952             return h.apply(scope, arguments);
5953         };
5954     };
5955
5956     var createDelayed = function(h, o, scope){
5957         return function(){
5958             var args = Array.prototype.slice.call(arguments, 0);
5959             setTimeout(function(){
5960                 h.apply(scope, args);
5961             }, o.delay || 10);
5962         };
5963     };
5964
5965     Roo.util.Event = function(obj, name){
5966         this.name = name;
5967         this.obj = obj;
5968         this.listeners = [];
5969     };
5970
5971     Roo.util.Event.prototype = {
5972         addListener : function(fn, scope, options){
5973             var o = options || {};
5974             scope = scope || this.obj;
5975             if(!this.isListening(fn, scope)){
5976                 var l = {fn: fn, scope: scope, options: o};
5977                 var h = fn;
5978                 if(o.delay){
5979                     h = createDelayed(h, o, scope);
5980                 }
5981                 if(o.single){
5982                     h = createSingle(h, this, fn, scope);
5983                 }
5984                 if(o.buffer){
5985                     h = createBuffered(h, o, scope);
5986                 }
5987                 l.fireFn = h;
5988                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5989                     this.listeners.push(l);
5990                 }else{
5991                     this.listeners = this.listeners.slice(0);
5992                     this.listeners.push(l);
5993                 }
5994             }
5995         },
5996
5997         findListener : function(fn, scope){
5998             scope = scope || this.obj;
5999             var ls = this.listeners;
6000             for(var i = 0, len = ls.length; i < len; i++){
6001                 var l = ls[i];
6002                 if(l.fn == fn && l.scope == scope){
6003                     return i;
6004                 }
6005             }
6006             return -1;
6007         },
6008
6009         isListening : function(fn, scope){
6010             return this.findListener(fn, scope) != -1;
6011         },
6012
6013         removeListener : function(fn, scope){
6014             var index;
6015             if((index = this.findListener(fn, scope)) != -1){
6016                 if(!this.firing){
6017                     this.listeners.splice(index, 1);
6018                 }else{
6019                     this.listeners = this.listeners.slice(0);
6020                     this.listeners.splice(index, 1);
6021                 }
6022                 return true;
6023             }
6024             return false;
6025         },
6026
6027         clearListeners : function(){
6028             this.listeners = [];
6029         },
6030
6031         fire : function(){
6032             var ls = this.listeners, scope, len = ls.length;
6033             if(len > 0){
6034                 this.firing = true;
6035                 var args = Array.prototype.slice.call(arguments, 0);
6036                 for(var i = 0; i < len; i++){
6037                     var l = ls[i];
6038                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6039                         this.firing = false;
6040                         return false;
6041                     }
6042                 }
6043                 this.firing = false;
6044             }
6045             return true;
6046         }
6047     };
6048 })();/*
6049  * Based on:
6050  * Ext JS Library 1.1.1
6051  * Copyright(c) 2006-2007, Ext JS, LLC.
6052  *
6053  * Originally Released Under LGPL - original licence link has changed is not relivant.
6054  *
6055  * Fork - LGPL
6056  * <script type="text/javascript">
6057  */
6058
6059 /**
6060  * @class Roo.EventManager
6061  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6062  * several useful events directly.
6063  * See {@link Roo.EventObject} for more details on normalized event objects.
6064  * @singleton
6065  */
6066 Roo.EventManager = function(){
6067     var docReadyEvent, docReadyProcId, docReadyState = false;
6068     var resizeEvent, resizeTask, textEvent, textSize;
6069     var E = Roo.lib.Event;
6070     var D = Roo.lib.Dom;
6071
6072     
6073     
6074
6075     var fireDocReady = function(){
6076         if(!docReadyState){
6077             docReadyState = true;
6078             Roo.isReady = true;
6079             if(docReadyProcId){
6080                 clearInterval(docReadyProcId);
6081             }
6082             if(Roo.isGecko || Roo.isOpera) {
6083                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6084             }
6085             if(Roo.isIE){
6086                 var defer = document.getElementById("ie-deferred-loader");
6087                 if(defer){
6088                     defer.onreadystatechange = null;
6089                     defer.parentNode.removeChild(defer);
6090                 }
6091             }
6092             if(docReadyEvent){
6093                 docReadyEvent.fire();
6094                 docReadyEvent.clearListeners();
6095             }
6096         }
6097     };
6098     
6099     var initDocReady = function(){
6100         docReadyEvent = new Roo.util.Event();
6101         if(Roo.isGecko || Roo.isOpera) {
6102             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6103         }else if(Roo.isIE){
6104             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6105             var defer = document.getElementById("ie-deferred-loader");
6106             defer.onreadystatechange = function(){
6107                 if(this.readyState == "complete"){
6108                     fireDocReady();
6109                 }
6110             };
6111         }else if(Roo.isSafari){ 
6112             docReadyProcId = setInterval(function(){
6113                 var rs = document.readyState;
6114                 if(rs == "complete") {
6115                     fireDocReady();     
6116                  }
6117             }, 10);
6118         }
6119         // no matter what, make sure it fires on load
6120         E.on(window, "load", fireDocReady);
6121     };
6122
6123     var createBuffered = function(h, o){
6124         var task = new Roo.util.DelayedTask(h);
6125         return function(e){
6126             // create new event object impl so new events don't wipe out properties
6127             e = new Roo.EventObjectImpl(e);
6128             task.delay(o.buffer, h, null, [e]);
6129         };
6130     };
6131
6132     var createSingle = function(h, el, ename, fn){
6133         return function(e){
6134             Roo.EventManager.removeListener(el, ename, fn);
6135             h(e);
6136         };
6137     };
6138
6139     var createDelayed = function(h, o){
6140         return function(e){
6141             // create new event object impl so new events don't wipe out properties
6142             e = new Roo.EventObjectImpl(e);
6143             setTimeout(function(){
6144                 h(e);
6145             }, o.delay || 10);
6146         };
6147     };
6148     var transitionEndVal = false;
6149     
6150     var transitionEnd = function()
6151     {
6152         if (transitionEndVal) {
6153             return transitionEndVal;
6154         }
6155         var el = document.createElement('div');
6156
6157         var transEndEventNames = {
6158             WebkitTransition : 'webkitTransitionEnd',
6159             MozTransition    : 'transitionend',
6160             OTransition      : 'oTransitionEnd otransitionend',
6161             transition       : 'transitionend'
6162         };
6163     
6164         for (var name in transEndEventNames) {
6165             if (el.style[name] !== undefined) {
6166                 transitionEndVal = transEndEventNames[name];
6167                 return  transitionEndVal ;
6168             }
6169         }
6170     }
6171     
6172
6173     var listen = function(element, ename, opt, fn, scope){
6174         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6175         fn = fn || o.fn; scope = scope || o.scope;
6176         var el = Roo.getDom(element);
6177         
6178         
6179         if(!el){
6180             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6181         }
6182         
6183         if (ename == 'transitionend') {
6184             ename = transitionEnd();
6185         }
6186         var h = function(e){
6187             e = Roo.EventObject.setEvent(e);
6188             var t;
6189             if(o.delegate){
6190                 t = e.getTarget(o.delegate, el);
6191                 if(!t){
6192                     return;
6193                 }
6194             }else{
6195                 t = e.target;
6196             }
6197             if(o.stopEvent === true){
6198                 e.stopEvent();
6199             }
6200             if(o.preventDefault === true){
6201                e.preventDefault();
6202             }
6203             if(o.stopPropagation === true){
6204                 e.stopPropagation();
6205             }
6206
6207             if(o.normalized === false){
6208                 e = e.browserEvent;
6209             }
6210
6211             fn.call(scope || el, e, t, o);
6212         };
6213         if(o.delay){
6214             h = createDelayed(h, o);
6215         }
6216         if(o.single){
6217             h = createSingle(h, el, ename, fn);
6218         }
6219         if(o.buffer){
6220             h = createBuffered(h, o);
6221         }
6222         fn._handlers = fn._handlers || [];
6223         
6224         
6225         fn._handlers.push([Roo.id(el), ename, h]);
6226         
6227         
6228          
6229         E.on(el, ename, h);
6230         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6231             el.addEventListener("DOMMouseScroll", h, false);
6232             E.on(window, 'unload', function(){
6233                 el.removeEventListener("DOMMouseScroll", h, false);
6234             });
6235         }
6236         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6237             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6238         }
6239         return h;
6240     };
6241
6242     var stopListening = function(el, ename, fn){
6243         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6244         if(hds){
6245             for(var i = 0, len = hds.length; i < len; i++){
6246                 var h = hds[i];
6247                 if(h[0] == id && h[1] == ename){
6248                     hd = h[2];
6249                     hds.splice(i, 1);
6250                     break;
6251                 }
6252             }
6253         }
6254         E.un(el, ename, hd);
6255         el = Roo.getDom(el);
6256         if(ename == "mousewheel" && el.addEventListener){
6257             el.removeEventListener("DOMMouseScroll", hd, false);
6258         }
6259         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6260             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6261         }
6262     };
6263
6264     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6265     
6266     var pub = {
6267         
6268         
6269         /** 
6270          * Fix for doc tools
6271          * @scope Roo.EventManager
6272          */
6273         
6274         
6275         /** 
6276          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6277          * object with a Roo.EventObject
6278          * @param {Function} fn        The method the event invokes
6279          * @param {Object}   scope    An object that becomes the scope of the handler
6280          * @param {boolean}  override If true, the obj passed in becomes
6281          *                             the execution scope of the listener
6282          * @return {Function} The wrapped function
6283          * @deprecated
6284          */
6285         wrap : function(fn, scope, override){
6286             return function(e){
6287                 Roo.EventObject.setEvent(e);
6288                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6289             };
6290         },
6291         
6292         /**
6293      * Appends an event handler to an element (shorthand for addListener)
6294      * @param {String/HTMLElement}   element        The html element or id to assign the
6295      * @param {String}   eventName The type of event to listen for
6296      * @param {Function} handler The method the event invokes
6297      * @param {Object}   scope (optional) The scope in which to execute the handler
6298      * function. The handler function's "this" context.
6299      * @param {Object}   options (optional) An object containing handler configuration
6300      * properties. This may contain any of the following properties:<ul>
6301      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6302      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6303      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6304      * <li>preventDefault {Boolean} True to prevent the default action</li>
6305      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6306      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6307      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6308      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6309      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6310      * by the specified number of milliseconds. If the event fires again within that time, the original
6311      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6312      * </ul><br>
6313      * <p>
6314      * <b>Combining Options</b><br>
6315      * Using the options argument, it is possible to combine different types of listeners:<br>
6316      * <br>
6317      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6318      * Code:<pre><code>
6319 el.on('click', this.onClick, this, {
6320     single: true,
6321     delay: 100,
6322     stopEvent : true,
6323     forumId: 4
6324 });</code></pre>
6325      * <p>
6326      * <b>Attaching multiple handlers in 1 call</b><br>
6327       * The method also allows for a single argument to be passed which is a config object containing properties
6328      * which specify multiple handlers.
6329      * <p>
6330      * Code:<pre><code>
6331 el.on({
6332     'click' : {
6333         fn: this.onClick
6334         scope: this,
6335         delay: 100
6336     },
6337     'mouseover' : {
6338         fn: this.onMouseOver
6339         scope: this
6340     },
6341     'mouseout' : {
6342         fn: this.onMouseOut
6343         scope: this
6344     }
6345 });</code></pre>
6346      * <p>
6347      * Or a shorthand syntax:<br>
6348      * Code:<pre><code>
6349 el.on({
6350     'click' : this.onClick,
6351     'mouseover' : this.onMouseOver,
6352     'mouseout' : this.onMouseOut
6353     scope: this
6354 });</code></pre>
6355      */
6356         addListener : function(element, eventName, fn, scope, options){
6357             if(typeof eventName == "object"){
6358                 var o = eventName;
6359                 for(var e in o){
6360                     if(propRe.test(e)){
6361                         continue;
6362                     }
6363                     if(typeof o[e] == "function"){
6364                         // shared options
6365                         listen(element, e, o, o[e], o.scope);
6366                     }else{
6367                         // individual options
6368                         listen(element, e, o[e]);
6369                     }
6370                 }
6371                 return;
6372             }
6373             return listen(element, eventName, options, fn, scope);
6374         },
6375         
6376         /**
6377          * Removes an event handler
6378          *
6379          * @param {String/HTMLElement}   element        The id or html element to remove the 
6380          *                             event from
6381          * @param {String}   eventName     The type of event
6382          * @param {Function} fn
6383          * @return {Boolean} True if a listener was actually removed
6384          */
6385         removeListener : function(element, eventName, fn){
6386             return stopListening(element, eventName, fn);
6387         },
6388         
6389         /**
6390          * Fires when the document is ready (before onload and before images are loaded). Can be 
6391          * accessed shorthanded Roo.onReady().
6392          * @param {Function} fn        The method the event invokes
6393          * @param {Object}   scope    An  object that becomes the scope of the handler
6394          * @param {boolean}  options
6395          */
6396         onDocumentReady : function(fn, scope, options){
6397             if(docReadyState){ // if it already fired
6398                 docReadyEvent.addListener(fn, scope, options);
6399                 docReadyEvent.fire();
6400                 docReadyEvent.clearListeners();
6401                 return;
6402             }
6403             if(!docReadyEvent){
6404                 initDocReady();
6405             }
6406             docReadyEvent.addListener(fn, scope, options);
6407         },
6408         
6409         /**
6410          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6411          * @param {Function} fn        The method the event invokes
6412          * @param {Object}   scope    An object that becomes the scope of the handler
6413          * @param {boolean}  options
6414          */
6415         onWindowResize : function(fn, scope, options){
6416             if(!resizeEvent){
6417                 resizeEvent = new Roo.util.Event();
6418                 resizeTask = new Roo.util.DelayedTask(function(){
6419                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6420                 });
6421                 E.on(window, "resize", function(){
6422                     if(Roo.isIE){
6423                         resizeTask.delay(50);
6424                     }else{
6425                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6426                     }
6427                 });
6428             }
6429             resizeEvent.addListener(fn, scope, options);
6430         },
6431
6432         /**
6433          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6434          * @param {Function} fn        The method the event invokes
6435          * @param {Object}   scope    An object that becomes the scope of the handler
6436          * @param {boolean}  options
6437          */
6438         onTextResize : function(fn, scope, options){
6439             if(!textEvent){
6440                 textEvent = new Roo.util.Event();
6441                 var textEl = new Roo.Element(document.createElement('div'));
6442                 textEl.dom.className = 'x-text-resize';
6443                 textEl.dom.innerHTML = 'X';
6444                 textEl.appendTo(document.body);
6445                 textSize = textEl.dom.offsetHeight;
6446                 setInterval(function(){
6447                     if(textEl.dom.offsetHeight != textSize){
6448                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6449                     }
6450                 }, this.textResizeInterval);
6451             }
6452             textEvent.addListener(fn, scope, options);
6453         },
6454
6455         /**
6456          * Removes the passed window resize listener.
6457          * @param {Function} fn        The method the event invokes
6458          * @param {Object}   scope    The scope of handler
6459          */
6460         removeResizeListener : function(fn, scope){
6461             if(resizeEvent){
6462                 resizeEvent.removeListener(fn, scope);
6463             }
6464         },
6465
6466         // private
6467         fireResize : function(){
6468             if(resizeEvent){
6469                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6470             }   
6471         },
6472         /**
6473          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6474          */
6475         ieDeferSrc : false,
6476         /**
6477          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6478          */
6479         textResizeInterval : 50
6480     };
6481     
6482     /**
6483      * Fix for doc tools
6484      * @scopeAlias pub=Roo.EventManager
6485      */
6486     
6487      /**
6488      * Appends an event handler to an element (shorthand for addListener)
6489      * @param {String/HTMLElement}   element        The html element or id to assign the
6490      * @param {String}   eventName The type of event to listen for
6491      * @param {Function} handler The method the event invokes
6492      * @param {Object}   scope (optional) The scope in which to execute the handler
6493      * function. The handler function's "this" context.
6494      * @param {Object}   options (optional) An object containing handler configuration
6495      * properties. This may contain any of the following properties:<ul>
6496      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6497      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6498      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6499      * <li>preventDefault {Boolean} True to prevent the default action</li>
6500      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6501      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6502      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6503      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6504      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6505      * by the specified number of milliseconds. If the event fires again within that time, the original
6506      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6507      * </ul><br>
6508      * <p>
6509      * <b>Combining Options</b><br>
6510      * Using the options argument, it is possible to combine different types of listeners:<br>
6511      * <br>
6512      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6513      * Code:<pre><code>
6514 el.on('click', this.onClick, this, {
6515     single: true,
6516     delay: 100,
6517     stopEvent : true,
6518     forumId: 4
6519 });</code></pre>
6520      * <p>
6521      * <b>Attaching multiple handlers in 1 call</b><br>
6522       * The method also allows for a single argument to be passed which is a config object containing properties
6523      * which specify multiple handlers.
6524      * <p>
6525      * Code:<pre><code>
6526 el.on({
6527     'click' : {
6528         fn: this.onClick
6529         scope: this,
6530         delay: 100
6531     },
6532     'mouseover' : {
6533         fn: this.onMouseOver
6534         scope: this
6535     },
6536     'mouseout' : {
6537         fn: this.onMouseOut
6538         scope: this
6539     }
6540 });</code></pre>
6541      * <p>
6542      * Or a shorthand syntax:<br>
6543      * Code:<pre><code>
6544 el.on({
6545     'click' : this.onClick,
6546     'mouseover' : this.onMouseOver,
6547     'mouseout' : this.onMouseOut
6548     scope: this
6549 });</code></pre>
6550      */
6551     pub.on = pub.addListener;
6552     pub.un = pub.removeListener;
6553
6554     pub.stoppedMouseDownEvent = new Roo.util.Event();
6555     return pub;
6556 }();
6557 /**
6558   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6559   * @param {Function} fn        The method the event invokes
6560   * @param {Object}   scope    An  object that becomes the scope of the handler
6561   * @param {boolean}  override If true, the obj passed in becomes
6562   *                             the execution scope of the listener
6563   * @member Roo
6564   * @method onReady
6565  */
6566 Roo.onReady = Roo.EventManager.onDocumentReady;
6567
6568 Roo.onReady(function(){
6569     var bd = Roo.get(document.body);
6570     if(!bd){ return; }
6571
6572     var cls = [
6573             Roo.isIE ? "roo-ie"
6574             : Roo.isGecko ? "roo-gecko"
6575             : Roo.isOpera ? "roo-opera"
6576             : Roo.isSafari ? "roo-safari" : ""];
6577
6578     if(Roo.isMac){
6579         cls.push("roo-mac");
6580     }
6581     if(Roo.isLinux){
6582         cls.push("roo-linux");
6583     }
6584     if(Roo.isIOS){
6585         cls.push("roo-ios");
6586     }
6587     if(Roo.isBorderBox){
6588         cls.push('roo-border-box');
6589     }
6590     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6591         var p = bd.dom.parentNode;
6592         if(p){
6593             p.className += ' roo-strict';
6594         }
6595     }
6596     bd.addClass(cls.join(' '));
6597 });
6598
6599 /**
6600  * @class Roo.EventObject
6601  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6602  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6603  * Example:
6604  * <pre><code>
6605  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6606     e.preventDefault();
6607     var target = e.getTarget();
6608     ...
6609  }
6610  var myDiv = Roo.get("myDiv");
6611  myDiv.on("click", handleClick);
6612  //or
6613  Roo.EventManager.on("myDiv", 'click', handleClick);
6614  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6615  </code></pre>
6616  * @singleton
6617  */
6618 Roo.EventObject = function(){
6619     
6620     var E = Roo.lib.Event;
6621     
6622     // safari keypress events for special keys return bad keycodes
6623     var safariKeys = {
6624         63234 : 37, // left
6625         63235 : 39, // right
6626         63232 : 38, // up
6627         63233 : 40, // down
6628         63276 : 33, // page up
6629         63277 : 34, // page down
6630         63272 : 46, // delete
6631         63273 : 36, // home
6632         63275 : 35  // end
6633     };
6634
6635     // normalize button clicks
6636     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6637                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6638
6639     Roo.EventObjectImpl = function(e){
6640         if(e){
6641             this.setEvent(e.browserEvent || e);
6642         }
6643     };
6644     Roo.EventObjectImpl.prototype = {
6645         /**
6646          * Used to fix doc tools.
6647          * @scope Roo.EventObject.prototype
6648          */
6649             
6650
6651         
6652         
6653         /** The normal browser event */
6654         browserEvent : null,
6655         /** The button pressed in a mouse event */
6656         button : -1,
6657         /** True if the shift key was down during the event */
6658         shiftKey : false,
6659         /** True if the control key was down during the event */
6660         ctrlKey : false,
6661         /** True if the alt key was down during the event */
6662         altKey : false,
6663
6664         /** Key constant 
6665         * @type Number */
6666         BACKSPACE : 8,
6667         /** Key constant 
6668         * @type Number */
6669         TAB : 9,
6670         /** Key constant 
6671         * @type Number */
6672         RETURN : 13,
6673         /** Key constant 
6674         * @type Number */
6675         ENTER : 13,
6676         /** Key constant 
6677         * @type Number */
6678         SHIFT : 16,
6679         /** Key constant 
6680         * @type Number */
6681         CONTROL : 17,
6682         /** Key constant 
6683         * @type Number */
6684         ESC : 27,
6685         /** Key constant 
6686         * @type Number */
6687         SPACE : 32,
6688         /** Key constant 
6689         * @type Number */
6690         PAGEUP : 33,
6691         /** Key constant 
6692         * @type Number */
6693         PAGEDOWN : 34,
6694         /** Key constant 
6695         * @type Number */
6696         END : 35,
6697         /** Key constant 
6698         * @type Number */
6699         HOME : 36,
6700         /** Key constant 
6701         * @type Number */
6702         LEFT : 37,
6703         /** Key constant 
6704         * @type Number */
6705         UP : 38,
6706         /** Key constant 
6707         * @type Number */
6708         RIGHT : 39,
6709         /** Key constant 
6710         * @type Number */
6711         DOWN : 40,
6712         /** Key constant 
6713         * @type Number */
6714         DELETE : 46,
6715         /** Key constant 
6716         * @type Number */
6717         F5 : 116,
6718
6719            /** @private */
6720         setEvent : function(e){
6721             if(e == this || (e && e.browserEvent)){ // already wrapped
6722                 return e;
6723             }
6724             this.browserEvent = e;
6725             if(e){
6726                 // normalize buttons
6727                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6728                 if(e.type == 'click' && this.button == -1){
6729                     this.button = 0;
6730                 }
6731                 this.type = e.type;
6732                 this.shiftKey = e.shiftKey;
6733                 // mac metaKey behaves like ctrlKey
6734                 this.ctrlKey = e.ctrlKey || e.metaKey;
6735                 this.altKey = e.altKey;
6736                 // in getKey these will be normalized for the mac
6737                 this.keyCode = e.keyCode;
6738                 // keyup warnings on firefox.
6739                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6740                 // cache the target for the delayed and or buffered events
6741                 this.target = E.getTarget(e);
6742                 // same for XY
6743                 this.xy = E.getXY(e);
6744             }else{
6745                 this.button = -1;
6746                 this.shiftKey = false;
6747                 this.ctrlKey = false;
6748                 this.altKey = false;
6749                 this.keyCode = 0;
6750                 this.charCode =0;
6751                 this.target = null;
6752                 this.xy = [0, 0];
6753             }
6754             return this;
6755         },
6756
6757         /**
6758          * Stop the event (preventDefault and stopPropagation)
6759          */
6760         stopEvent : function(){
6761             if(this.browserEvent){
6762                 if(this.browserEvent.type == 'mousedown'){
6763                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6764                 }
6765                 E.stopEvent(this.browserEvent);
6766             }
6767         },
6768
6769         /**
6770          * Prevents the browsers default handling of the event.
6771          */
6772         preventDefault : function(){
6773             if(this.browserEvent){
6774                 E.preventDefault(this.browserEvent);
6775             }
6776         },
6777
6778         /** @private */
6779         isNavKeyPress : function(){
6780             var k = this.keyCode;
6781             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6782             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6783         },
6784
6785         isSpecialKey : function(){
6786             var k = this.keyCode;
6787             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6788             (k == 16) || (k == 17) ||
6789             (k >= 18 && k <= 20) ||
6790             (k >= 33 && k <= 35) ||
6791             (k >= 36 && k <= 39) ||
6792             (k >= 44 && k <= 45);
6793         },
6794         /**
6795          * Cancels bubbling of the event.
6796          */
6797         stopPropagation : function(){
6798             if(this.browserEvent){
6799                 if(this.type == 'mousedown'){
6800                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6801                 }
6802                 E.stopPropagation(this.browserEvent);
6803             }
6804         },
6805
6806         /**
6807          * Gets the key code for the event.
6808          * @return {Number}
6809          */
6810         getCharCode : function(){
6811             return this.charCode || this.keyCode;
6812         },
6813
6814         /**
6815          * Returns a normalized keyCode for the event.
6816          * @return {Number} The key code
6817          */
6818         getKey : function(){
6819             var k = this.keyCode || this.charCode;
6820             return Roo.isSafari ? (safariKeys[k] || k) : k;
6821         },
6822
6823         /**
6824          * Gets the x coordinate of the event.
6825          * @return {Number}
6826          */
6827         getPageX : function(){
6828             return this.xy[0];
6829         },
6830
6831         /**
6832          * Gets the y coordinate of the event.
6833          * @return {Number}
6834          */
6835         getPageY : function(){
6836             return this.xy[1];
6837         },
6838
6839         /**
6840          * Gets the time of the event.
6841          * @return {Number}
6842          */
6843         getTime : function(){
6844             if(this.browserEvent){
6845                 return E.getTime(this.browserEvent);
6846             }
6847             return null;
6848         },
6849
6850         /**
6851          * Gets the page coordinates of the event.
6852          * @return {Array} The xy values like [x, y]
6853          */
6854         getXY : function(){
6855             return this.xy;
6856         },
6857
6858         /**
6859          * Gets the target for the event.
6860          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6861          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6862                 search as a number or element (defaults to 10 || document.body)
6863          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6864          * @return {HTMLelement}
6865          */
6866         getTarget : function(selector, maxDepth, returnEl){
6867             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6868         },
6869         /**
6870          * Gets the related target.
6871          * @return {HTMLElement}
6872          */
6873         getRelatedTarget : function(){
6874             if(this.browserEvent){
6875                 return E.getRelatedTarget(this.browserEvent);
6876             }
6877             return null;
6878         },
6879
6880         /**
6881          * Normalizes mouse wheel delta across browsers
6882          * @return {Number} The delta
6883          */
6884         getWheelDelta : function(){
6885             var e = this.browserEvent;
6886             var delta = 0;
6887             if(e.wheelDelta){ /* IE/Opera. */
6888                 delta = e.wheelDelta/120;
6889             }else if(e.detail){ /* Mozilla case. */
6890                 delta = -e.detail/3;
6891             }
6892             return delta;
6893         },
6894
6895         /**
6896          * Returns true if the control, meta, shift or alt key was pressed during this event.
6897          * @return {Boolean}
6898          */
6899         hasModifier : function(){
6900             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6901         },
6902
6903         /**
6904          * Returns true if the target of this event equals el or is a child of el
6905          * @param {String/HTMLElement/Element} el
6906          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6907          * @return {Boolean}
6908          */
6909         within : function(el, related){
6910             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6911             return t && Roo.fly(el).contains(t);
6912         },
6913
6914         getPoint : function(){
6915             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6916         }
6917     };
6918
6919     return new Roo.EventObjectImpl();
6920 }();
6921             
6922     /*
6923  * Based on:
6924  * Ext JS Library 1.1.1
6925  * Copyright(c) 2006-2007, Ext JS, LLC.
6926  *
6927  * Originally Released Under LGPL - original licence link has changed is not relivant.
6928  *
6929  * Fork - LGPL
6930  * <script type="text/javascript">
6931  */
6932
6933  
6934 // was in Composite Element!??!?!
6935  
6936 (function(){
6937     var D = Roo.lib.Dom;
6938     var E = Roo.lib.Event;
6939     var A = Roo.lib.Anim;
6940
6941     // local style camelizing for speed
6942     var propCache = {};
6943     var camelRe = /(-[a-z])/gi;
6944     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6945     var view = document.defaultView;
6946
6947 /**
6948  * @class Roo.Element
6949  * Represents an Element in the DOM.<br><br>
6950  * Usage:<br>
6951 <pre><code>
6952 var el = Roo.get("my-div");
6953
6954 // or with getEl
6955 var el = getEl("my-div");
6956
6957 // or with a DOM element
6958 var el = Roo.get(myDivElement);
6959 </code></pre>
6960  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6961  * each call instead of constructing a new one.<br><br>
6962  * <b>Animations</b><br />
6963  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6964  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6965 <pre>
6966 Option    Default   Description
6967 --------- --------  ---------------------------------------------
6968 duration  .35       The duration of the animation in seconds
6969 easing    easeOut   The YUI easing method
6970 callback  none      A function to execute when the anim completes
6971 scope     this      The scope (this) of the callback function
6972 </pre>
6973 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6974 * manipulate the animation. Here's an example:
6975 <pre><code>
6976 var el = Roo.get("my-div");
6977
6978 // no animation
6979 el.setWidth(100);
6980
6981 // default animation
6982 el.setWidth(100, true);
6983
6984 // animation with some options set
6985 el.setWidth(100, {
6986     duration: 1,
6987     callback: this.foo,
6988     scope: this
6989 });
6990
6991 // using the "anim" property to get the Anim object
6992 var opt = {
6993     duration: 1,
6994     callback: this.foo,
6995     scope: this
6996 };
6997 el.setWidth(100, opt);
6998 ...
6999 if(opt.anim.isAnimated()){
7000     opt.anim.stop();
7001 }
7002 </code></pre>
7003 * <b> Composite (Collections of) Elements</b><br />
7004  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7005  * @constructor Create a new Element directly.
7006  * @param {String/HTMLElement} element
7007  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
7008  */
7009     Roo.Element = function(element, forceNew){
7010         var dom = typeof element == "string" ?
7011                 document.getElementById(element) : element;
7012         if(!dom){ // invalid id/element
7013             return null;
7014         }
7015         var id = dom.id;
7016         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7017             return Roo.Element.cache[id];
7018         }
7019
7020         /**
7021          * The DOM element
7022          * @type HTMLElement
7023          */
7024         this.dom = dom;
7025
7026         /**
7027          * The DOM element ID
7028          * @type String
7029          */
7030         this.id = id || Roo.id(dom);
7031     };
7032
7033     var El = Roo.Element;
7034
7035     El.prototype = {
7036         /**
7037          * The element's default display mode  (defaults to "")
7038          * @type String
7039          */
7040         originalDisplay : "",
7041
7042         visibilityMode : 1,
7043         /**
7044          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7045          * @type String
7046          */
7047         defaultUnit : "px",
7048         /**
7049          * Sets the element's visibility mode. When setVisible() is called it
7050          * will use this to determine whether to set the visibility or the display property.
7051          * @param visMode Element.VISIBILITY or Element.DISPLAY
7052          * @return {Roo.Element} this
7053          */
7054         setVisibilityMode : function(visMode){
7055             this.visibilityMode = visMode;
7056             return this;
7057         },
7058         /**
7059          * Convenience method for setVisibilityMode(Element.DISPLAY)
7060          * @param {String} display (optional) What to set display to when visible
7061          * @return {Roo.Element} this
7062          */
7063         enableDisplayMode : function(display){
7064             this.setVisibilityMode(El.DISPLAY);
7065             if(typeof display != "undefined") this.originalDisplay = display;
7066             return this;
7067         },
7068
7069         /**
7070          * 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)
7071          * @param {String} selector The simple selector to test
7072          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7073                 search as a number or element (defaults to 10 || document.body)
7074          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7075          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7076          */
7077         findParent : function(simpleSelector, maxDepth, returnEl){
7078             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7079             maxDepth = maxDepth || 50;
7080             if(typeof maxDepth != "number"){
7081                 stopEl = Roo.getDom(maxDepth);
7082                 maxDepth = 10;
7083             }
7084             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7085                 if(dq.is(p, simpleSelector)){
7086                     return returnEl ? Roo.get(p) : p;
7087                 }
7088                 depth++;
7089                 p = p.parentNode;
7090             }
7091             return null;
7092         },
7093
7094
7095         /**
7096          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7097          * @param {String} selector The simple selector to test
7098          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7099                 search as a number or element (defaults to 10 || document.body)
7100          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7101          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7102          */
7103         findParentNode : function(simpleSelector, maxDepth, returnEl){
7104             var p = Roo.fly(this.dom.parentNode, '_internal');
7105             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7106         },
7107
7108         /**
7109          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7110          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7111          * @param {String} selector The simple selector to test
7112          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7113                 search as a number or element (defaults to 10 || document.body)
7114          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7115          */
7116         up : function(simpleSelector, maxDepth){
7117             return this.findParentNode(simpleSelector, maxDepth, true);
7118         },
7119
7120
7121
7122         /**
7123          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7124          * @param {String} selector The simple selector to test
7125          * @return {Boolean} True if this element matches the selector, else false
7126          */
7127         is : function(simpleSelector){
7128             return Roo.DomQuery.is(this.dom, simpleSelector);
7129         },
7130
7131         /**
7132          * Perform animation on this element.
7133          * @param {Object} args The YUI animation control args
7134          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7135          * @param {Function} onComplete (optional) Function to call when animation completes
7136          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7137          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7138          * @return {Roo.Element} this
7139          */
7140         animate : function(args, duration, onComplete, easing, animType){
7141             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7142             return this;
7143         },
7144
7145         /*
7146          * @private Internal animation call
7147          */
7148         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7149             animType = animType || 'run';
7150             opt = opt || {};
7151             var anim = Roo.lib.Anim[animType](
7152                 this.dom, args,
7153                 (opt.duration || defaultDur) || .35,
7154                 (opt.easing || defaultEase) || 'easeOut',
7155                 function(){
7156                     Roo.callback(cb, this);
7157                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7158                 },
7159                 this
7160             );
7161             opt.anim = anim;
7162             return anim;
7163         },
7164
7165         // private legacy anim prep
7166         preanim : function(a, i){
7167             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7168         },
7169
7170         /**
7171          * Removes worthless text nodes
7172          * @param {Boolean} forceReclean (optional) By default the element
7173          * keeps track if it has been cleaned already so
7174          * you can call this over and over. However, if you update the element and
7175          * need to force a reclean, you can pass true.
7176          */
7177         clean : function(forceReclean){
7178             if(this.isCleaned && forceReclean !== true){
7179                 return this;
7180             }
7181             var ns = /\S/;
7182             var d = this.dom, n = d.firstChild, ni = -1;
7183             while(n){
7184                 var nx = n.nextSibling;
7185                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7186                     d.removeChild(n);
7187                 }else{
7188                     n.nodeIndex = ++ni;
7189                 }
7190                 n = nx;
7191             }
7192             this.isCleaned = true;
7193             return this;
7194         },
7195
7196         // private
7197         calcOffsetsTo : function(el){
7198             el = Roo.get(el);
7199             var d = el.dom;
7200             var restorePos = false;
7201             if(el.getStyle('position') == 'static'){
7202                 el.position('relative');
7203                 restorePos = true;
7204             }
7205             var x = 0, y =0;
7206             var op = this.dom;
7207             while(op && op != d && op.tagName != 'HTML'){
7208                 x+= op.offsetLeft;
7209                 y+= op.offsetTop;
7210                 op = op.offsetParent;
7211             }
7212             if(restorePos){
7213                 el.position('static');
7214             }
7215             return [x, y];
7216         },
7217
7218         /**
7219          * Scrolls this element into view within the passed container.
7220          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7221          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7222          * @return {Roo.Element} this
7223          */
7224         scrollIntoView : function(container, hscroll){
7225             var c = Roo.getDom(container) || document.body;
7226             var el = this.dom;
7227
7228             var o = this.calcOffsetsTo(c),
7229                 l = o[0],
7230                 t = o[1],
7231                 b = t+el.offsetHeight,
7232                 r = l+el.offsetWidth;
7233
7234             var ch = c.clientHeight;
7235             var ct = parseInt(c.scrollTop, 10);
7236             var cl = parseInt(c.scrollLeft, 10);
7237             var cb = ct + ch;
7238             var cr = cl + c.clientWidth;
7239
7240             if(t < ct){
7241                 c.scrollTop = t;
7242             }else if(b > cb){
7243                 c.scrollTop = b-ch;
7244             }
7245
7246             if(hscroll !== false){
7247                 if(l < cl){
7248                     c.scrollLeft = l;
7249                 }else if(r > cr){
7250                     c.scrollLeft = r-c.clientWidth;
7251                 }
7252             }
7253             return this;
7254         },
7255
7256         // private
7257         scrollChildIntoView : function(child, hscroll){
7258             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7259         },
7260
7261         /**
7262          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7263          * the new height may not be available immediately.
7264          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7265          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7266          * @param {Function} onComplete (optional) Function to call when animation completes
7267          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7268          * @return {Roo.Element} this
7269          */
7270         autoHeight : function(animate, duration, onComplete, easing){
7271             var oldHeight = this.getHeight();
7272             this.clip();
7273             this.setHeight(1); // force clipping
7274             setTimeout(function(){
7275                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7276                 if(!animate){
7277                     this.setHeight(height);
7278                     this.unclip();
7279                     if(typeof onComplete == "function"){
7280                         onComplete();
7281                     }
7282                 }else{
7283                     this.setHeight(oldHeight); // restore original height
7284                     this.setHeight(height, animate, duration, function(){
7285                         this.unclip();
7286                         if(typeof onComplete == "function") onComplete();
7287                     }.createDelegate(this), easing);
7288                 }
7289             }.createDelegate(this), 0);
7290             return this;
7291         },
7292
7293         /**
7294          * Returns true if this element is an ancestor of the passed element
7295          * @param {HTMLElement/String} el The element to check
7296          * @return {Boolean} True if this element is an ancestor of el, else false
7297          */
7298         contains : function(el){
7299             if(!el){return false;}
7300             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7301         },
7302
7303         /**
7304          * Checks whether the element is currently visible using both visibility and display properties.
7305          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7306          * @return {Boolean} True if the element is currently visible, else false
7307          */
7308         isVisible : function(deep) {
7309             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7310             if(deep !== true || !vis){
7311                 return vis;
7312             }
7313             var p = this.dom.parentNode;
7314             while(p && p.tagName.toLowerCase() != "body"){
7315                 if(!Roo.fly(p, '_isVisible').isVisible()){
7316                     return false;
7317                 }
7318                 p = p.parentNode;
7319             }
7320             return true;
7321         },
7322
7323         /**
7324          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7325          * @param {String} selector The CSS selector
7326          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7327          * @return {CompositeElement/CompositeElementLite} The composite element
7328          */
7329         select : function(selector, unique){
7330             return El.select(selector, unique, this.dom);
7331         },
7332
7333         /**
7334          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7335          * @param {String} selector The CSS selector
7336          * @return {Array} An array of the matched nodes
7337          */
7338         query : function(selector, unique){
7339             return Roo.DomQuery.select(selector, this.dom);
7340         },
7341
7342         /**
7343          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7344          * @param {String} selector The CSS selector
7345          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7346          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7347          */
7348         child : function(selector, returnDom){
7349             var n = Roo.DomQuery.selectNode(selector, this.dom);
7350             return returnDom ? n : Roo.get(n);
7351         },
7352
7353         /**
7354          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7355          * @param {String} selector The CSS selector
7356          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7357          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7358          */
7359         down : function(selector, returnDom){
7360             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7361             return returnDom ? n : Roo.get(n);
7362         },
7363
7364         /**
7365          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7366          * @param {String} group The group the DD object is member of
7367          * @param {Object} config The DD config object
7368          * @param {Object} overrides An object containing methods to override/implement on the DD object
7369          * @return {Roo.dd.DD} The DD object
7370          */
7371         initDD : function(group, config, overrides){
7372             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7373             return Roo.apply(dd, overrides);
7374         },
7375
7376         /**
7377          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7378          * @param {String} group The group the DDProxy object is member of
7379          * @param {Object} config The DDProxy config object
7380          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7381          * @return {Roo.dd.DDProxy} The DDProxy object
7382          */
7383         initDDProxy : function(group, config, overrides){
7384             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7385             return Roo.apply(dd, overrides);
7386         },
7387
7388         /**
7389          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7390          * @param {String} group The group the DDTarget object is member of
7391          * @param {Object} config The DDTarget config object
7392          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7393          * @return {Roo.dd.DDTarget} The DDTarget object
7394          */
7395         initDDTarget : function(group, config, overrides){
7396             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7397             return Roo.apply(dd, overrides);
7398         },
7399
7400         /**
7401          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7402          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7403          * @param {Boolean} visible Whether the element is visible
7404          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7405          * @return {Roo.Element} this
7406          */
7407          setVisible : function(visible, animate){
7408             if(!animate || !A){
7409                 if(this.visibilityMode == El.DISPLAY){
7410                     this.setDisplayed(visible);
7411                 }else{
7412                     this.fixDisplay();
7413                     this.dom.style.visibility = visible ? "visible" : "hidden";
7414                 }
7415             }else{
7416                 // closure for composites
7417                 var dom = this.dom;
7418                 var visMode = this.visibilityMode;
7419                 if(visible){
7420                     this.setOpacity(.01);
7421                     this.setVisible(true);
7422                 }
7423                 this.anim({opacity: { to: (visible?1:0) }},
7424                       this.preanim(arguments, 1),
7425                       null, .35, 'easeIn', function(){
7426                          if(!visible){
7427                              if(visMode == El.DISPLAY){
7428                                  dom.style.display = "none";
7429                              }else{
7430                                  dom.style.visibility = "hidden";
7431                              }
7432                              Roo.get(dom).setOpacity(1);
7433                          }
7434                      });
7435             }
7436             return this;
7437         },
7438
7439         /**
7440          * Returns true if display is not "none"
7441          * @return {Boolean}
7442          */
7443         isDisplayed : function() {
7444             return this.getStyle("display") != "none";
7445         },
7446
7447         /**
7448          * Toggles the element's visibility or display, depending on visibility mode.
7449          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7450          * @return {Roo.Element} this
7451          */
7452         toggle : function(animate){
7453             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7454             return this;
7455         },
7456
7457         /**
7458          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7459          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7460          * @return {Roo.Element} this
7461          */
7462         setDisplayed : function(value) {
7463             if(typeof value == "boolean"){
7464                value = value ? this.originalDisplay : "none";
7465             }
7466             this.setStyle("display", value);
7467             return this;
7468         },
7469
7470         /**
7471          * Tries to focus the element. Any exceptions are caught and ignored.
7472          * @return {Roo.Element} this
7473          */
7474         focus : function() {
7475             try{
7476                 this.dom.focus();
7477             }catch(e){}
7478             return this;
7479         },
7480
7481         /**
7482          * Tries to blur the element. Any exceptions are caught and ignored.
7483          * @return {Roo.Element} this
7484          */
7485         blur : function() {
7486             try{
7487                 this.dom.blur();
7488             }catch(e){}
7489             return this;
7490         },
7491
7492         /**
7493          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7494          * @param {String/Array} className The CSS class to add, or an array of classes
7495          * @return {Roo.Element} this
7496          */
7497         addClass : function(className){
7498             if(className instanceof Array){
7499                 for(var i = 0, len = className.length; i < len; i++) {
7500                     this.addClass(className[i]);
7501                 }
7502             }else{
7503                 if(className && !this.hasClass(className)){
7504                     this.dom.className = this.dom.className + " " + className;
7505                 }
7506             }
7507             return this;
7508         },
7509
7510         /**
7511          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7512          * @param {String/Array} className The CSS class to add, or an array of classes
7513          * @return {Roo.Element} this
7514          */
7515         radioClass : function(className){
7516             var siblings = this.dom.parentNode.childNodes;
7517             for(var i = 0; i < siblings.length; i++) {
7518                 var s = siblings[i];
7519                 if(s.nodeType == 1){
7520                     Roo.get(s).removeClass(className);
7521                 }
7522             }
7523             this.addClass(className);
7524             return this;
7525         },
7526
7527         /**
7528          * Removes one or more CSS classes from the element.
7529          * @param {String/Array} className The CSS class to remove, or an array of classes
7530          * @return {Roo.Element} this
7531          */
7532         removeClass : function(className){
7533             if(!className || !this.dom.className){
7534                 return this;
7535             }
7536             if(className instanceof Array){
7537                 for(var i = 0, len = className.length; i < len; i++) {
7538                     this.removeClass(className[i]);
7539                 }
7540             }else{
7541                 if(this.hasClass(className)){
7542                     var re = this.classReCache[className];
7543                     if (!re) {
7544                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7545                        this.classReCache[className] = re;
7546                     }
7547                     this.dom.className =
7548                         this.dom.className.replace(re, " ");
7549                 }
7550             }
7551             return this;
7552         },
7553
7554         // private
7555         classReCache: {},
7556
7557         /**
7558          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7559          * @param {String} className The CSS class to toggle
7560          * @return {Roo.Element} this
7561          */
7562         toggleClass : function(className){
7563             if(this.hasClass(className)){
7564                 this.removeClass(className);
7565             }else{
7566                 this.addClass(className);
7567             }
7568             return this;
7569         },
7570
7571         /**
7572          * Checks if the specified CSS class exists on this element's DOM node.
7573          * @param {String} className The CSS class to check for
7574          * @return {Boolean} True if the class exists, else false
7575          */
7576         hasClass : function(className){
7577             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7578         },
7579
7580         /**
7581          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7582          * @param {String} oldClassName The CSS class to replace
7583          * @param {String} newClassName The replacement CSS class
7584          * @return {Roo.Element} this
7585          */
7586         replaceClass : function(oldClassName, newClassName){
7587             this.removeClass(oldClassName);
7588             this.addClass(newClassName);
7589             return this;
7590         },
7591
7592         /**
7593          * Returns an object with properties matching the styles requested.
7594          * For example, el.getStyles('color', 'font-size', 'width') might return
7595          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7596          * @param {String} style1 A style name
7597          * @param {String} style2 A style name
7598          * @param {String} etc.
7599          * @return {Object} The style object
7600          */
7601         getStyles : function(){
7602             var a = arguments, len = a.length, r = {};
7603             for(var i = 0; i < len; i++){
7604                 r[a[i]] = this.getStyle(a[i]);
7605             }
7606             return r;
7607         },
7608
7609         /**
7610          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7611          * @param {String} property The style property whose value is returned.
7612          * @return {String} The current value of the style property for this element.
7613          */
7614         getStyle : function(){
7615             return view && view.getComputedStyle ?
7616                 function(prop){
7617                     var el = this.dom, v, cs, camel;
7618                     if(prop == 'float'){
7619                         prop = "cssFloat";
7620                     }
7621                     if(el.style && (v = el.style[prop])){
7622                         return v;
7623                     }
7624                     if(cs = view.getComputedStyle(el, "")){
7625                         if(!(camel = propCache[prop])){
7626                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7627                         }
7628                         return cs[camel];
7629                     }
7630                     return null;
7631                 } :
7632                 function(prop){
7633                     var el = this.dom, v, cs, camel;
7634                     if(prop == 'opacity'){
7635                         if(typeof el.style.filter == 'string'){
7636                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7637                             if(m){
7638                                 var fv = parseFloat(m[1]);
7639                                 if(!isNaN(fv)){
7640                                     return fv ? fv / 100 : 0;
7641                                 }
7642                             }
7643                         }
7644                         return 1;
7645                     }else if(prop == 'float'){
7646                         prop = "styleFloat";
7647                     }
7648                     if(!(camel = propCache[prop])){
7649                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7650                     }
7651                     if(v = el.style[camel]){
7652                         return v;
7653                     }
7654                     if(cs = el.currentStyle){
7655                         return cs[camel];
7656                     }
7657                     return null;
7658                 };
7659         }(),
7660
7661         /**
7662          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7663          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7664          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7665          * @return {Roo.Element} this
7666          */
7667         setStyle : function(prop, value){
7668             if(typeof prop == "string"){
7669                 
7670                 if (prop == 'float') {
7671                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7672                     return this;
7673                 }
7674                 
7675                 var camel;
7676                 if(!(camel = propCache[prop])){
7677                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7678                 }
7679                 
7680                 if(camel == 'opacity') {
7681                     this.setOpacity(value);
7682                 }else{
7683                     this.dom.style[camel] = value;
7684                 }
7685             }else{
7686                 for(var style in prop){
7687                     if(typeof prop[style] != "function"){
7688                        this.setStyle(style, prop[style]);
7689                     }
7690                 }
7691             }
7692             return this;
7693         },
7694
7695         /**
7696          * More flexible version of {@link #setStyle} for setting style properties.
7697          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7698          * a function which returns such a specification.
7699          * @return {Roo.Element} this
7700          */
7701         applyStyles : function(style){
7702             Roo.DomHelper.applyStyles(this.dom, style);
7703             return this;
7704         },
7705
7706         /**
7707           * 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).
7708           * @return {Number} The X position of the element
7709           */
7710         getX : function(){
7711             return D.getX(this.dom);
7712         },
7713
7714         /**
7715           * 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).
7716           * @return {Number} The Y position of the element
7717           */
7718         getY : function(){
7719             return D.getY(this.dom);
7720         },
7721
7722         /**
7723           * 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).
7724           * @return {Array} The XY position of the element
7725           */
7726         getXY : function(){
7727             return D.getXY(this.dom);
7728         },
7729
7730         /**
7731          * 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).
7732          * @param {Number} The X position of the element
7733          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7734          * @return {Roo.Element} this
7735          */
7736         setX : function(x, animate){
7737             if(!animate || !A){
7738                 D.setX(this.dom, x);
7739             }else{
7740                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7741             }
7742             return this;
7743         },
7744
7745         /**
7746          * 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).
7747          * @param {Number} The Y position of the element
7748          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7749          * @return {Roo.Element} this
7750          */
7751         setY : function(y, animate){
7752             if(!animate || !A){
7753                 D.setY(this.dom, y);
7754             }else{
7755                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7756             }
7757             return this;
7758         },
7759
7760         /**
7761          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7762          * @param {String} left The left CSS property value
7763          * @return {Roo.Element} this
7764          */
7765         setLeft : function(left){
7766             this.setStyle("left", this.addUnits(left));
7767             return this;
7768         },
7769
7770         /**
7771          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7772          * @param {String} top The top CSS property value
7773          * @return {Roo.Element} this
7774          */
7775         setTop : function(top){
7776             this.setStyle("top", this.addUnits(top));
7777             return this;
7778         },
7779
7780         /**
7781          * Sets the element's CSS right style.
7782          * @param {String} right The right CSS property value
7783          * @return {Roo.Element} this
7784          */
7785         setRight : function(right){
7786             this.setStyle("right", this.addUnits(right));
7787             return this;
7788         },
7789
7790         /**
7791          * Sets the element's CSS bottom style.
7792          * @param {String} bottom The bottom CSS property value
7793          * @return {Roo.Element} this
7794          */
7795         setBottom : function(bottom){
7796             this.setStyle("bottom", this.addUnits(bottom));
7797             return this;
7798         },
7799
7800         /**
7801          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7802          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7803          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7804          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7805          * @return {Roo.Element} this
7806          */
7807         setXY : function(pos, animate){
7808             if(!animate || !A){
7809                 D.setXY(this.dom, pos);
7810             }else{
7811                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7812             }
7813             return this;
7814         },
7815
7816         /**
7817          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7818          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7819          * @param {Number} x X value for new position (coordinates are page-based)
7820          * @param {Number} y Y value for new position (coordinates are page-based)
7821          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7822          * @return {Roo.Element} this
7823          */
7824         setLocation : function(x, y, animate){
7825             this.setXY([x, y], this.preanim(arguments, 2));
7826             return this;
7827         },
7828
7829         /**
7830          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7831          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7832          * @param {Number} x X value for new position (coordinates are page-based)
7833          * @param {Number} y Y value for new position (coordinates are page-based)
7834          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7835          * @return {Roo.Element} this
7836          */
7837         moveTo : function(x, y, animate){
7838             this.setXY([x, y], this.preanim(arguments, 2));
7839             return this;
7840         },
7841
7842         /**
7843          * Returns the region of the given element.
7844          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7845          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7846          */
7847         getRegion : function(){
7848             return D.getRegion(this.dom);
7849         },
7850
7851         /**
7852          * Returns the offset height of the element
7853          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7854          * @return {Number} The element's height
7855          */
7856         getHeight : function(contentHeight){
7857             var h = this.dom.offsetHeight || 0;
7858             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7859         },
7860
7861         /**
7862          * Returns the offset width of the element
7863          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7864          * @return {Number} The element's width
7865          */
7866         getWidth : function(contentWidth){
7867             var w = this.dom.offsetWidth || 0;
7868             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7869         },
7870
7871         /**
7872          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7873          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7874          * if a height has not been set using CSS.
7875          * @return {Number}
7876          */
7877         getComputedHeight : function(){
7878             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7879             if(!h){
7880                 h = parseInt(this.getStyle('height'), 10) || 0;
7881                 if(!this.isBorderBox()){
7882                     h += this.getFrameWidth('tb');
7883                 }
7884             }
7885             return h;
7886         },
7887
7888         /**
7889          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7890          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7891          * if a width has not been set using CSS.
7892          * @return {Number}
7893          */
7894         getComputedWidth : function(){
7895             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7896             if(!w){
7897                 w = parseInt(this.getStyle('width'), 10) || 0;
7898                 if(!this.isBorderBox()){
7899                     w += this.getFrameWidth('lr');
7900                 }
7901             }
7902             return w;
7903         },
7904
7905         /**
7906          * Returns the size of the element.
7907          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7908          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7909          */
7910         getSize : function(contentSize){
7911             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7912         },
7913
7914         /**
7915          * Returns the width and height of the viewport.
7916          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7917          */
7918         getViewSize : function(){
7919             var d = this.dom, doc = document, aw = 0, ah = 0;
7920             if(d == doc || d == doc.body){
7921                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7922             }else{
7923                 return {
7924                     width : d.clientWidth,
7925                     height: d.clientHeight
7926                 };
7927             }
7928         },
7929
7930         /**
7931          * Returns the value of the "value" attribute
7932          * @param {Boolean} asNumber true to parse the value as a number
7933          * @return {String/Number}
7934          */
7935         getValue : function(asNumber){
7936             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7937         },
7938
7939         // private
7940         adjustWidth : function(width){
7941             if(typeof width == "number"){
7942                 if(this.autoBoxAdjust && !this.isBorderBox()){
7943                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7944                 }
7945                 if(width < 0){
7946                     width = 0;
7947                 }
7948             }
7949             return width;
7950         },
7951
7952         // private
7953         adjustHeight : function(height){
7954             if(typeof height == "number"){
7955                if(this.autoBoxAdjust && !this.isBorderBox()){
7956                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7957                }
7958                if(height < 0){
7959                    height = 0;
7960                }
7961             }
7962             return height;
7963         },
7964
7965         /**
7966          * Set the width of the element
7967          * @param {Number} width The new width
7968          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7969          * @return {Roo.Element} this
7970          */
7971         setWidth : function(width, animate){
7972             width = this.adjustWidth(width);
7973             if(!animate || !A){
7974                 this.dom.style.width = this.addUnits(width);
7975             }else{
7976                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7977             }
7978             return this;
7979         },
7980
7981         /**
7982          * Set the height of the element
7983          * @param {Number} height The new height
7984          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7985          * @return {Roo.Element} this
7986          */
7987          setHeight : function(height, animate){
7988             height = this.adjustHeight(height);
7989             if(!animate || !A){
7990                 this.dom.style.height = this.addUnits(height);
7991             }else{
7992                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7993             }
7994             return this;
7995         },
7996
7997         /**
7998          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7999          * @param {Number} width The new width
8000          * @param {Number} height The new height
8001          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8002          * @return {Roo.Element} this
8003          */
8004          setSize : function(width, height, animate){
8005             if(typeof width == "object"){ // in case of object from getSize()
8006                 height = width.height; width = width.width;
8007             }
8008             width = this.adjustWidth(width); height = this.adjustHeight(height);
8009             if(!animate || !A){
8010                 this.dom.style.width = this.addUnits(width);
8011                 this.dom.style.height = this.addUnits(height);
8012             }else{
8013                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8014             }
8015             return this;
8016         },
8017
8018         /**
8019          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8020          * @param {Number} x X value for new position (coordinates are page-based)
8021          * @param {Number} y Y value for new position (coordinates are page-based)
8022          * @param {Number} width The new width
8023          * @param {Number} height The new height
8024          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8025          * @return {Roo.Element} this
8026          */
8027         setBounds : function(x, y, width, height, animate){
8028             if(!animate || !A){
8029                 this.setSize(width, height);
8030                 this.setLocation(x, y);
8031             }else{
8032                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8033                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8034                               this.preanim(arguments, 4), 'motion');
8035             }
8036             return this;
8037         },
8038
8039         /**
8040          * 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.
8041          * @param {Roo.lib.Region} region The region to fill
8042          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8043          * @return {Roo.Element} this
8044          */
8045         setRegion : function(region, animate){
8046             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8047             return this;
8048         },
8049
8050         /**
8051          * Appends an event handler
8052          *
8053          * @param {String}   eventName     The type of event to append
8054          * @param {Function} fn        The method the event invokes
8055          * @param {Object} scope       (optional) The scope (this object) of the fn
8056          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8057          */
8058         addListener : function(eventName, fn, scope, options){
8059             if (this.dom) {
8060                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8061             }
8062         },
8063
8064         /**
8065          * Removes an event handler from this element
8066          * @param {String} eventName the type of event to remove
8067          * @param {Function} fn the method the event invokes
8068          * @return {Roo.Element} this
8069          */
8070         removeListener : function(eventName, fn){
8071             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8072             return this;
8073         },
8074
8075         /**
8076          * Removes all previous added listeners from this element
8077          * @return {Roo.Element} this
8078          */
8079         removeAllListeners : function(){
8080             E.purgeElement(this.dom);
8081             return this;
8082         },
8083
8084         relayEvent : function(eventName, observable){
8085             this.on(eventName, function(e){
8086                 observable.fireEvent(eventName, e);
8087             });
8088         },
8089
8090         /**
8091          * Set the opacity of the element
8092          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8093          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8094          * @return {Roo.Element} this
8095          */
8096          setOpacity : function(opacity, animate){
8097             if(!animate || !A){
8098                 var s = this.dom.style;
8099                 if(Roo.isIE){
8100                     s.zoom = 1;
8101                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8102                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8103                 }else{
8104                     s.opacity = opacity;
8105                 }
8106             }else{
8107                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8108             }
8109             return this;
8110         },
8111
8112         /**
8113          * Gets the left X coordinate
8114          * @param {Boolean} local True to get the local css position instead of page coordinate
8115          * @return {Number}
8116          */
8117         getLeft : function(local){
8118             if(!local){
8119                 return this.getX();
8120             }else{
8121                 return parseInt(this.getStyle("left"), 10) || 0;
8122             }
8123         },
8124
8125         /**
8126          * Gets the right X coordinate of the element (element X position + element width)
8127          * @param {Boolean} local True to get the local css position instead of page coordinate
8128          * @return {Number}
8129          */
8130         getRight : function(local){
8131             if(!local){
8132                 return this.getX() + this.getWidth();
8133             }else{
8134                 return (this.getLeft(true) + this.getWidth()) || 0;
8135             }
8136         },
8137
8138         /**
8139          * Gets the top Y coordinate
8140          * @param {Boolean} local True to get the local css position instead of page coordinate
8141          * @return {Number}
8142          */
8143         getTop : function(local) {
8144             if(!local){
8145                 return this.getY();
8146             }else{
8147                 return parseInt(this.getStyle("top"), 10) || 0;
8148             }
8149         },
8150
8151         /**
8152          * Gets the bottom Y coordinate of the element (element Y position + element height)
8153          * @param {Boolean} local True to get the local css position instead of page coordinate
8154          * @return {Number}
8155          */
8156         getBottom : function(local){
8157             if(!local){
8158                 return this.getY() + this.getHeight();
8159             }else{
8160                 return (this.getTop(true) + this.getHeight()) || 0;
8161             }
8162         },
8163
8164         /**
8165         * Initializes positioning on this element. If a desired position is not passed, it will make the
8166         * the element positioned relative IF it is not already positioned.
8167         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8168         * @param {Number} zIndex (optional) The zIndex to apply
8169         * @param {Number} x (optional) Set the page X position
8170         * @param {Number} y (optional) Set the page Y position
8171         */
8172         position : function(pos, zIndex, x, y){
8173             if(!pos){
8174                if(this.getStyle('position') == 'static'){
8175                    this.setStyle('position', 'relative');
8176                }
8177             }else{
8178                 this.setStyle("position", pos);
8179             }
8180             if(zIndex){
8181                 this.setStyle("z-index", zIndex);
8182             }
8183             if(x !== undefined && y !== undefined){
8184                 this.setXY([x, y]);
8185             }else if(x !== undefined){
8186                 this.setX(x);
8187             }else if(y !== undefined){
8188                 this.setY(y);
8189             }
8190         },
8191
8192         /**
8193         * Clear positioning back to the default when the document was loaded
8194         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8195         * @return {Roo.Element} this
8196          */
8197         clearPositioning : function(value){
8198             value = value ||'';
8199             this.setStyle({
8200                 "left": value,
8201                 "right": value,
8202                 "top": value,
8203                 "bottom": value,
8204                 "z-index": "",
8205                 "position" : "static"
8206             });
8207             return this;
8208         },
8209
8210         /**
8211         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8212         * snapshot before performing an update and then restoring the element.
8213         * @return {Object}
8214         */
8215         getPositioning : function(){
8216             var l = this.getStyle("left");
8217             var t = this.getStyle("top");
8218             return {
8219                 "position" : this.getStyle("position"),
8220                 "left" : l,
8221                 "right" : l ? "" : this.getStyle("right"),
8222                 "top" : t,
8223                 "bottom" : t ? "" : this.getStyle("bottom"),
8224                 "z-index" : this.getStyle("z-index")
8225             };
8226         },
8227
8228         /**
8229          * Gets the width of the border(s) for the specified side(s)
8230          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8231          * passing lr would get the border (l)eft width + the border (r)ight width.
8232          * @return {Number} The width of the sides passed added together
8233          */
8234         getBorderWidth : function(side){
8235             return this.addStyles(side, El.borders);
8236         },
8237
8238         /**
8239          * Gets the width of the padding(s) for the specified side(s)
8240          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8241          * passing lr would get the padding (l)eft + the padding (r)ight.
8242          * @return {Number} The padding of the sides passed added together
8243          */
8244         getPadding : function(side){
8245             return this.addStyles(side, El.paddings);
8246         },
8247
8248         /**
8249         * Set positioning with an object returned by getPositioning().
8250         * @param {Object} posCfg
8251         * @return {Roo.Element} this
8252          */
8253         setPositioning : function(pc){
8254             this.applyStyles(pc);
8255             if(pc.right == "auto"){
8256                 this.dom.style.right = "";
8257             }
8258             if(pc.bottom == "auto"){
8259                 this.dom.style.bottom = "";
8260             }
8261             return this;
8262         },
8263
8264         // private
8265         fixDisplay : function(){
8266             if(this.getStyle("display") == "none"){
8267                 this.setStyle("visibility", "hidden");
8268                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8269                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8270                     this.setStyle("display", "block");
8271                 }
8272             }
8273         },
8274
8275         /**
8276          * Quick set left and top adding default units
8277          * @param {String} left The left CSS property value
8278          * @param {String} top The top CSS property value
8279          * @return {Roo.Element} this
8280          */
8281          setLeftTop : function(left, top){
8282             this.dom.style.left = this.addUnits(left);
8283             this.dom.style.top = this.addUnits(top);
8284             return this;
8285         },
8286
8287         /**
8288          * Move this element relative to its current position.
8289          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8290          * @param {Number} distance How far to move the element in pixels
8291          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8292          * @return {Roo.Element} this
8293          */
8294          move : function(direction, distance, animate){
8295             var xy = this.getXY();
8296             direction = direction.toLowerCase();
8297             switch(direction){
8298                 case "l":
8299                 case "left":
8300                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8301                     break;
8302                case "r":
8303                case "right":
8304                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8305                     break;
8306                case "t":
8307                case "top":
8308                case "up":
8309                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8310                     break;
8311                case "b":
8312                case "bottom":
8313                case "down":
8314                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8315                     break;
8316             }
8317             return this;
8318         },
8319
8320         /**
8321          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8322          * @return {Roo.Element} this
8323          */
8324         clip : function(){
8325             if(!this.isClipped){
8326                this.isClipped = true;
8327                this.originalClip = {
8328                    "o": this.getStyle("overflow"),
8329                    "x": this.getStyle("overflow-x"),
8330                    "y": this.getStyle("overflow-y")
8331                };
8332                this.setStyle("overflow", "hidden");
8333                this.setStyle("overflow-x", "hidden");
8334                this.setStyle("overflow-y", "hidden");
8335             }
8336             return this;
8337         },
8338
8339         /**
8340          *  Return clipping (overflow) to original clipping before clip() was called
8341          * @return {Roo.Element} this
8342          */
8343         unclip : function(){
8344             if(this.isClipped){
8345                 this.isClipped = false;
8346                 var o = this.originalClip;
8347                 if(o.o){this.setStyle("overflow", o.o);}
8348                 if(o.x){this.setStyle("overflow-x", o.x);}
8349                 if(o.y){this.setStyle("overflow-y", o.y);}
8350             }
8351             return this;
8352         },
8353
8354
8355         /**
8356          * Gets the x,y coordinates specified by the anchor position on the element.
8357          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8358          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8359          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8360          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8361          * @return {Array} [x, y] An array containing the element's x and y coordinates
8362          */
8363         getAnchorXY : function(anchor, local, s){
8364             //Passing a different size is useful for pre-calculating anchors,
8365             //especially for anchored animations that change the el size.
8366
8367             var w, h, vp = false;
8368             if(!s){
8369                 var d = this.dom;
8370                 if(d == document.body || d == document){
8371                     vp = true;
8372                     w = D.getViewWidth(); h = D.getViewHeight();
8373                 }else{
8374                     w = this.getWidth(); h = this.getHeight();
8375                 }
8376             }else{
8377                 w = s.width;  h = s.height;
8378             }
8379             var x = 0, y = 0, r = Math.round;
8380             switch((anchor || "tl").toLowerCase()){
8381                 case "c":
8382                     x = r(w*.5);
8383                     y = r(h*.5);
8384                 break;
8385                 case "t":
8386                     x = r(w*.5);
8387                     y = 0;
8388                 break;
8389                 case "l":
8390                     x = 0;
8391                     y = r(h*.5);
8392                 break;
8393                 case "r":
8394                     x = w;
8395                     y = r(h*.5);
8396                 break;
8397                 case "b":
8398                     x = r(w*.5);
8399                     y = h;
8400                 break;
8401                 case "tl":
8402                     x = 0;
8403                     y = 0;
8404                 break;
8405                 case "bl":
8406                     x = 0;
8407                     y = h;
8408                 break;
8409                 case "br":
8410                     x = w;
8411                     y = h;
8412                 break;
8413                 case "tr":
8414                     x = w;
8415                     y = 0;
8416                 break;
8417             }
8418             if(local === true){
8419                 return [x, y];
8420             }
8421             if(vp){
8422                 var sc = this.getScroll();
8423                 return [x + sc.left, y + sc.top];
8424             }
8425             //Add the element's offset xy
8426             var o = this.getXY();
8427             return [x+o[0], y+o[1]];
8428         },
8429
8430         /**
8431          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8432          * supported position values.
8433          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8434          * @param {String} position The position to align to.
8435          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8436          * @return {Array} [x, y]
8437          */
8438         getAlignToXY : function(el, p, o){
8439             el = Roo.get(el);
8440             var d = this.dom;
8441             if(!el.dom){
8442                 throw "Element.alignTo with an element that doesn't exist";
8443             }
8444             var c = false; //constrain to viewport
8445             var p1 = "", p2 = "";
8446             o = o || [0,0];
8447
8448             if(!p){
8449                 p = "tl-bl";
8450             }else if(p == "?"){
8451                 p = "tl-bl?";
8452             }else if(p.indexOf("-") == -1){
8453                 p = "tl-" + p;
8454             }
8455             p = p.toLowerCase();
8456             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8457             if(!m){
8458                throw "Element.alignTo with an invalid alignment " + p;
8459             }
8460             p1 = m[1]; p2 = m[2]; c = !!m[3];
8461
8462             //Subtract the aligned el's internal xy from the target's offset xy
8463             //plus custom offset to get the aligned el's new offset xy
8464             var a1 = this.getAnchorXY(p1, true);
8465             var a2 = el.getAnchorXY(p2, false);
8466             var x = a2[0] - a1[0] + o[0];
8467             var y = a2[1] - a1[1] + o[1];
8468             if(c){
8469                 //constrain the aligned el to viewport if necessary
8470                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8471                 // 5px of margin for ie
8472                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8473
8474                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8475                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8476                 //otherwise swap the aligned el to the opposite border of the target.
8477                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8478                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8479                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8480                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8481
8482                var doc = document;
8483                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8484                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8485
8486                if((x+w) > dw + scrollX){
8487                     x = swapX ? r.left-w : dw+scrollX-w;
8488                 }
8489                if(x < scrollX){
8490                    x = swapX ? r.right : scrollX;
8491                }
8492                if((y+h) > dh + scrollY){
8493                     y = swapY ? r.top-h : dh+scrollY-h;
8494                 }
8495                if (y < scrollY){
8496                    y = swapY ? r.bottom : scrollY;
8497                }
8498             }
8499             return [x,y];
8500         },
8501
8502         // private
8503         getConstrainToXY : function(){
8504             var os = {top:0, left:0, bottom:0, right: 0};
8505
8506             return function(el, local, offsets, proposedXY){
8507                 el = Roo.get(el);
8508                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8509
8510                 var vw, vh, vx = 0, vy = 0;
8511                 if(el.dom == document.body || el.dom == document){
8512                     vw = Roo.lib.Dom.getViewWidth();
8513                     vh = Roo.lib.Dom.getViewHeight();
8514                 }else{
8515                     vw = el.dom.clientWidth;
8516                     vh = el.dom.clientHeight;
8517                     if(!local){
8518                         var vxy = el.getXY();
8519                         vx = vxy[0];
8520                         vy = vxy[1];
8521                     }
8522                 }
8523
8524                 var s = el.getScroll();
8525
8526                 vx += offsets.left + s.left;
8527                 vy += offsets.top + s.top;
8528
8529                 vw -= offsets.right;
8530                 vh -= offsets.bottom;
8531
8532                 var vr = vx+vw;
8533                 var vb = vy+vh;
8534
8535                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8536                 var x = xy[0], y = xy[1];
8537                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8538
8539                 // only move it if it needs it
8540                 var moved = false;
8541
8542                 // first validate right/bottom
8543                 if((x + w) > vr){
8544                     x = vr - w;
8545                     moved = true;
8546                 }
8547                 if((y + h) > vb){
8548                     y = vb - h;
8549                     moved = true;
8550                 }
8551                 // then make sure top/left isn't negative
8552                 if(x < vx){
8553                     x = vx;
8554                     moved = true;
8555                 }
8556                 if(y < vy){
8557                     y = vy;
8558                     moved = true;
8559                 }
8560                 return moved ? [x, y] : false;
8561             };
8562         }(),
8563
8564         // private
8565         adjustForConstraints : function(xy, parent, offsets){
8566             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8567         },
8568
8569         /**
8570          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8571          * document it aligns it to the viewport.
8572          * The position parameter is optional, and can be specified in any one of the following formats:
8573          * <ul>
8574          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8575          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8576          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8577          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8578          *   <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
8579          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8580          * </ul>
8581          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8582          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8583          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8584          * that specified in order to enforce the viewport constraints.
8585          * Following are all of the supported anchor positions:
8586     <pre>
8587     Value  Description
8588     -----  -----------------------------
8589     tl     The top left corner (default)
8590     t      The center of the top edge
8591     tr     The top right corner
8592     l      The center of the left edge
8593     c      In the center of the element
8594     r      The center of the right edge
8595     bl     The bottom left corner
8596     b      The center of the bottom edge
8597     br     The bottom right corner
8598     </pre>
8599     Example Usage:
8600     <pre><code>
8601     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8602     el.alignTo("other-el");
8603
8604     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8605     el.alignTo("other-el", "tr?");
8606
8607     // align the bottom right corner of el with the center left edge of other-el
8608     el.alignTo("other-el", "br-l?");
8609
8610     // align the center of el with the bottom left corner of other-el and
8611     // adjust the x position by -6 pixels (and the y position by 0)
8612     el.alignTo("other-el", "c-bl", [-6, 0]);
8613     </code></pre>
8614          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8615          * @param {String} position The position to align to.
8616          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8617          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8618          * @return {Roo.Element} this
8619          */
8620         alignTo : function(element, position, offsets, animate){
8621             var xy = this.getAlignToXY(element, position, offsets);
8622             this.setXY(xy, this.preanim(arguments, 3));
8623             return this;
8624         },
8625
8626         /**
8627          * Anchors an element to another element and realigns it when the window is resized.
8628          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8629          * @param {String} position The position to align to.
8630          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8631          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8632          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8633          * is a number, it is used as the buffer delay (defaults to 50ms).
8634          * @param {Function} callback The function to call after the animation finishes
8635          * @return {Roo.Element} this
8636          */
8637         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8638             var action = function(){
8639                 this.alignTo(el, alignment, offsets, animate);
8640                 Roo.callback(callback, this);
8641             };
8642             Roo.EventManager.onWindowResize(action, this);
8643             var tm = typeof monitorScroll;
8644             if(tm != 'undefined'){
8645                 Roo.EventManager.on(window, 'scroll', action, this,
8646                     {buffer: tm == 'number' ? monitorScroll : 50});
8647             }
8648             action.call(this); // align immediately
8649             return this;
8650         },
8651         /**
8652          * Clears any opacity settings from this element. Required in some cases for IE.
8653          * @return {Roo.Element} this
8654          */
8655         clearOpacity : function(){
8656             if (window.ActiveXObject) {
8657                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8658                     this.dom.style.filter = "";
8659                 }
8660             } else {
8661                 this.dom.style.opacity = "";
8662                 this.dom.style["-moz-opacity"] = "";
8663                 this.dom.style["-khtml-opacity"] = "";
8664             }
8665             return this;
8666         },
8667
8668         /**
8669          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8670          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8671          * @return {Roo.Element} this
8672          */
8673         hide : function(animate){
8674             this.setVisible(false, this.preanim(arguments, 0));
8675             return this;
8676         },
8677
8678         /**
8679         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8680         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8681          * @return {Roo.Element} this
8682          */
8683         show : function(animate){
8684             this.setVisible(true, this.preanim(arguments, 0));
8685             return this;
8686         },
8687
8688         /**
8689          * @private Test if size has a unit, otherwise appends the default
8690          */
8691         addUnits : function(size){
8692             return Roo.Element.addUnits(size, this.defaultUnit);
8693         },
8694
8695         /**
8696          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8697          * @return {Roo.Element} this
8698          */
8699         beginMeasure : function(){
8700             var el = this.dom;
8701             if(el.offsetWidth || el.offsetHeight){
8702                 return this; // offsets work already
8703             }
8704             var changed = [];
8705             var p = this.dom, b = document.body; // start with this element
8706             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8707                 var pe = Roo.get(p);
8708                 if(pe.getStyle('display') == 'none'){
8709                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8710                     p.style.visibility = "hidden";
8711                     p.style.display = "block";
8712                 }
8713                 p = p.parentNode;
8714             }
8715             this._measureChanged = changed;
8716             return this;
8717
8718         },
8719
8720         /**
8721          * Restores displays to before beginMeasure was called
8722          * @return {Roo.Element} this
8723          */
8724         endMeasure : function(){
8725             var changed = this._measureChanged;
8726             if(changed){
8727                 for(var i = 0, len = changed.length; i < len; i++) {
8728                     var r = changed[i];
8729                     r.el.style.visibility = r.visibility;
8730                     r.el.style.display = "none";
8731                 }
8732                 this._measureChanged = null;
8733             }
8734             return this;
8735         },
8736
8737         /**
8738         * Update the innerHTML of this element, optionally searching for and processing scripts
8739         * @param {String} html The new HTML
8740         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8741         * @param {Function} callback For async script loading you can be noticed when the update completes
8742         * @return {Roo.Element} this
8743          */
8744         update : function(html, loadScripts, callback){
8745             if(typeof html == "undefined"){
8746                 html = "";
8747             }
8748             if(loadScripts !== true){
8749                 this.dom.innerHTML = html;
8750                 if(typeof callback == "function"){
8751                     callback();
8752                 }
8753                 return this;
8754             }
8755             var id = Roo.id();
8756             var dom = this.dom;
8757
8758             html += '<span id="' + id + '"></span>';
8759
8760             E.onAvailable(id, function(){
8761                 var hd = document.getElementsByTagName("head")[0];
8762                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8763                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8764                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8765
8766                 var match;
8767                 while(match = re.exec(html)){
8768                     var attrs = match[1];
8769                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8770                     if(srcMatch && srcMatch[2]){
8771                        var s = document.createElement("script");
8772                        s.src = srcMatch[2];
8773                        var typeMatch = attrs.match(typeRe);
8774                        if(typeMatch && typeMatch[2]){
8775                            s.type = typeMatch[2];
8776                        }
8777                        hd.appendChild(s);
8778                     }else if(match[2] && match[2].length > 0){
8779                         if(window.execScript) {
8780                            window.execScript(match[2]);
8781                         } else {
8782                             /**
8783                              * eval:var:id
8784                              * eval:var:dom
8785                              * eval:var:html
8786                              * 
8787                              */
8788                            window.eval(match[2]);
8789                         }
8790                     }
8791                 }
8792                 var el = document.getElementById(id);
8793                 if(el){el.parentNode.removeChild(el);}
8794                 if(typeof callback == "function"){
8795                     callback();
8796                 }
8797             });
8798             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8799             return this;
8800         },
8801
8802         /**
8803          * Direct access to the UpdateManager update() method (takes the same parameters).
8804          * @param {String/Function} url The url for this request or a function to call to get the url
8805          * @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}
8806          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8807          * @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.
8808          * @return {Roo.Element} this
8809          */
8810         load : function(){
8811             var um = this.getUpdateManager();
8812             um.update.apply(um, arguments);
8813             return this;
8814         },
8815
8816         /**
8817         * Gets this element's UpdateManager
8818         * @return {Roo.UpdateManager} The UpdateManager
8819         */
8820         getUpdateManager : function(){
8821             if(!this.updateManager){
8822                 this.updateManager = new Roo.UpdateManager(this);
8823             }
8824             return this.updateManager;
8825         },
8826
8827         /**
8828          * Disables text selection for this element (normalized across browsers)
8829          * @return {Roo.Element} this
8830          */
8831         unselectable : function(){
8832             this.dom.unselectable = "on";
8833             this.swallowEvent("selectstart", true);
8834             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8835             this.addClass("x-unselectable");
8836             return this;
8837         },
8838
8839         /**
8840         * Calculates the x, y to center this element on the screen
8841         * @return {Array} The x, y values [x, y]
8842         */
8843         getCenterXY : function(){
8844             return this.getAlignToXY(document, 'c-c');
8845         },
8846
8847         /**
8848         * Centers the Element in either the viewport, or another Element.
8849         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8850         */
8851         center : function(centerIn){
8852             this.alignTo(centerIn || document, 'c-c');
8853             return this;
8854         },
8855
8856         /**
8857          * Tests various css rules/browsers to determine if this element uses a border box
8858          * @return {Boolean}
8859          */
8860         isBorderBox : function(){
8861             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8862         },
8863
8864         /**
8865          * Return a box {x, y, width, height} that can be used to set another elements
8866          * size/location to match this element.
8867          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8868          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8869          * @return {Object} box An object in the format {x, y, width, height}
8870          */
8871         getBox : function(contentBox, local){
8872             var xy;
8873             if(!local){
8874                 xy = this.getXY();
8875             }else{
8876                 var left = parseInt(this.getStyle("left"), 10) || 0;
8877                 var top = parseInt(this.getStyle("top"), 10) || 0;
8878                 xy = [left, top];
8879             }
8880             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8881             if(!contentBox){
8882                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8883             }else{
8884                 var l = this.getBorderWidth("l")+this.getPadding("l");
8885                 var r = this.getBorderWidth("r")+this.getPadding("r");
8886                 var t = this.getBorderWidth("t")+this.getPadding("t");
8887                 var b = this.getBorderWidth("b")+this.getPadding("b");
8888                 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)};
8889             }
8890             bx.right = bx.x + bx.width;
8891             bx.bottom = bx.y + bx.height;
8892             return bx;
8893         },
8894
8895         /**
8896          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8897          for more information about the sides.
8898          * @param {String} sides
8899          * @return {Number}
8900          */
8901         getFrameWidth : function(sides, onlyContentBox){
8902             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8903         },
8904
8905         /**
8906          * 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.
8907          * @param {Object} box The box to fill {x, y, width, height}
8908          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8909          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8910          * @return {Roo.Element} this
8911          */
8912         setBox : function(box, adjust, animate){
8913             var w = box.width, h = box.height;
8914             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8915                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8916                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8917             }
8918             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8919             return this;
8920         },
8921
8922         /**
8923          * Forces the browser to repaint this element
8924          * @return {Roo.Element} this
8925          */
8926          repaint : function(){
8927             var dom = this.dom;
8928             this.addClass("x-repaint");
8929             setTimeout(function(){
8930                 Roo.get(dom).removeClass("x-repaint");
8931             }, 1);
8932             return this;
8933         },
8934
8935         /**
8936          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8937          * then it returns the calculated width of the sides (see getPadding)
8938          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8939          * @return {Object/Number}
8940          */
8941         getMargins : function(side){
8942             if(!side){
8943                 return {
8944                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8945                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8946                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8947                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8948                 };
8949             }else{
8950                 return this.addStyles(side, El.margins);
8951              }
8952         },
8953
8954         // private
8955         addStyles : function(sides, styles){
8956             var val = 0, v, w;
8957             for(var i = 0, len = sides.length; i < len; i++){
8958                 v = this.getStyle(styles[sides.charAt(i)]);
8959                 if(v){
8960                      w = parseInt(v, 10);
8961                      if(w){ val += w; }
8962                 }
8963             }
8964             return val;
8965         },
8966
8967         /**
8968          * Creates a proxy element of this element
8969          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8970          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8971          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8972          * @return {Roo.Element} The new proxy element
8973          */
8974         createProxy : function(config, renderTo, matchBox){
8975             if(renderTo){
8976                 renderTo = Roo.getDom(renderTo);
8977             }else{
8978                 renderTo = document.body;
8979             }
8980             config = typeof config == "object" ?
8981                 config : {tag : "div", cls: config};
8982             var proxy = Roo.DomHelper.append(renderTo, config, true);
8983             if(matchBox){
8984                proxy.setBox(this.getBox());
8985             }
8986             return proxy;
8987         },
8988
8989         /**
8990          * Puts a mask over this element to disable user interaction. Requires core.css.
8991          * This method can only be applied to elements which accept child nodes.
8992          * @param {String} msg (optional) A message to display in the mask
8993          * @param {String} msgCls (optional) A css class to apply to the msg element
8994          * @return {Element} The mask  element
8995          */
8996         mask : function(msg, msgCls)
8997         {
8998             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
8999                 this.setStyle("position", "relative");
9000             }
9001             if(!this._mask){
9002                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9003             }
9004             this.addClass("x-masked");
9005             this._mask.setDisplayed(true);
9006             
9007             // we wander
9008             var z = 0;
9009             var dom = this.dom
9010             while (dom && dom.style) {
9011                 if (!isNaN(parseInt(dom.style.zIndex))) {
9012                     z = Math.max(z, parseInt(dom.style.zIndex));
9013                 }
9014                 dom = dom.parentNode;
9015             }
9016             // if we are masking the body - then it hides everything..
9017             if (this.dom == document.body) {
9018                 z = 1000000;
9019                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9020                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9021             }
9022            
9023             if(typeof msg == 'string'){
9024                 if(!this._maskMsg){
9025                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9026                 }
9027                 var mm = this._maskMsg;
9028                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9029                 if (mm.dom.firstChild) { // weird IE issue?
9030                     mm.dom.firstChild.innerHTML = msg;
9031                 }
9032                 mm.setDisplayed(true);
9033                 mm.center(this);
9034                 mm.setStyle('z-index', z + 102);
9035             }
9036             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9037                 this._mask.setHeight(this.getHeight());
9038             }
9039             this._mask.setStyle('z-index', z + 100);
9040             
9041             return this._mask;
9042         },
9043
9044         /**
9045          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9046          * it is cached for reuse.
9047          */
9048         unmask : function(removeEl){
9049             if(this._mask){
9050                 if(removeEl === true){
9051                     this._mask.remove();
9052                     delete this._mask;
9053                     if(this._maskMsg){
9054                         this._maskMsg.remove();
9055                         delete this._maskMsg;
9056                     }
9057                 }else{
9058                     this._mask.setDisplayed(false);
9059                     if(this._maskMsg){
9060                         this._maskMsg.setDisplayed(false);
9061                     }
9062                 }
9063             }
9064             this.removeClass("x-masked");
9065         },
9066
9067         /**
9068          * Returns true if this element is masked
9069          * @return {Boolean}
9070          */
9071         isMasked : function(){
9072             return this._mask && this._mask.isVisible();
9073         },
9074
9075         /**
9076          * Creates an iframe shim for this element to keep selects and other windowed objects from
9077          * showing through.
9078          * @return {Roo.Element} The new shim element
9079          */
9080         createShim : function(){
9081             var el = document.createElement('iframe');
9082             el.frameBorder = 'no';
9083             el.className = 'roo-shim';
9084             if(Roo.isIE && Roo.isSecure){
9085                 el.src = Roo.SSL_SECURE_URL;
9086             }
9087             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9088             shim.autoBoxAdjust = false;
9089             return shim;
9090         },
9091
9092         /**
9093          * Removes this element from the DOM and deletes it from the cache
9094          */
9095         remove : function(){
9096             if(this.dom.parentNode){
9097                 this.dom.parentNode.removeChild(this.dom);
9098             }
9099             delete El.cache[this.dom.id];
9100         },
9101
9102         /**
9103          * Sets up event handlers to add and remove a css class when the mouse is over this element
9104          * @param {String} className
9105          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9106          * mouseout events for children elements
9107          * @return {Roo.Element} this
9108          */
9109         addClassOnOver : function(className, preventFlicker){
9110             this.on("mouseover", function(){
9111                 Roo.fly(this, '_internal').addClass(className);
9112             }, this.dom);
9113             var removeFn = function(e){
9114                 if(preventFlicker !== true || !e.within(this, true)){
9115                     Roo.fly(this, '_internal').removeClass(className);
9116                 }
9117             };
9118             this.on("mouseout", removeFn, this.dom);
9119             return this;
9120         },
9121
9122         /**
9123          * Sets up event handlers to add and remove a css class when this element has the focus
9124          * @param {String} className
9125          * @return {Roo.Element} this
9126          */
9127         addClassOnFocus : function(className){
9128             this.on("focus", function(){
9129                 Roo.fly(this, '_internal').addClass(className);
9130             }, this.dom);
9131             this.on("blur", function(){
9132                 Roo.fly(this, '_internal').removeClass(className);
9133             }, this.dom);
9134             return this;
9135         },
9136         /**
9137          * 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)
9138          * @param {String} className
9139          * @return {Roo.Element} this
9140          */
9141         addClassOnClick : function(className){
9142             var dom = this.dom;
9143             this.on("mousedown", function(){
9144                 Roo.fly(dom, '_internal').addClass(className);
9145                 var d = Roo.get(document);
9146                 var fn = function(){
9147                     Roo.fly(dom, '_internal').removeClass(className);
9148                     d.removeListener("mouseup", fn);
9149                 };
9150                 d.on("mouseup", fn);
9151             });
9152             return this;
9153         },
9154
9155         /**
9156          * Stops the specified event from bubbling and optionally prevents the default action
9157          * @param {String} eventName
9158          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9159          * @return {Roo.Element} this
9160          */
9161         swallowEvent : function(eventName, preventDefault){
9162             var fn = function(e){
9163                 e.stopPropagation();
9164                 if(preventDefault){
9165                     e.preventDefault();
9166                 }
9167             };
9168             if(eventName instanceof Array){
9169                 for(var i = 0, len = eventName.length; i < len; i++){
9170                      this.on(eventName[i], fn);
9171                 }
9172                 return this;
9173             }
9174             this.on(eventName, fn);
9175             return this;
9176         },
9177
9178         /**
9179          * @private
9180          */
9181       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9182
9183         /**
9184          * Sizes this element to its parent element's dimensions performing
9185          * neccessary box adjustments.
9186          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9187          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9188          * @return {Roo.Element} this
9189          */
9190         fitToParent : function(monitorResize, targetParent) {
9191           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9192           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9193           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9194             return;
9195           }
9196           var p = Roo.get(targetParent || this.dom.parentNode);
9197           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9198           if (monitorResize === true) {
9199             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9200             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9201           }
9202           return this;
9203         },
9204
9205         /**
9206          * Gets the next sibling, skipping text nodes
9207          * @return {HTMLElement} The next sibling or null
9208          */
9209         getNextSibling : function(){
9210             var n = this.dom.nextSibling;
9211             while(n && n.nodeType != 1){
9212                 n = n.nextSibling;
9213             }
9214             return n;
9215         },
9216
9217         /**
9218          * Gets the previous sibling, skipping text nodes
9219          * @return {HTMLElement} The previous sibling or null
9220          */
9221         getPrevSibling : function(){
9222             var n = this.dom.previousSibling;
9223             while(n && n.nodeType != 1){
9224                 n = n.previousSibling;
9225             }
9226             return n;
9227         },
9228
9229
9230         /**
9231          * Appends the passed element(s) to this element
9232          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9233          * @return {Roo.Element} this
9234          */
9235         appendChild: function(el){
9236             el = Roo.get(el);
9237             el.appendTo(this);
9238             return this;
9239         },
9240
9241         /**
9242          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9243          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9244          * automatically generated with the specified attributes.
9245          * @param {HTMLElement} insertBefore (optional) a child element of this element
9246          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9247          * @return {Roo.Element} The new child element
9248          */
9249         createChild: function(config, insertBefore, returnDom){
9250             config = config || {tag:'div'};
9251             if(insertBefore){
9252                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9253             }
9254             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9255         },
9256
9257         /**
9258          * Appends this element to the passed element
9259          * @param {String/HTMLElement/Element} el The new parent element
9260          * @return {Roo.Element} this
9261          */
9262         appendTo: function(el){
9263             el = Roo.getDom(el);
9264             el.appendChild(this.dom);
9265             return this;
9266         },
9267
9268         /**
9269          * Inserts this element before the passed element in the DOM
9270          * @param {String/HTMLElement/Element} el The element to insert before
9271          * @return {Roo.Element} this
9272          */
9273         insertBefore: function(el){
9274             el = Roo.getDom(el);
9275             el.parentNode.insertBefore(this.dom, el);
9276             return this;
9277         },
9278
9279         /**
9280          * Inserts this element after the passed element in the DOM
9281          * @param {String/HTMLElement/Element} el The element to insert after
9282          * @return {Roo.Element} this
9283          */
9284         insertAfter: function(el){
9285             el = Roo.getDom(el);
9286             el.parentNode.insertBefore(this.dom, el.nextSibling);
9287             return this;
9288         },
9289
9290         /**
9291          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9292          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9293          * @return {Roo.Element} The new child
9294          */
9295         insertFirst: function(el, returnDom){
9296             el = el || {};
9297             if(typeof el == 'object' && !el.nodeType){ // dh config
9298                 return this.createChild(el, this.dom.firstChild, returnDom);
9299             }else{
9300                 el = Roo.getDom(el);
9301                 this.dom.insertBefore(el, this.dom.firstChild);
9302                 return !returnDom ? Roo.get(el) : el;
9303             }
9304         },
9305
9306         /**
9307          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9308          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9309          * @param {String} where (optional) 'before' or 'after' defaults to before
9310          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9311          * @return {Roo.Element} the inserted Element
9312          */
9313         insertSibling: function(el, where, returnDom){
9314             where = where ? where.toLowerCase() : 'before';
9315             el = el || {};
9316             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9317
9318             if(typeof el == 'object' && !el.nodeType){ // dh config
9319                 if(where == 'after' && !this.dom.nextSibling){
9320                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9321                 }else{
9322                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9323                 }
9324
9325             }else{
9326                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9327                             where == 'before' ? this.dom : this.dom.nextSibling);
9328                 if(!returnDom){
9329                     rt = Roo.get(rt);
9330                 }
9331             }
9332             return rt;
9333         },
9334
9335         /**
9336          * Creates and wraps this element with another element
9337          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9338          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9339          * @return {HTMLElement/Element} The newly created wrapper element
9340          */
9341         wrap: function(config, returnDom){
9342             if(!config){
9343                 config = {tag: "div"};
9344             }
9345             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9346             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9347             return newEl;
9348         },
9349
9350         /**
9351          * Replaces the passed element with this element
9352          * @param {String/HTMLElement/Element} el The element to replace
9353          * @return {Roo.Element} this
9354          */
9355         replace: function(el){
9356             el = Roo.get(el);
9357             this.insertBefore(el);
9358             el.remove();
9359             return this;
9360         },
9361
9362         /**
9363          * Inserts an html fragment into this element
9364          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9365          * @param {String} html The HTML fragment
9366          * @param {Boolean} returnEl True to return an Roo.Element
9367          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9368          */
9369         insertHtml : function(where, html, returnEl){
9370             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9371             return returnEl ? Roo.get(el) : el;
9372         },
9373
9374         /**
9375          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9376          * @param {Object} o The object with the attributes
9377          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9378          * @return {Roo.Element} this
9379          */
9380         set : function(o, useSet){
9381             var el = this.dom;
9382             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9383             for(var attr in o){
9384                 if(attr == "style" || typeof o[attr] == "function") continue;
9385                 if(attr=="cls"){
9386                     el.className = o["cls"];
9387                 }else{
9388                     if(useSet) el.setAttribute(attr, o[attr]);
9389                     else el[attr] = o[attr];
9390                 }
9391             }
9392             if(o.style){
9393                 Roo.DomHelper.applyStyles(el, o.style);
9394             }
9395             return this;
9396         },
9397
9398         /**
9399          * Convenience method for constructing a KeyMap
9400          * @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:
9401          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9402          * @param {Function} fn The function to call
9403          * @param {Object} scope (optional) The scope of the function
9404          * @return {Roo.KeyMap} The KeyMap created
9405          */
9406         addKeyListener : function(key, fn, scope){
9407             var config;
9408             if(typeof key != "object" || key instanceof Array){
9409                 config = {
9410                     key: key,
9411                     fn: fn,
9412                     scope: scope
9413                 };
9414             }else{
9415                 config = {
9416                     key : key.key,
9417                     shift : key.shift,
9418                     ctrl : key.ctrl,
9419                     alt : key.alt,
9420                     fn: fn,
9421                     scope: scope
9422                 };
9423             }
9424             return new Roo.KeyMap(this, config);
9425         },
9426
9427         /**
9428          * Creates a KeyMap for this element
9429          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9430          * @return {Roo.KeyMap} The KeyMap created
9431          */
9432         addKeyMap : function(config){
9433             return new Roo.KeyMap(this, config);
9434         },
9435
9436         /**
9437          * Returns true if this element is scrollable.
9438          * @return {Boolean}
9439          */
9440          isScrollable : function(){
9441             var dom = this.dom;
9442             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9443         },
9444
9445         /**
9446          * 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().
9447          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9448          * @param {Number} value The new scroll value
9449          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9450          * @return {Element} this
9451          */
9452
9453         scrollTo : function(side, value, animate){
9454             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9455             if(!animate || !A){
9456                 this.dom[prop] = value;
9457             }else{
9458                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9459                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9460             }
9461             return this;
9462         },
9463
9464         /**
9465          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9466          * within this element's scrollable range.
9467          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9468          * @param {Number} distance How far to scroll the element in pixels
9469          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9470          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9471          * was scrolled as far as it could go.
9472          */
9473          scroll : function(direction, distance, animate){
9474              if(!this.isScrollable()){
9475                  return;
9476              }
9477              var el = this.dom;
9478              var l = el.scrollLeft, t = el.scrollTop;
9479              var w = el.scrollWidth, h = el.scrollHeight;
9480              var cw = el.clientWidth, ch = el.clientHeight;
9481              direction = direction.toLowerCase();
9482              var scrolled = false;
9483              var a = this.preanim(arguments, 2);
9484              switch(direction){
9485                  case "l":
9486                  case "left":
9487                      if(w - l > cw){
9488                          var v = Math.min(l + distance, w-cw);
9489                          this.scrollTo("left", v, a);
9490                          scrolled = true;
9491                      }
9492                      break;
9493                 case "r":
9494                 case "right":
9495                      if(l > 0){
9496                          var v = Math.max(l - distance, 0);
9497                          this.scrollTo("left", v, a);
9498                          scrolled = true;
9499                      }
9500                      break;
9501                 case "t":
9502                 case "top":
9503                 case "up":
9504                      if(t > 0){
9505                          var v = Math.max(t - distance, 0);
9506                          this.scrollTo("top", v, a);
9507                          scrolled = true;
9508                      }
9509                      break;
9510                 case "b":
9511                 case "bottom":
9512                 case "down":
9513                      if(h - t > ch){
9514                          var v = Math.min(t + distance, h-ch);
9515                          this.scrollTo("top", v, a);
9516                          scrolled = true;
9517                      }
9518                      break;
9519              }
9520              return scrolled;
9521         },
9522
9523         /**
9524          * Translates the passed page coordinates into left/top css values for this element
9525          * @param {Number/Array} x The page x or an array containing [x, y]
9526          * @param {Number} y The page y
9527          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9528          */
9529         translatePoints : function(x, y){
9530             if(typeof x == 'object' || x instanceof Array){
9531                 y = x[1]; x = x[0];
9532             }
9533             var p = this.getStyle('position');
9534             var o = this.getXY();
9535
9536             var l = parseInt(this.getStyle('left'), 10);
9537             var t = parseInt(this.getStyle('top'), 10);
9538
9539             if(isNaN(l)){
9540                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9541             }
9542             if(isNaN(t)){
9543                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9544             }
9545
9546             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9547         },
9548
9549         /**
9550          * Returns the current scroll position of the element.
9551          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9552          */
9553         getScroll : function(){
9554             var d = this.dom, doc = document;
9555             if(d == doc || d == doc.body){
9556                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9557                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9558                 return {left: l, top: t};
9559             }else{
9560                 return {left: d.scrollLeft, top: d.scrollTop};
9561             }
9562         },
9563
9564         /**
9565          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9566          * are convert to standard 6 digit hex color.
9567          * @param {String} attr The css attribute
9568          * @param {String} defaultValue The default value to use when a valid color isn't found
9569          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9570          * YUI color anims.
9571          */
9572         getColor : function(attr, defaultValue, prefix){
9573             var v = this.getStyle(attr);
9574             if(!v || v == "transparent" || v == "inherit") {
9575                 return defaultValue;
9576             }
9577             var color = typeof prefix == "undefined" ? "#" : prefix;
9578             if(v.substr(0, 4) == "rgb("){
9579                 var rvs = v.slice(4, v.length -1).split(",");
9580                 for(var i = 0; i < 3; i++){
9581                     var h = parseInt(rvs[i]).toString(16);
9582                     if(h < 16){
9583                         h = "0" + h;
9584                     }
9585                     color += h;
9586                 }
9587             } else {
9588                 if(v.substr(0, 1) == "#"){
9589                     if(v.length == 4) {
9590                         for(var i = 1; i < 4; i++){
9591                             var c = v.charAt(i);
9592                             color +=  c + c;
9593                         }
9594                     }else if(v.length == 7){
9595                         color += v.substr(1);
9596                     }
9597                 }
9598             }
9599             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9600         },
9601
9602         /**
9603          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9604          * gradient background, rounded corners and a 4-way shadow.
9605          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9606          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9607          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9608          * @return {Roo.Element} this
9609          */
9610         boxWrap : function(cls){
9611             cls = cls || 'x-box';
9612             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9613             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9614             return el;
9615         },
9616
9617         /**
9618          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9619          * @param {String} namespace The namespace in which to look for the attribute
9620          * @param {String} name The attribute name
9621          * @return {String} The attribute value
9622          */
9623         getAttributeNS : Roo.isIE ? function(ns, name){
9624             var d = this.dom;
9625             var type = typeof d[ns+":"+name];
9626             if(type != 'undefined' && type != 'unknown'){
9627                 return d[ns+":"+name];
9628             }
9629             return d[name];
9630         } : function(ns, name){
9631             var d = this.dom;
9632             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9633         },
9634         
9635         
9636         /**
9637          * Sets or Returns the value the dom attribute value
9638          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9639          * @param {String} value (optional) The value to set the attribute to
9640          * @return {String} The attribute value
9641          */
9642         attr : function(name){
9643             if (arguments.length > 1) {
9644                 this.dom.setAttribute(name, arguments[1]);
9645                 return arguments[1];
9646             }
9647             if (typeof(name) == 'object') {
9648                 for(var i in name) {
9649                     this.attr(i, name[i]);
9650                 }
9651                 return name;
9652             }
9653             
9654             
9655             if (!this.dom.hasAttribute(name)) {
9656                 return undefined;
9657             }
9658             return this.dom.getAttribute(name);
9659         }
9660         
9661         
9662         
9663     };
9664
9665     var ep = El.prototype;
9666
9667     /**
9668      * Appends an event handler (Shorthand for addListener)
9669      * @param {String}   eventName     The type of event to append
9670      * @param {Function} fn        The method the event invokes
9671      * @param {Object} scope       (optional) The scope (this object) of the fn
9672      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9673      * @method
9674      */
9675     ep.on = ep.addListener;
9676         // backwards compat
9677     ep.mon = ep.addListener;
9678
9679     /**
9680      * Removes an event handler from this element (shorthand for removeListener)
9681      * @param {String} eventName the type of event to remove
9682      * @param {Function} fn the method the event invokes
9683      * @return {Roo.Element} this
9684      * @method
9685      */
9686     ep.un = ep.removeListener;
9687
9688     /**
9689      * true to automatically adjust width and height settings for box-model issues (default to true)
9690      */
9691     ep.autoBoxAdjust = true;
9692
9693     // private
9694     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9695
9696     // private
9697     El.addUnits = function(v, defaultUnit){
9698         if(v === "" || v == "auto"){
9699             return v;
9700         }
9701         if(v === undefined){
9702             return '';
9703         }
9704         if(typeof v == "number" || !El.unitPattern.test(v)){
9705             return v + (defaultUnit || 'px');
9706         }
9707         return v;
9708     };
9709
9710     // special markup used throughout Roo when box wrapping elements
9711     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>';
9712     /**
9713      * Visibility mode constant - Use visibility to hide element
9714      * @static
9715      * @type Number
9716      */
9717     El.VISIBILITY = 1;
9718     /**
9719      * Visibility mode constant - Use display to hide element
9720      * @static
9721      * @type Number
9722      */
9723     El.DISPLAY = 2;
9724
9725     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9726     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9727     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9728
9729
9730
9731     /**
9732      * @private
9733      */
9734     El.cache = {};
9735
9736     var docEl;
9737
9738     /**
9739      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9740      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9741      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9742      * @return {Element} The Element object
9743      * @static
9744      */
9745     El.get = function(el){
9746         var ex, elm, id;
9747         if(!el){ return null; }
9748         if(typeof el == "string"){ // element id
9749             if(!(elm = document.getElementById(el))){
9750                 return null;
9751             }
9752             if(ex = El.cache[el]){
9753                 ex.dom = elm;
9754             }else{
9755                 ex = El.cache[el] = new El(elm);
9756             }
9757             return ex;
9758         }else if(el.tagName){ // dom element
9759             if(!(id = el.id)){
9760                 id = Roo.id(el);
9761             }
9762             if(ex = El.cache[id]){
9763                 ex.dom = el;
9764             }else{
9765                 ex = El.cache[id] = new El(el);
9766             }
9767             return ex;
9768         }else if(el instanceof El){
9769             if(el != docEl){
9770                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9771                                                               // catch case where it hasn't been appended
9772                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9773             }
9774             return el;
9775         }else if(el.isComposite){
9776             return el;
9777         }else if(el instanceof Array){
9778             return El.select(el);
9779         }else if(el == document){
9780             // create a bogus element object representing the document object
9781             if(!docEl){
9782                 var f = function(){};
9783                 f.prototype = El.prototype;
9784                 docEl = new f();
9785                 docEl.dom = document;
9786             }
9787             return docEl;
9788         }
9789         return null;
9790     };
9791
9792     // private
9793     El.uncache = function(el){
9794         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9795             if(a[i]){
9796                 delete El.cache[a[i].id || a[i]];
9797             }
9798         }
9799     };
9800
9801     // private
9802     // Garbage collection - uncache elements/purge listeners on orphaned elements
9803     // so we don't hold a reference and cause the browser to retain them
9804     El.garbageCollect = function(){
9805         if(!Roo.enableGarbageCollector){
9806             clearInterval(El.collectorThread);
9807             return;
9808         }
9809         for(var eid in El.cache){
9810             var el = El.cache[eid], d = el.dom;
9811             // -------------------------------------------------------
9812             // Determining what is garbage:
9813             // -------------------------------------------------------
9814             // !d
9815             // dom node is null, definitely garbage
9816             // -------------------------------------------------------
9817             // !d.parentNode
9818             // no parentNode == direct orphan, definitely garbage
9819             // -------------------------------------------------------
9820             // !d.offsetParent && !document.getElementById(eid)
9821             // display none elements have no offsetParent so we will
9822             // also try to look it up by it's id. However, check
9823             // offsetParent first so we don't do unneeded lookups.
9824             // This enables collection of elements that are not orphans
9825             // directly, but somewhere up the line they have an orphan
9826             // parent.
9827             // -------------------------------------------------------
9828             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9829                 delete El.cache[eid];
9830                 if(d && Roo.enableListenerCollection){
9831                     E.purgeElement(d);
9832                 }
9833             }
9834         }
9835     }
9836     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9837
9838
9839     // dom is optional
9840     El.Flyweight = function(dom){
9841         this.dom = dom;
9842     };
9843     El.Flyweight.prototype = El.prototype;
9844
9845     El._flyweights = {};
9846     /**
9847      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9848      * the dom node can be overwritten by other code.
9849      * @param {String/HTMLElement} el The dom node or id
9850      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9851      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9852      * @static
9853      * @return {Element} The shared Element object
9854      */
9855     El.fly = function(el, named){
9856         named = named || '_global';
9857         el = Roo.getDom(el);
9858         if(!el){
9859             return null;
9860         }
9861         if(!El._flyweights[named]){
9862             El._flyweights[named] = new El.Flyweight();
9863         }
9864         El._flyweights[named].dom = el;
9865         return El._flyweights[named];
9866     };
9867
9868     /**
9869      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9870      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9871      * Shorthand of {@link Roo.Element#get}
9872      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9873      * @return {Element} The Element object
9874      * @member Roo
9875      * @method get
9876      */
9877     Roo.get = El.get;
9878     /**
9879      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9880      * the dom node can be overwritten by other code.
9881      * Shorthand of {@link Roo.Element#fly}
9882      * @param {String/HTMLElement} el The dom node or id
9883      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9884      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9885      * @static
9886      * @return {Element} The shared Element object
9887      * @member Roo
9888      * @method fly
9889      */
9890     Roo.fly = El.fly;
9891
9892     // speedy lookup for elements never to box adjust
9893     var noBoxAdjust = Roo.isStrict ? {
9894         select:1
9895     } : {
9896         input:1, select:1, textarea:1
9897     };
9898     if(Roo.isIE || Roo.isGecko){
9899         noBoxAdjust['button'] = 1;
9900     }
9901
9902
9903     Roo.EventManager.on(window, 'unload', function(){
9904         delete El.cache;
9905         delete El._flyweights;
9906     });
9907 })();
9908
9909
9910
9911
9912 if(Roo.DomQuery){
9913     Roo.Element.selectorFunction = Roo.DomQuery.select;
9914 }
9915
9916 Roo.Element.select = function(selector, unique, root){
9917     var els;
9918     if(typeof selector == "string"){
9919         els = Roo.Element.selectorFunction(selector, root);
9920     }else if(selector.length !== undefined){
9921         els = selector;
9922     }else{
9923         throw "Invalid selector";
9924     }
9925     if(unique === true){
9926         return new Roo.CompositeElement(els);
9927     }else{
9928         return new Roo.CompositeElementLite(els);
9929     }
9930 };
9931 /**
9932  * Selects elements based on the passed CSS selector to enable working on them as 1.
9933  * @param {String/Array} selector The CSS selector or an array of elements
9934  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9935  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9936  * @return {CompositeElementLite/CompositeElement}
9937  * @member Roo
9938  * @method select
9939  */
9940 Roo.select = Roo.Element.select;
9941
9942
9943
9944
9945
9946
9947
9948
9949
9950
9951
9952
9953
9954
9955 /*
9956  * Based on:
9957  * Ext JS Library 1.1.1
9958  * Copyright(c) 2006-2007, Ext JS, LLC.
9959  *
9960  * Originally Released Under LGPL - original licence link has changed is not relivant.
9961  *
9962  * Fork - LGPL
9963  * <script type="text/javascript">
9964  */
9965
9966
9967
9968 //Notifies Element that fx methods are available
9969 Roo.enableFx = true;
9970
9971 /**
9972  * @class Roo.Fx
9973  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9974  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9975  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9976  * Element effects to work.</p><br/>
9977  *
9978  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9979  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9980  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9981  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9982  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9983  * expected results and should be done with care.</p><br/>
9984  *
9985  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9986  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9987 <pre>
9988 Value  Description
9989 -----  -----------------------------
9990 tl     The top left corner
9991 t      The center of the top edge
9992 tr     The top right corner
9993 l      The center of the left edge
9994 r      The center of the right edge
9995 bl     The bottom left corner
9996 b      The center of the bottom edge
9997 br     The bottom right corner
9998 </pre>
9999  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10000  * below are common options that can be passed to any Fx method.</b>
10001  * @cfg {Function} callback A function called when the effect is finished
10002  * @cfg {Object} scope The scope of the effect function
10003  * @cfg {String} easing A valid Easing value for the effect
10004  * @cfg {String} afterCls A css class to apply after the effect
10005  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10006  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10007  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10008  * effects that end with the element being visually hidden, ignored otherwise)
10009  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10010  * a function which returns such a specification that will be applied to the Element after the effect finishes
10011  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10012  * @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
10013  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10014  */
10015 Roo.Fx = {
10016         /**
10017          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10018          * origin for the slide effect.  This function automatically handles wrapping the element with
10019          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10020          * Usage:
10021          *<pre><code>
10022 // default: slide the element in from the top
10023 el.slideIn();
10024
10025 // custom: slide the element in from the right with a 2-second duration
10026 el.slideIn('r', { duration: 2 });
10027
10028 // common config options shown with default values
10029 el.slideIn('t', {
10030     easing: 'easeOut',
10031     duration: .5
10032 });
10033 </code></pre>
10034          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10035          * @param {Object} options (optional) Object literal with any of the Fx config options
10036          * @return {Roo.Element} The Element
10037          */
10038     slideIn : function(anchor, o){
10039         var el = this.getFxEl();
10040         o = o || {};
10041
10042         el.queueFx(o, function(){
10043
10044             anchor = anchor || "t";
10045
10046             // fix display to visibility
10047             this.fixDisplay();
10048
10049             // restore values after effect
10050             var r = this.getFxRestore();
10051             var b = this.getBox();
10052             // fixed size for slide
10053             this.setSize(b);
10054
10055             // wrap if needed
10056             var wrap = this.fxWrap(r.pos, o, "hidden");
10057
10058             var st = this.dom.style;
10059             st.visibility = "visible";
10060             st.position = "absolute";
10061
10062             // clear out temp styles after slide and unwrap
10063             var after = function(){
10064                 el.fxUnwrap(wrap, r.pos, o);
10065                 st.width = r.width;
10066                 st.height = r.height;
10067                 el.afterFx(o);
10068             };
10069             // time to calc the positions
10070             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10071
10072             switch(anchor.toLowerCase()){
10073                 case "t":
10074                     wrap.setSize(b.width, 0);
10075                     st.left = st.bottom = "0";
10076                     a = {height: bh};
10077                 break;
10078                 case "l":
10079                     wrap.setSize(0, b.height);
10080                     st.right = st.top = "0";
10081                     a = {width: bw};
10082                 break;
10083                 case "r":
10084                     wrap.setSize(0, b.height);
10085                     wrap.setX(b.right);
10086                     st.left = st.top = "0";
10087                     a = {width: bw, points: pt};
10088                 break;
10089                 case "b":
10090                     wrap.setSize(b.width, 0);
10091                     wrap.setY(b.bottom);
10092                     st.left = st.top = "0";
10093                     a = {height: bh, points: pt};
10094                 break;
10095                 case "tl":
10096                     wrap.setSize(0, 0);
10097                     st.right = st.bottom = "0";
10098                     a = {width: bw, height: bh};
10099                 break;
10100                 case "bl":
10101                     wrap.setSize(0, 0);
10102                     wrap.setY(b.y+b.height);
10103                     st.right = st.top = "0";
10104                     a = {width: bw, height: bh, points: pt};
10105                 break;
10106                 case "br":
10107                     wrap.setSize(0, 0);
10108                     wrap.setXY([b.right, b.bottom]);
10109                     st.left = st.top = "0";
10110                     a = {width: bw, height: bh, points: pt};
10111                 break;
10112                 case "tr":
10113                     wrap.setSize(0, 0);
10114                     wrap.setX(b.x+b.width);
10115                     st.left = st.bottom = "0";
10116                     a = {width: bw, height: bh, points: pt};
10117                 break;
10118             }
10119             this.dom.style.visibility = "visible";
10120             wrap.show();
10121
10122             arguments.callee.anim = wrap.fxanim(a,
10123                 o,
10124                 'motion',
10125                 .5,
10126                 'easeOut', after);
10127         });
10128         return this;
10129     },
10130     
10131         /**
10132          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10133          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10134          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10135          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10136          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10137          * Usage:
10138          *<pre><code>
10139 // default: slide the element out to the top
10140 el.slideOut();
10141
10142 // custom: slide the element out to the right with a 2-second duration
10143 el.slideOut('r', { duration: 2 });
10144
10145 // common config options shown with default values
10146 el.slideOut('t', {
10147     easing: 'easeOut',
10148     duration: .5,
10149     remove: false,
10150     useDisplay: false
10151 });
10152 </code></pre>
10153          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10154          * @param {Object} options (optional) Object literal with any of the Fx config options
10155          * @return {Roo.Element} The Element
10156          */
10157     slideOut : function(anchor, o){
10158         var el = this.getFxEl();
10159         o = o || {};
10160
10161         el.queueFx(o, function(){
10162
10163             anchor = anchor || "t";
10164
10165             // restore values after effect
10166             var r = this.getFxRestore();
10167             
10168             var b = this.getBox();
10169             // fixed size for slide
10170             this.setSize(b);
10171
10172             // wrap if needed
10173             var wrap = this.fxWrap(r.pos, o, "visible");
10174
10175             var st = this.dom.style;
10176             st.visibility = "visible";
10177             st.position = "absolute";
10178
10179             wrap.setSize(b);
10180
10181             var after = function(){
10182                 if(o.useDisplay){
10183                     el.setDisplayed(false);
10184                 }else{
10185                     el.hide();
10186                 }
10187
10188                 el.fxUnwrap(wrap, r.pos, o);
10189
10190                 st.width = r.width;
10191                 st.height = r.height;
10192
10193                 el.afterFx(o);
10194             };
10195
10196             var a, zero = {to: 0};
10197             switch(anchor.toLowerCase()){
10198                 case "t":
10199                     st.left = st.bottom = "0";
10200                     a = {height: zero};
10201                 break;
10202                 case "l":
10203                     st.right = st.top = "0";
10204                     a = {width: zero};
10205                 break;
10206                 case "r":
10207                     st.left = st.top = "0";
10208                     a = {width: zero, points: {to:[b.right, b.y]}};
10209                 break;
10210                 case "b":
10211                     st.left = st.top = "0";
10212                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10213                 break;
10214                 case "tl":
10215                     st.right = st.bottom = "0";
10216                     a = {width: zero, height: zero};
10217                 break;
10218                 case "bl":
10219                     st.right = st.top = "0";
10220                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10221                 break;
10222                 case "br":
10223                     st.left = st.top = "0";
10224                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10225                 break;
10226                 case "tr":
10227                     st.left = st.bottom = "0";
10228                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10229                 break;
10230             }
10231
10232             arguments.callee.anim = wrap.fxanim(a,
10233                 o,
10234                 'motion',
10235                 .5,
10236                 "easeOut", after);
10237         });
10238         return this;
10239     },
10240
10241         /**
10242          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10243          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10244          * The element must be removed from the DOM using the 'remove' config option if desired.
10245          * Usage:
10246          *<pre><code>
10247 // default
10248 el.puff();
10249
10250 // common config options shown with default values
10251 el.puff({
10252     easing: 'easeOut',
10253     duration: .5,
10254     remove: false,
10255     useDisplay: false
10256 });
10257 </code></pre>
10258          * @param {Object} options (optional) Object literal with any of the Fx config options
10259          * @return {Roo.Element} The Element
10260          */
10261     puff : function(o){
10262         var el = this.getFxEl();
10263         o = o || {};
10264
10265         el.queueFx(o, function(){
10266             this.clearOpacity();
10267             this.show();
10268
10269             // restore values after effect
10270             var r = this.getFxRestore();
10271             var st = this.dom.style;
10272
10273             var after = function(){
10274                 if(o.useDisplay){
10275                     el.setDisplayed(false);
10276                 }else{
10277                     el.hide();
10278                 }
10279
10280                 el.clearOpacity();
10281
10282                 el.setPositioning(r.pos);
10283                 st.width = r.width;
10284                 st.height = r.height;
10285                 st.fontSize = '';
10286                 el.afterFx(o);
10287             };
10288
10289             var width = this.getWidth();
10290             var height = this.getHeight();
10291
10292             arguments.callee.anim = this.fxanim({
10293                     width : {to: this.adjustWidth(width * 2)},
10294                     height : {to: this.adjustHeight(height * 2)},
10295                     points : {by: [-(width * .5), -(height * .5)]},
10296                     opacity : {to: 0},
10297                     fontSize: {to:200, unit: "%"}
10298                 },
10299                 o,
10300                 'motion',
10301                 .5,
10302                 "easeOut", after);
10303         });
10304         return this;
10305     },
10306
10307         /**
10308          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10309          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10310          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10311          * Usage:
10312          *<pre><code>
10313 // default
10314 el.switchOff();
10315
10316 // all config options shown with default values
10317 el.switchOff({
10318     easing: 'easeIn',
10319     duration: .3,
10320     remove: false,
10321     useDisplay: false
10322 });
10323 </code></pre>
10324          * @param {Object} options (optional) Object literal with any of the Fx config options
10325          * @return {Roo.Element} The Element
10326          */
10327     switchOff : function(o){
10328         var el = this.getFxEl();
10329         o = o || {};
10330
10331         el.queueFx(o, function(){
10332             this.clearOpacity();
10333             this.clip();
10334
10335             // restore values after effect
10336             var r = this.getFxRestore();
10337             var st = this.dom.style;
10338
10339             var after = function(){
10340                 if(o.useDisplay){
10341                     el.setDisplayed(false);
10342                 }else{
10343                     el.hide();
10344                 }
10345
10346                 el.clearOpacity();
10347                 el.setPositioning(r.pos);
10348                 st.width = r.width;
10349                 st.height = r.height;
10350
10351                 el.afterFx(o);
10352             };
10353
10354             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10355                 this.clearOpacity();
10356                 (function(){
10357                     this.fxanim({
10358                         height:{to:1},
10359                         points:{by:[0, this.getHeight() * .5]}
10360                     }, o, 'motion', 0.3, 'easeIn', after);
10361                 }).defer(100, this);
10362             });
10363         });
10364         return this;
10365     },
10366
10367     /**
10368      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10369      * changed using the "attr" config option) and then fading back to the original color. If no original
10370      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10371      * Usage:
10372 <pre><code>
10373 // default: highlight background to yellow
10374 el.highlight();
10375
10376 // custom: highlight foreground text to blue for 2 seconds
10377 el.highlight("0000ff", { attr: 'color', duration: 2 });
10378
10379 // common config options shown with default values
10380 el.highlight("ffff9c", {
10381     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10382     endColor: (current color) or "ffffff",
10383     easing: 'easeIn',
10384     duration: 1
10385 });
10386 </code></pre>
10387      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10388      * @param {Object} options (optional) Object literal with any of the Fx config options
10389      * @return {Roo.Element} The Element
10390      */ 
10391     highlight : function(color, o){
10392         var el = this.getFxEl();
10393         o = o || {};
10394
10395         el.queueFx(o, function(){
10396             color = color || "ffff9c";
10397             attr = o.attr || "backgroundColor";
10398
10399             this.clearOpacity();
10400             this.show();
10401
10402             var origColor = this.getColor(attr);
10403             var restoreColor = this.dom.style[attr];
10404             endColor = (o.endColor || origColor) || "ffffff";
10405
10406             var after = function(){
10407                 el.dom.style[attr] = restoreColor;
10408                 el.afterFx(o);
10409             };
10410
10411             var a = {};
10412             a[attr] = {from: color, to: endColor};
10413             arguments.callee.anim = this.fxanim(a,
10414                 o,
10415                 'color',
10416                 1,
10417                 'easeIn', after);
10418         });
10419         return this;
10420     },
10421
10422    /**
10423     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10424     * Usage:
10425 <pre><code>
10426 // default: a single light blue ripple
10427 el.frame();
10428
10429 // custom: 3 red ripples lasting 3 seconds total
10430 el.frame("ff0000", 3, { duration: 3 });
10431
10432 // common config options shown with default values
10433 el.frame("C3DAF9", 1, {
10434     duration: 1 //duration of entire animation (not each individual ripple)
10435     // Note: Easing is not configurable and will be ignored if included
10436 });
10437 </code></pre>
10438     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10439     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10440     * @param {Object} options (optional) Object literal with any of the Fx config options
10441     * @return {Roo.Element} The Element
10442     */
10443     frame : function(color, count, o){
10444         var el = this.getFxEl();
10445         o = o || {};
10446
10447         el.queueFx(o, function(){
10448             color = color || "#C3DAF9";
10449             if(color.length == 6){
10450                 color = "#" + color;
10451             }
10452             count = count || 1;
10453             duration = o.duration || 1;
10454             this.show();
10455
10456             var b = this.getBox();
10457             var animFn = function(){
10458                 var proxy = this.createProxy({
10459
10460                      style:{
10461                         visbility:"hidden",
10462                         position:"absolute",
10463                         "z-index":"35000", // yee haw
10464                         border:"0px solid " + color
10465                      }
10466                   });
10467                 var scale = Roo.isBorderBox ? 2 : 1;
10468                 proxy.animate({
10469                     top:{from:b.y, to:b.y - 20},
10470                     left:{from:b.x, to:b.x - 20},
10471                     borderWidth:{from:0, to:10},
10472                     opacity:{from:1, to:0},
10473                     height:{from:b.height, to:(b.height + (20*scale))},
10474                     width:{from:b.width, to:(b.width + (20*scale))}
10475                 }, duration, function(){
10476                     proxy.remove();
10477                 });
10478                 if(--count > 0){
10479                      animFn.defer((duration/2)*1000, this);
10480                 }else{
10481                     el.afterFx(o);
10482                 }
10483             };
10484             animFn.call(this);
10485         });
10486         return this;
10487     },
10488
10489    /**
10490     * Creates a pause before any subsequent queued effects begin.  If there are
10491     * no effects queued after the pause it will have no effect.
10492     * Usage:
10493 <pre><code>
10494 el.pause(1);
10495 </code></pre>
10496     * @param {Number} seconds The length of time to pause (in seconds)
10497     * @return {Roo.Element} The Element
10498     */
10499     pause : function(seconds){
10500         var el = this.getFxEl();
10501         var o = {};
10502
10503         el.queueFx(o, function(){
10504             setTimeout(function(){
10505                 el.afterFx(o);
10506             }, seconds * 1000);
10507         });
10508         return this;
10509     },
10510
10511    /**
10512     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10513     * using the "endOpacity" config option.
10514     * Usage:
10515 <pre><code>
10516 // default: fade in from opacity 0 to 100%
10517 el.fadeIn();
10518
10519 // custom: fade in from opacity 0 to 75% over 2 seconds
10520 el.fadeIn({ endOpacity: .75, duration: 2});
10521
10522 // common config options shown with default values
10523 el.fadeIn({
10524     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10525     easing: 'easeOut',
10526     duration: .5
10527 });
10528 </code></pre>
10529     * @param {Object} options (optional) Object literal with any of the Fx config options
10530     * @return {Roo.Element} The Element
10531     */
10532     fadeIn : function(o){
10533         var el = this.getFxEl();
10534         o = o || {};
10535         el.queueFx(o, function(){
10536             this.setOpacity(0);
10537             this.fixDisplay();
10538             this.dom.style.visibility = 'visible';
10539             var to = o.endOpacity || 1;
10540             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10541                 o, null, .5, "easeOut", function(){
10542                 if(to == 1){
10543                     this.clearOpacity();
10544                 }
10545                 el.afterFx(o);
10546             });
10547         });
10548         return this;
10549     },
10550
10551    /**
10552     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10553     * using the "endOpacity" config option.
10554     * Usage:
10555 <pre><code>
10556 // default: fade out from the element's current opacity to 0
10557 el.fadeOut();
10558
10559 // custom: fade out from the element's current opacity to 25% over 2 seconds
10560 el.fadeOut({ endOpacity: .25, duration: 2});
10561
10562 // common config options shown with default values
10563 el.fadeOut({
10564     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10565     easing: 'easeOut',
10566     duration: .5
10567     remove: false,
10568     useDisplay: false
10569 });
10570 </code></pre>
10571     * @param {Object} options (optional) Object literal with any of the Fx config options
10572     * @return {Roo.Element} The Element
10573     */
10574     fadeOut : function(o){
10575         var el = this.getFxEl();
10576         o = o || {};
10577         el.queueFx(o, function(){
10578             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10579                 o, null, .5, "easeOut", function(){
10580                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10581                      this.dom.style.display = "none";
10582                 }else{
10583                      this.dom.style.visibility = "hidden";
10584                 }
10585                 this.clearOpacity();
10586                 el.afterFx(o);
10587             });
10588         });
10589         return this;
10590     },
10591
10592    /**
10593     * Animates the transition of an element's dimensions from a starting height/width
10594     * to an ending height/width.
10595     * Usage:
10596 <pre><code>
10597 // change height and width to 100x100 pixels
10598 el.scale(100, 100);
10599
10600 // common config options shown with default values.  The height and width will default to
10601 // the element's existing values if passed as null.
10602 el.scale(
10603     [element's width],
10604     [element's height], {
10605     easing: 'easeOut',
10606     duration: .35
10607 });
10608 </code></pre>
10609     * @param {Number} width  The new width (pass undefined to keep the original width)
10610     * @param {Number} height  The new height (pass undefined to keep the original height)
10611     * @param {Object} options (optional) Object literal with any of the Fx config options
10612     * @return {Roo.Element} The Element
10613     */
10614     scale : function(w, h, o){
10615         this.shift(Roo.apply({}, o, {
10616             width: w,
10617             height: h
10618         }));
10619         return this;
10620     },
10621
10622    /**
10623     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10624     * Any of these properties not specified in the config object will not be changed.  This effect 
10625     * requires that at least one new dimension, position or opacity setting must be passed in on
10626     * the config object in order for the function to have any effect.
10627     * Usage:
10628 <pre><code>
10629 // slide the element horizontally to x position 200 while changing the height and opacity
10630 el.shift({ x: 200, height: 50, opacity: .8 });
10631
10632 // common config options shown with default values.
10633 el.shift({
10634     width: [element's width],
10635     height: [element's height],
10636     x: [element's x position],
10637     y: [element's y position],
10638     opacity: [element's opacity],
10639     easing: 'easeOut',
10640     duration: .35
10641 });
10642 </code></pre>
10643     * @param {Object} options  Object literal with any of the Fx config options
10644     * @return {Roo.Element} The Element
10645     */
10646     shift : function(o){
10647         var el = this.getFxEl();
10648         o = o || {};
10649         el.queueFx(o, function(){
10650             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10651             if(w !== undefined){
10652                 a.width = {to: this.adjustWidth(w)};
10653             }
10654             if(h !== undefined){
10655                 a.height = {to: this.adjustHeight(h)};
10656             }
10657             if(x !== undefined || y !== undefined){
10658                 a.points = {to: [
10659                     x !== undefined ? x : this.getX(),
10660                     y !== undefined ? y : this.getY()
10661                 ]};
10662             }
10663             if(op !== undefined){
10664                 a.opacity = {to: op};
10665             }
10666             if(o.xy !== undefined){
10667                 a.points = {to: o.xy};
10668             }
10669             arguments.callee.anim = this.fxanim(a,
10670                 o, 'motion', .35, "easeOut", function(){
10671                 el.afterFx(o);
10672             });
10673         });
10674         return this;
10675     },
10676
10677         /**
10678          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10679          * ending point of the effect.
10680          * Usage:
10681          *<pre><code>
10682 // default: slide the element downward while fading out
10683 el.ghost();
10684
10685 // custom: slide the element out to the right with a 2-second duration
10686 el.ghost('r', { duration: 2 });
10687
10688 // common config options shown with default values
10689 el.ghost('b', {
10690     easing: 'easeOut',
10691     duration: .5
10692     remove: false,
10693     useDisplay: false
10694 });
10695 </code></pre>
10696          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10697          * @param {Object} options (optional) Object literal with any of the Fx config options
10698          * @return {Roo.Element} The Element
10699          */
10700     ghost : function(anchor, o){
10701         var el = this.getFxEl();
10702         o = o || {};
10703
10704         el.queueFx(o, function(){
10705             anchor = anchor || "b";
10706
10707             // restore values after effect
10708             var r = this.getFxRestore();
10709             var w = this.getWidth(),
10710                 h = this.getHeight();
10711
10712             var st = this.dom.style;
10713
10714             var after = function(){
10715                 if(o.useDisplay){
10716                     el.setDisplayed(false);
10717                 }else{
10718                     el.hide();
10719                 }
10720
10721                 el.clearOpacity();
10722                 el.setPositioning(r.pos);
10723                 st.width = r.width;
10724                 st.height = r.height;
10725
10726                 el.afterFx(o);
10727             };
10728
10729             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10730             switch(anchor.toLowerCase()){
10731                 case "t":
10732                     pt.by = [0, -h];
10733                 break;
10734                 case "l":
10735                     pt.by = [-w, 0];
10736                 break;
10737                 case "r":
10738                     pt.by = [w, 0];
10739                 break;
10740                 case "b":
10741                     pt.by = [0, h];
10742                 break;
10743                 case "tl":
10744                     pt.by = [-w, -h];
10745                 break;
10746                 case "bl":
10747                     pt.by = [-w, h];
10748                 break;
10749                 case "br":
10750                     pt.by = [w, h];
10751                 break;
10752                 case "tr":
10753                     pt.by = [w, -h];
10754                 break;
10755             }
10756
10757             arguments.callee.anim = this.fxanim(a,
10758                 o,
10759                 'motion',
10760                 .5,
10761                 "easeOut", after);
10762         });
10763         return this;
10764     },
10765
10766         /**
10767          * Ensures that all effects queued after syncFx is called on the element are
10768          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10769          * @return {Roo.Element} The Element
10770          */
10771     syncFx : function(){
10772         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10773             block : false,
10774             concurrent : true,
10775             stopFx : false
10776         });
10777         return this;
10778     },
10779
10780         /**
10781          * Ensures that all effects queued after sequenceFx is called on the element are
10782          * run in sequence.  This is the opposite of {@link #syncFx}.
10783          * @return {Roo.Element} The Element
10784          */
10785     sequenceFx : function(){
10786         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10787             block : false,
10788             concurrent : false,
10789             stopFx : false
10790         });
10791         return this;
10792     },
10793
10794         /* @private */
10795     nextFx : function(){
10796         var ef = this.fxQueue[0];
10797         if(ef){
10798             ef.call(this);
10799         }
10800     },
10801
10802         /**
10803          * Returns true if the element has any effects actively running or queued, else returns false.
10804          * @return {Boolean} True if element has active effects, else false
10805          */
10806     hasActiveFx : function(){
10807         return this.fxQueue && this.fxQueue[0];
10808     },
10809
10810         /**
10811          * Stops any running effects and clears the element's internal effects queue if it contains
10812          * any additional effects that haven't started yet.
10813          * @return {Roo.Element} The Element
10814          */
10815     stopFx : function(){
10816         if(this.hasActiveFx()){
10817             var cur = this.fxQueue[0];
10818             if(cur && cur.anim && cur.anim.isAnimated()){
10819                 this.fxQueue = [cur]; // clear out others
10820                 cur.anim.stop(true);
10821             }
10822         }
10823         return this;
10824     },
10825
10826         /* @private */
10827     beforeFx : function(o){
10828         if(this.hasActiveFx() && !o.concurrent){
10829            if(o.stopFx){
10830                this.stopFx();
10831                return true;
10832            }
10833            return false;
10834         }
10835         return true;
10836     },
10837
10838         /**
10839          * Returns true if the element is currently blocking so that no other effect can be queued
10840          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10841          * used to ensure that an effect initiated by a user action runs to completion prior to the
10842          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10843          * @return {Boolean} True if blocking, else false
10844          */
10845     hasFxBlock : function(){
10846         var q = this.fxQueue;
10847         return q && q[0] && q[0].block;
10848     },
10849
10850         /* @private */
10851     queueFx : function(o, fn){
10852         if(!this.fxQueue){
10853             this.fxQueue = [];
10854         }
10855         if(!this.hasFxBlock()){
10856             Roo.applyIf(o, this.fxDefaults);
10857             if(!o.concurrent){
10858                 var run = this.beforeFx(o);
10859                 fn.block = o.block;
10860                 this.fxQueue.push(fn);
10861                 if(run){
10862                     this.nextFx();
10863                 }
10864             }else{
10865                 fn.call(this);
10866             }
10867         }
10868         return this;
10869     },
10870
10871         /* @private */
10872     fxWrap : function(pos, o, vis){
10873         var wrap;
10874         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10875             var wrapXY;
10876             if(o.fixPosition){
10877                 wrapXY = this.getXY();
10878             }
10879             var div = document.createElement("div");
10880             div.style.visibility = vis;
10881             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10882             wrap.setPositioning(pos);
10883             if(wrap.getStyle("position") == "static"){
10884                 wrap.position("relative");
10885             }
10886             this.clearPositioning('auto');
10887             wrap.clip();
10888             wrap.dom.appendChild(this.dom);
10889             if(wrapXY){
10890                 wrap.setXY(wrapXY);
10891             }
10892         }
10893         return wrap;
10894     },
10895
10896         /* @private */
10897     fxUnwrap : function(wrap, pos, o){
10898         this.clearPositioning();
10899         this.setPositioning(pos);
10900         if(!o.wrap){
10901             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10902             wrap.remove();
10903         }
10904     },
10905
10906         /* @private */
10907     getFxRestore : function(){
10908         var st = this.dom.style;
10909         return {pos: this.getPositioning(), width: st.width, height : st.height};
10910     },
10911
10912         /* @private */
10913     afterFx : function(o){
10914         if(o.afterStyle){
10915             this.applyStyles(o.afterStyle);
10916         }
10917         if(o.afterCls){
10918             this.addClass(o.afterCls);
10919         }
10920         if(o.remove === true){
10921             this.remove();
10922         }
10923         Roo.callback(o.callback, o.scope, [this]);
10924         if(!o.concurrent){
10925             this.fxQueue.shift();
10926             this.nextFx();
10927         }
10928     },
10929
10930         /* @private */
10931     getFxEl : function(){ // support for composite element fx
10932         return Roo.get(this.dom);
10933     },
10934
10935         /* @private */
10936     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10937         animType = animType || 'run';
10938         opt = opt || {};
10939         var anim = Roo.lib.Anim[animType](
10940             this.dom, args,
10941             (opt.duration || defaultDur) || .35,
10942             (opt.easing || defaultEase) || 'easeOut',
10943             function(){
10944                 Roo.callback(cb, this);
10945             },
10946             this
10947         );
10948         opt.anim = anim;
10949         return anim;
10950     }
10951 };
10952
10953 // backwords compat
10954 Roo.Fx.resize = Roo.Fx.scale;
10955
10956 //When included, Roo.Fx is automatically applied to Element so that all basic
10957 //effects are available directly via the Element API
10958 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10959  * Based on:
10960  * Ext JS Library 1.1.1
10961  * Copyright(c) 2006-2007, Ext JS, LLC.
10962  *
10963  * Originally Released Under LGPL - original licence link has changed is not relivant.
10964  *
10965  * Fork - LGPL
10966  * <script type="text/javascript">
10967  */
10968
10969
10970 /**
10971  * @class Roo.CompositeElement
10972  * Standard composite class. Creates a Roo.Element for every element in the collection.
10973  * <br><br>
10974  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10975  * actions will be performed on all the elements in this collection.</b>
10976  * <br><br>
10977  * All methods return <i>this</i> and can be chained.
10978  <pre><code>
10979  var els = Roo.select("#some-el div.some-class", true);
10980  // or select directly from an existing element
10981  var el = Roo.get('some-el');
10982  el.select('div.some-class', true);
10983
10984  els.setWidth(100); // all elements become 100 width
10985  els.hide(true); // all elements fade out and hide
10986  // or
10987  els.setWidth(100).hide(true);
10988  </code></pre>
10989  */
10990 Roo.CompositeElement = function(els){
10991     this.elements = [];
10992     this.addElements(els);
10993 };
10994 Roo.CompositeElement.prototype = {
10995     isComposite: true,
10996     addElements : function(els){
10997         if(!els) return this;
10998         if(typeof els == "string"){
10999             els = Roo.Element.selectorFunction(els);
11000         }
11001         var yels = this.elements;
11002         var index = yels.length-1;
11003         for(var i = 0, len = els.length; i < len; i++) {
11004                 yels[++index] = Roo.get(els[i]);
11005         }
11006         return this;
11007     },
11008
11009     /**
11010     * Clears this composite and adds the elements returned by the passed selector.
11011     * @param {String/Array} els A string CSS selector, an array of elements or an element
11012     * @return {CompositeElement} this
11013     */
11014     fill : function(els){
11015         this.elements = [];
11016         this.add(els);
11017         return this;
11018     },
11019
11020     /**
11021     * Filters this composite to only elements that match the passed selector.
11022     * @param {String} selector A string CSS selector
11023     * @param {Boolean} inverse return inverse filter (not matches)
11024     * @return {CompositeElement} this
11025     */
11026     filter : function(selector, inverse){
11027         var els = [];
11028         inverse = inverse || false;
11029         this.each(function(el){
11030             var match = inverse ? !el.is(selector) : el.is(selector);
11031             if(match){
11032                 els[els.length] = el.dom;
11033             }
11034         });
11035         this.fill(els);
11036         return this;
11037     },
11038
11039     invoke : function(fn, args){
11040         var els = this.elements;
11041         for(var i = 0, len = els.length; i < len; i++) {
11042                 Roo.Element.prototype[fn].apply(els[i], args);
11043         }
11044         return this;
11045     },
11046     /**
11047     * Adds elements to this composite.
11048     * @param {String/Array} els A string CSS selector, an array of elements or an element
11049     * @return {CompositeElement} this
11050     */
11051     add : function(els){
11052         if(typeof els == "string"){
11053             this.addElements(Roo.Element.selectorFunction(els));
11054         }else if(els.length !== undefined){
11055             this.addElements(els);
11056         }else{
11057             this.addElements([els]);
11058         }
11059         return this;
11060     },
11061     /**
11062     * Calls the passed function passing (el, this, index) for each element in this composite.
11063     * @param {Function} fn The function to call
11064     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11065     * @return {CompositeElement} this
11066     */
11067     each : function(fn, scope){
11068         var els = this.elements;
11069         for(var i = 0, len = els.length; i < len; i++){
11070             if(fn.call(scope || els[i], els[i], this, i) === false) {
11071                 break;
11072             }
11073         }
11074         return this;
11075     },
11076
11077     /**
11078      * Returns the Element object at the specified index
11079      * @param {Number} index
11080      * @return {Roo.Element}
11081      */
11082     item : function(index){
11083         return this.elements[index] || null;
11084     },
11085
11086     /**
11087      * Returns the first Element
11088      * @return {Roo.Element}
11089      */
11090     first : function(){
11091         return this.item(0);
11092     },
11093
11094     /**
11095      * Returns the last Element
11096      * @return {Roo.Element}
11097      */
11098     last : function(){
11099         return this.item(this.elements.length-1);
11100     },
11101
11102     /**
11103      * Returns the number of elements in this composite
11104      * @return Number
11105      */
11106     getCount : function(){
11107         return this.elements.length;
11108     },
11109
11110     /**
11111      * Returns true if this composite contains the passed element
11112      * @return Boolean
11113      */
11114     contains : function(el){
11115         return this.indexOf(el) !== -1;
11116     },
11117
11118     /**
11119      * Returns true if this composite contains the passed element
11120      * @return Boolean
11121      */
11122     indexOf : function(el){
11123         return this.elements.indexOf(Roo.get(el));
11124     },
11125
11126
11127     /**
11128     * Removes the specified element(s).
11129     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11130     * or an array of any of those.
11131     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11132     * @return {CompositeElement} this
11133     */
11134     removeElement : function(el, removeDom){
11135         if(el instanceof Array){
11136             for(var i = 0, len = el.length; i < len; i++){
11137                 this.removeElement(el[i]);
11138             }
11139             return this;
11140         }
11141         var index = typeof el == 'number' ? el : this.indexOf(el);
11142         if(index !== -1){
11143             if(removeDom){
11144                 var d = this.elements[index];
11145                 if(d.dom){
11146                     d.remove();
11147                 }else{
11148                     d.parentNode.removeChild(d);
11149                 }
11150             }
11151             this.elements.splice(index, 1);
11152         }
11153         return this;
11154     },
11155
11156     /**
11157     * Replaces the specified element with the passed element.
11158     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11159     * to replace.
11160     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11161     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11162     * @return {CompositeElement} this
11163     */
11164     replaceElement : function(el, replacement, domReplace){
11165         var index = typeof el == 'number' ? el : this.indexOf(el);
11166         if(index !== -1){
11167             if(domReplace){
11168                 this.elements[index].replaceWith(replacement);
11169             }else{
11170                 this.elements.splice(index, 1, Roo.get(replacement))
11171             }
11172         }
11173         return this;
11174     },
11175
11176     /**
11177      * Removes all elements.
11178      */
11179     clear : function(){
11180         this.elements = [];
11181     }
11182 };
11183 (function(){
11184     Roo.CompositeElement.createCall = function(proto, fnName){
11185         if(!proto[fnName]){
11186             proto[fnName] = function(){
11187                 return this.invoke(fnName, arguments);
11188             };
11189         }
11190     };
11191     for(var fnName in Roo.Element.prototype){
11192         if(typeof Roo.Element.prototype[fnName] == "function"){
11193             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11194         }
11195     };
11196 })();
11197 /*
11198  * Based on:
11199  * Ext JS Library 1.1.1
11200  * Copyright(c) 2006-2007, Ext JS, LLC.
11201  *
11202  * Originally Released Under LGPL - original licence link has changed is not relivant.
11203  *
11204  * Fork - LGPL
11205  * <script type="text/javascript">
11206  */
11207
11208 /**
11209  * @class Roo.CompositeElementLite
11210  * @extends Roo.CompositeElement
11211  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11212  <pre><code>
11213  var els = Roo.select("#some-el div.some-class");
11214  // or select directly from an existing element
11215  var el = Roo.get('some-el');
11216  el.select('div.some-class');
11217
11218  els.setWidth(100); // all elements become 100 width
11219  els.hide(true); // all elements fade out and hide
11220  // or
11221  els.setWidth(100).hide(true);
11222  </code></pre><br><br>
11223  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11224  * actions will be performed on all the elements in this collection.</b>
11225  */
11226 Roo.CompositeElementLite = function(els){
11227     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11228     this.el = new Roo.Element.Flyweight();
11229 };
11230 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11231     addElements : function(els){
11232         if(els){
11233             if(els instanceof Array){
11234                 this.elements = this.elements.concat(els);
11235             }else{
11236                 var yels = this.elements;
11237                 var index = yels.length-1;
11238                 for(var i = 0, len = els.length; i < len; i++) {
11239                     yels[++index] = els[i];
11240                 }
11241             }
11242         }
11243         return this;
11244     },
11245     invoke : function(fn, args){
11246         var els = this.elements;
11247         var el = this.el;
11248         for(var i = 0, len = els.length; i < len; i++) {
11249             el.dom = els[i];
11250                 Roo.Element.prototype[fn].apply(el, args);
11251         }
11252         return this;
11253     },
11254     /**
11255      * Returns a flyweight Element of the dom element object at the specified index
11256      * @param {Number} index
11257      * @return {Roo.Element}
11258      */
11259     item : function(index){
11260         if(!this.elements[index]){
11261             return null;
11262         }
11263         this.el.dom = this.elements[index];
11264         return this.el;
11265     },
11266
11267     // fixes scope with flyweight
11268     addListener : function(eventName, handler, scope, opt){
11269         var els = this.elements;
11270         for(var i = 0, len = els.length; i < len; i++) {
11271             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11272         }
11273         return this;
11274     },
11275
11276     /**
11277     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11278     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11279     * a reference to the dom node, use el.dom.</b>
11280     * @param {Function} fn The function to call
11281     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11282     * @return {CompositeElement} this
11283     */
11284     each : function(fn, scope){
11285         var els = this.elements;
11286         var el = this.el;
11287         for(var i = 0, len = els.length; i < len; i++){
11288             el.dom = els[i];
11289                 if(fn.call(scope || el, el, this, i) === false){
11290                 break;
11291             }
11292         }
11293         return this;
11294     },
11295
11296     indexOf : function(el){
11297         return this.elements.indexOf(Roo.getDom(el));
11298     },
11299
11300     replaceElement : function(el, replacement, domReplace){
11301         var index = typeof el == 'number' ? el : this.indexOf(el);
11302         if(index !== -1){
11303             replacement = Roo.getDom(replacement);
11304             if(domReplace){
11305                 var d = this.elements[index];
11306                 d.parentNode.insertBefore(replacement, d);
11307                 d.parentNode.removeChild(d);
11308             }
11309             this.elements.splice(index, 1, replacement);
11310         }
11311         return this;
11312     }
11313 });
11314 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11315
11316 /*
11317  * Based on:
11318  * Ext JS Library 1.1.1
11319  * Copyright(c) 2006-2007, Ext JS, LLC.
11320  *
11321  * Originally Released Under LGPL - original licence link has changed is not relivant.
11322  *
11323  * Fork - LGPL
11324  * <script type="text/javascript">
11325  */
11326
11327  
11328
11329 /**
11330  * @class Roo.data.Connection
11331  * @extends Roo.util.Observable
11332  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11333  * either to a configured URL, or to a URL specified at request time.<br><br>
11334  * <p>
11335  * Requests made by this class are asynchronous, and will return immediately. No data from
11336  * the server will be available to the statement immediately following the {@link #request} call.
11337  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11338  * <p>
11339  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11340  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11341  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11342  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11343  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11344  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11345  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11346  * standard DOM methods.
11347  * @constructor
11348  * @param {Object} config a configuration object.
11349  */
11350 Roo.data.Connection = function(config){
11351     Roo.apply(this, config);
11352     this.addEvents({
11353         /**
11354          * @event beforerequest
11355          * Fires before a network request is made to retrieve a data object.
11356          * @param {Connection} conn This Connection object.
11357          * @param {Object} options The options config object passed to the {@link #request} method.
11358          */
11359         "beforerequest" : true,
11360         /**
11361          * @event requestcomplete
11362          * Fires if the request was successfully completed.
11363          * @param {Connection} conn This Connection object.
11364          * @param {Object} response The XHR object containing the response data.
11365          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11366          * @param {Object} options The options config object passed to the {@link #request} method.
11367          */
11368         "requestcomplete" : true,
11369         /**
11370          * @event requestexception
11371          * Fires if an error HTTP status was returned from the server.
11372          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11373          * @param {Connection} conn This Connection object.
11374          * @param {Object} response The XHR object containing the response data.
11375          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11376          * @param {Object} options The options config object passed to the {@link #request} method.
11377          */
11378         "requestexception" : true
11379     });
11380     Roo.data.Connection.superclass.constructor.call(this);
11381 };
11382
11383 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11384     /**
11385      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11386      */
11387     /**
11388      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11389      * extra parameters to each request made by this object. (defaults to undefined)
11390      */
11391     /**
11392      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11393      *  to each request made by this object. (defaults to undefined)
11394      */
11395     /**
11396      * @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)
11397      */
11398     /**
11399      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11400      */
11401     timeout : 30000,
11402     /**
11403      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11404      * @type Boolean
11405      */
11406     autoAbort:false,
11407
11408     /**
11409      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11410      * @type Boolean
11411      */
11412     disableCaching: true,
11413
11414     /**
11415      * Sends an HTTP request to a remote server.
11416      * @param {Object} options An object which may contain the following properties:<ul>
11417      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11418      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11419      * request, a url encoded string or a function to call to get either.</li>
11420      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11421      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11422      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11423      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11424      * <li>options {Object} The parameter to the request call.</li>
11425      * <li>success {Boolean} True if the request succeeded.</li>
11426      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11427      * </ul></li>
11428      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11429      * The callback is passed the following parameters:<ul>
11430      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11431      * <li>options {Object} The parameter to the request call.</li>
11432      * </ul></li>
11433      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11434      * The callback is passed the following parameters:<ul>
11435      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11436      * <li>options {Object} The parameter to the request call.</li>
11437      * </ul></li>
11438      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11439      * for the callback function. Defaults to the browser window.</li>
11440      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11441      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11442      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11443      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11444      * params for the post data. Any params will be appended to the URL.</li>
11445      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11446      * </ul>
11447      * @return {Number} transactionId
11448      */
11449     request : function(o){
11450         if(this.fireEvent("beforerequest", this, o) !== false){
11451             var p = o.params;
11452
11453             if(typeof p == "function"){
11454                 p = p.call(o.scope||window, o);
11455             }
11456             if(typeof p == "object"){
11457                 p = Roo.urlEncode(o.params);
11458             }
11459             if(this.extraParams){
11460                 var extras = Roo.urlEncode(this.extraParams);
11461                 p = p ? (p + '&' + extras) : extras;
11462             }
11463
11464             var url = o.url || this.url;
11465             if(typeof url == 'function'){
11466                 url = url.call(o.scope||window, o);
11467             }
11468
11469             if(o.form){
11470                 var form = Roo.getDom(o.form);
11471                 url = url || form.action;
11472
11473                 var enctype = form.getAttribute("enctype");
11474                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11475                     return this.doFormUpload(o, p, url);
11476                 }
11477                 var f = Roo.lib.Ajax.serializeForm(form);
11478                 p = p ? (p + '&' + f) : f;
11479             }
11480
11481             var hs = o.headers;
11482             if(this.defaultHeaders){
11483                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11484                 if(!o.headers){
11485                     o.headers = hs;
11486                 }
11487             }
11488
11489             var cb = {
11490                 success: this.handleResponse,
11491                 failure: this.handleFailure,
11492                 scope: this,
11493                 argument: {options: o},
11494                 timeout : o.timeout || this.timeout
11495             };
11496
11497             var method = o.method||this.method||(p ? "POST" : "GET");
11498
11499             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11500                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11501             }
11502
11503             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11504                 if(o.autoAbort){
11505                     this.abort();
11506                 }
11507             }else if(this.autoAbort !== false){
11508                 this.abort();
11509             }
11510
11511             if((method == 'GET' && p) || o.xmlData){
11512                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11513                 p = '';
11514             }
11515             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11516             return this.transId;
11517         }else{
11518             Roo.callback(o.callback, o.scope, [o, null, null]);
11519             return null;
11520         }
11521     },
11522
11523     /**
11524      * Determine whether this object has a request outstanding.
11525      * @param {Number} transactionId (Optional) defaults to the last transaction
11526      * @return {Boolean} True if there is an outstanding request.
11527      */
11528     isLoading : function(transId){
11529         if(transId){
11530             return Roo.lib.Ajax.isCallInProgress(transId);
11531         }else{
11532             return this.transId ? true : false;
11533         }
11534     },
11535
11536     /**
11537      * Aborts any outstanding request.
11538      * @param {Number} transactionId (Optional) defaults to the last transaction
11539      */
11540     abort : function(transId){
11541         if(transId || this.isLoading()){
11542             Roo.lib.Ajax.abort(transId || this.transId);
11543         }
11544     },
11545
11546     // private
11547     handleResponse : function(response){
11548         this.transId = false;
11549         var options = response.argument.options;
11550         response.argument = options ? options.argument : null;
11551         this.fireEvent("requestcomplete", this, response, options);
11552         Roo.callback(options.success, options.scope, [response, options]);
11553         Roo.callback(options.callback, options.scope, [options, true, response]);
11554     },
11555
11556     // private
11557     handleFailure : function(response, e){
11558         this.transId = false;
11559         var options = response.argument.options;
11560         response.argument = options ? options.argument : null;
11561         this.fireEvent("requestexception", this, response, options, e);
11562         Roo.callback(options.failure, options.scope, [response, options]);
11563         Roo.callback(options.callback, options.scope, [options, false, response]);
11564     },
11565
11566     // private
11567     doFormUpload : function(o, ps, url){
11568         var id = Roo.id();
11569         var frame = document.createElement('iframe');
11570         frame.id = id;
11571         frame.name = id;
11572         frame.className = 'x-hidden';
11573         if(Roo.isIE){
11574             frame.src = Roo.SSL_SECURE_URL;
11575         }
11576         document.body.appendChild(frame);
11577
11578         if(Roo.isIE){
11579            document.frames[id].name = id;
11580         }
11581
11582         var form = Roo.getDom(o.form);
11583         form.target = id;
11584         form.method = 'POST';
11585         form.enctype = form.encoding = 'multipart/form-data';
11586         if(url){
11587             form.action = url;
11588         }
11589
11590         var hiddens, hd;
11591         if(ps){ // add dynamic params
11592             hiddens = [];
11593             ps = Roo.urlDecode(ps, false);
11594             for(var k in ps){
11595                 if(ps.hasOwnProperty(k)){
11596                     hd = document.createElement('input');
11597                     hd.type = 'hidden';
11598                     hd.name = k;
11599                     hd.value = ps[k];
11600                     form.appendChild(hd);
11601                     hiddens.push(hd);
11602                 }
11603             }
11604         }
11605
11606         function cb(){
11607             var r = {  // bogus response object
11608                 responseText : '',
11609                 responseXML : null
11610             };
11611
11612             r.argument = o ? o.argument : null;
11613
11614             try { //
11615                 var doc;
11616                 if(Roo.isIE){
11617                     doc = frame.contentWindow.document;
11618                 }else {
11619                     doc = (frame.contentDocument || window.frames[id].document);
11620                 }
11621                 if(doc && doc.body){
11622                     r.responseText = doc.body.innerHTML;
11623                 }
11624                 if(doc && doc.XMLDocument){
11625                     r.responseXML = doc.XMLDocument;
11626                 }else {
11627                     r.responseXML = doc;
11628                 }
11629             }
11630             catch(e) {
11631                 // ignore
11632             }
11633
11634             Roo.EventManager.removeListener(frame, 'load', cb, this);
11635
11636             this.fireEvent("requestcomplete", this, r, o);
11637             Roo.callback(o.success, o.scope, [r, o]);
11638             Roo.callback(o.callback, o.scope, [o, true, r]);
11639
11640             setTimeout(function(){document.body.removeChild(frame);}, 100);
11641         }
11642
11643         Roo.EventManager.on(frame, 'load', cb, this);
11644         form.submit();
11645
11646         if(hiddens){ // remove dynamic params
11647             for(var i = 0, len = hiddens.length; i < len; i++){
11648                 form.removeChild(hiddens[i]);
11649             }
11650         }
11651     }
11652 });
11653 /*
11654  * Based on:
11655  * Ext JS Library 1.1.1
11656  * Copyright(c) 2006-2007, Ext JS, LLC.
11657  *
11658  * Originally Released Under LGPL - original licence link has changed is not relivant.
11659  *
11660  * Fork - LGPL
11661  * <script type="text/javascript">
11662  */
11663  
11664 /**
11665  * Global Ajax request class.
11666  * 
11667  * @class Roo.Ajax
11668  * @extends Roo.data.Connection
11669  * @static
11670  * 
11671  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11672  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11673  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11674  * @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)
11675  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11676  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11677  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11678  */
11679 Roo.Ajax = new Roo.data.Connection({
11680     // fix up the docs
11681     /**
11682      * @scope Roo.Ajax
11683      * @type {Boolear} 
11684      */
11685     autoAbort : false,
11686
11687     /**
11688      * Serialize the passed form into a url encoded string
11689      * @scope Roo.Ajax
11690      * @param {String/HTMLElement} form
11691      * @return {String}
11692      */
11693     serializeForm : function(form){
11694         return Roo.lib.Ajax.serializeForm(form);
11695     }
11696 });/*
11697  * Based on:
11698  * Ext JS Library 1.1.1
11699  * Copyright(c) 2006-2007, Ext JS, LLC.
11700  *
11701  * Originally Released Under LGPL - original licence link has changed is not relivant.
11702  *
11703  * Fork - LGPL
11704  * <script type="text/javascript">
11705  */
11706
11707  
11708 /**
11709  * @class Roo.UpdateManager
11710  * @extends Roo.util.Observable
11711  * Provides AJAX-style update for Element object.<br><br>
11712  * Usage:<br>
11713  * <pre><code>
11714  * // Get it from a Roo.Element object
11715  * var el = Roo.get("foo");
11716  * var mgr = el.getUpdateManager();
11717  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11718  * ...
11719  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11720  * <br>
11721  * // or directly (returns the same UpdateManager instance)
11722  * var mgr = new Roo.UpdateManager("myElementId");
11723  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11724  * mgr.on("update", myFcnNeedsToKnow);
11725  * <br>
11726    // short handed call directly from the element object
11727    Roo.get("foo").load({
11728         url: "bar.php",
11729         scripts:true,
11730         params: "for=bar",
11731         text: "Loading Foo..."
11732    });
11733  * </code></pre>
11734  * @constructor
11735  * Create new UpdateManager directly.
11736  * @param {String/HTMLElement/Roo.Element} el The element to update
11737  * @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).
11738  */
11739 Roo.UpdateManager = function(el, forceNew){
11740     el = Roo.get(el);
11741     if(!forceNew && el.updateManager){
11742         return el.updateManager;
11743     }
11744     /**
11745      * The Element object
11746      * @type Roo.Element
11747      */
11748     this.el = el;
11749     /**
11750      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11751      * @type String
11752      */
11753     this.defaultUrl = null;
11754
11755     this.addEvents({
11756         /**
11757          * @event beforeupdate
11758          * Fired before an update is made, return false from your handler and the update is cancelled.
11759          * @param {Roo.Element} el
11760          * @param {String/Object/Function} url
11761          * @param {String/Object} params
11762          */
11763         "beforeupdate": true,
11764         /**
11765          * @event update
11766          * Fired after successful update is made.
11767          * @param {Roo.Element} el
11768          * @param {Object} oResponseObject The response Object
11769          */
11770         "update": true,
11771         /**
11772          * @event failure
11773          * Fired on update failure.
11774          * @param {Roo.Element} el
11775          * @param {Object} oResponseObject The response Object
11776          */
11777         "failure": true
11778     });
11779     var d = Roo.UpdateManager.defaults;
11780     /**
11781      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11782      * @type String
11783      */
11784     this.sslBlankUrl = d.sslBlankUrl;
11785     /**
11786      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11787      * @type Boolean
11788      */
11789     this.disableCaching = d.disableCaching;
11790     /**
11791      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11792      * @type String
11793      */
11794     this.indicatorText = d.indicatorText;
11795     /**
11796      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11797      * @type String
11798      */
11799     this.showLoadIndicator = d.showLoadIndicator;
11800     /**
11801      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11802      * @type Number
11803      */
11804     this.timeout = d.timeout;
11805
11806     /**
11807      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11808      * @type Boolean
11809      */
11810     this.loadScripts = d.loadScripts;
11811
11812     /**
11813      * Transaction object of current executing transaction
11814      */
11815     this.transaction = null;
11816
11817     /**
11818      * @private
11819      */
11820     this.autoRefreshProcId = null;
11821     /**
11822      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11823      * @type Function
11824      */
11825     this.refreshDelegate = this.refresh.createDelegate(this);
11826     /**
11827      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11828      * @type Function
11829      */
11830     this.updateDelegate = this.update.createDelegate(this);
11831     /**
11832      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11833      * @type Function
11834      */
11835     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11836     /**
11837      * @private
11838      */
11839     this.successDelegate = this.processSuccess.createDelegate(this);
11840     /**
11841      * @private
11842      */
11843     this.failureDelegate = this.processFailure.createDelegate(this);
11844
11845     if(!this.renderer){
11846      /**
11847       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11848       */
11849     this.renderer = new Roo.UpdateManager.BasicRenderer();
11850     }
11851     
11852     Roo.UpdateManager.superclass.constructor.call(this);
11853 };
11854
11855 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11856     /**
11857      * Get the Element this UpdateManager is bound to
11858      * @return {Roo.Element} The element
11859      */
11860     getEl : function(){
11861         return this.el;
11862     },
11863     /**
11864      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11865      * @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:
11866 <pre><code>
11867 um.update({<br/>
11868     url: "your-url.php",<br/>
11869     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11870     callback: yourFunction,<br/>
11871     scope: yourObject, //(optional scope)  <br/>
11872     discardUrl: false, <br/>
11873     nocache: false,<br/>
11874     text: "Loading...",<br/>
11875     timeout: 30,<br/>
11876     scripts: false<br/>
11877 });
11878 </code></pre>
11879      * The only required property is url. The optional properties nocache, text and scripts
11880      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11881      * @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}
11882      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11883      * @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.
11884      */
11885     update : function(url, params, callback, discardUrl){
11886         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11887             var method = this.method,
11888                 cfg;
11889             if(typeof url == "object"){ // must be config object
11890                 cfg = url;
11891                 url = cfg.url;
11892                 params = params || cfg.params;
11893                 callback = callback || cfg.callback;
11894                 discardUrl = discardUrl || cfg.discardUrl;
11895                 if(callback && cfg.scope){
11896                     callback = callback.createDelegate(cfg.scope);
11897                 }
11898                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11899                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11900                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11901                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11902                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11903             }
11904             this.showLoading();
11905             if(!discardUrl){
11906                 this.defaultUrl = url;
11907             }
11908             if(typeof url == "function"){
11909                 url = url.call(this);
11910             }
11911
11912             method = method || (params ? "POST" : "GET");
11913             if(method == "GET"){
11914                 url = this.prepareUrl(url);
11915             }
11916
11917             var o = Roo.apply(cfg ||{}, {
11918                 url : url,
11919                 params: params,
11920                 success: this.successDelegate,
11921                 failure: this.failureDelegate,
11922                 callback: undefined,
11923                 timeout: (this.timeout*1000),
11924                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11925             });
11926             Roo.log("updated manager called with timeout of " + o.timeout);
11927             this.transaction = Roo.Ajax.request(o);
11928         }
11929     },
11930
11931     /**
11932      * 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.
11933      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11934      * @param {String/HTMLElement} form The form Id or form element
11935      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11936      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11937      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11938      */
11939     formUpdate : function(form, url, reset, callback){
11940         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11941             if(typeof url == "function"){
11942                 url = url.call(this);
11943             }
11944             form = Roo.getDom(form);
11945             this.transaction = Roo.Ajax.request({
11946                 form: form,
11947                 url:url,
11948                 success: this.successDelegate,
11949                 failure: this.failureDelegate,
11950                 timeout: (this.timeout*1000),
11951                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11952             });
11953             this.showLoading.defer(1, this);
11954         }
11955     },
11956
11957     /**
11958      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11959      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11960      */
11961     refresh : function(callback){
11962         if(this.defaultUrl == null){
11963             return;
11964         }
11965         this.update(this.defaultUrl, null, callback, true);
11966     },
11967
11968     /**
11969      * Set this element to auto refresh.
11970      * @param {Number} interval How often to update (in seconds).
11971      * @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)
11972      * @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}
11973      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11974      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11975      */
11976     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11977         if(refreshNow){
11978             this.update(url || this.defaultUrl, params, callback, true);
11979         }
11980         if(this.autoRefreshProcId){
11981             clearInterval(this.autoRefreshProcId);
11982         }
11983         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11984     },
11985
11986     /**
11987      * Stop auto refresh on this element.
11988      */
11989      stopAutoRefresh : function(){
11990         if(this.autoRefreshProcId){
11991             clearInterval(this.autoRefreshProcId);
11992             delete this.autoRefreshProcId;
11993         }
11994     },
11995
11996     isAutoRefreshing : function(){
11997        return this.autoRefreshProcId ? true : false;
11998     },
11999     /**
12000      * Called to update the element to "Loading" state. Override to perform custom action.
12001      */
12002     showLoading : function(){
12003         if(this.showLoadIndicator){
12004             this.el.update(this.indicatorText);
12005         }
12006     },
12007
12008     /**
12009      * Adds unique parameter to query string if disableCaching = true
12010      * @private
12011      */
12012     prepareUrl : function(url){
12013         if(this.disableCaching){
12014             var append = "_dc=" + (new Date().getTime());
12015             if(url.indexOf("?") !== -1){
12016                 url += "&" + append;
12017             }else{
12018                 url += "?" + append;
12019             }
12020         }
12021         return url;
12022     },
12023
12024     /**
12025      * @private
12026      */
12027     processSuccess : function(response){
12028         this.transaction = null;
12029         if(response.argument.form && response.argument.reset){
12030             try{ // put in try/catch since some older FF releases had problems with this
12031                 response.argument.form.reset();
12032             }catch(e){}
12033         }
12034         if(this.loadScripts){
12035             this.renderer.render(this.el, response, this,
12036                 this.updateComplete.createDelegate(this, [response]));
12037         }else{
12038             this.renderer.render(this.el, response, this);
12039             this.updateComplete(response);
12040         }
12041     },
12042
12043     updateComplete : function(response){
12044         this.fireEvent("update", this.el, response);
12045         if(typeof response.argument.callback == "function"){
12046             response.argument.callback(this.el, true, response);
12047         }
12048     },
12049
12050     /**
12051      * @private
12052      */
12053     processFailure : function(response){
12054         this.transaction = null;
12055         this.fireEvent("failure", this.el, response);
12056         if(typeof response.argument.callback == "function"){
12057             response.argument.callback(this.el, false, response);
12058         }
12059     },
12060
12061     /**
12062      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12063      * @param {Object} renderer The object implementing the render() method
12064      */
12065     setRenderer : function(renderer){
12066         this.renderer = renderer;
12067     },
12068
12069     getRenderer : function(){
12070        return this.renderer;
12071     },
12072
12073     /**
12074      * Set the defaultUrl used for updates
12075      * @param {String/Function} defaultUrl The url or a function to call to get the url
12076      */
12077     setDefaultUrl : function(defaultUrl){
12078         this.defaultUrl = defaultUrl;
12079     },
12080
12081     /**
12082      * Aborts the executing transaction
12083      */
12084     abort : function(){
12085         if(this.transaction){
12086             Roo.Ajax.abort(this.transaction);
12087         }
12088     },
12089
12090     /**
12091      * Returns true if an update is in progress
12092      * @return {Boolean}
12093      */
12094     isUpdating : function(){
12095         if(this.transaction){
12096             return Roo.Ajax.isLoading(this.transaction);
12097         }
12098         return false;
12099     }
12100 });
12101
12102 /**
12103  * @class Roo.UpdateManager.defaults
12104  * @static (not really - but it helps the doc tool)
12105  * The defaults collection enables customizing the default properties of UpdateManager
12106  */
12107    Roo.UpdateManager.defaults = {
12108        /**
12109          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12110          * @type Number
12111          */
12112          timeout : 30,
12113
12114          /**
12115          * True to process scripts by default (Defaults to false).
12116          * @type Boolean
12117          */
12118         loadScripts : false,
12119
12120         /**
12121         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12122         * @type String
12123         */
12124         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12125         /**
12126          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12127          * @type Boolean
12128          */
12129         disableCaching : false,
12130         /**
12131          * Whether to show indicatorText when loading (Defaults to true).
12132          * @type Boolean
12133          */
12134         showLoadIndicator : true,
12135         /**
12136          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12137          * @type String
12138          */
12139         indicatorText : '<div class="loading-indicator">Loading...</div>'
12140    };
12141
12142 /**
12143  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12144  *Usage:
12145  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12146  * @param {String/HTMLElement/Roo.Element} el The element to update
12147  * @param {String} url The url
12148  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12149  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12150  * @static
12151  * @deprecated
12152  * @member Roo.UpdateManager
12153  */
12154 Roo.UpdateManager.updateElement = function(el, url, params, options){
12155     var um = Roo.get(el, true).getUpdateManager();
12156     Roo.apply(um, options);
12157     um.update(url, params, options ? options.callback : null);
12158 };
12159 // alias for backwards compat
12160 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12161 /**
12162  * @class Roo.UpdateManager.BasicRenderer
12163  * Default Content renderer. Updates the elements innerHTML with the responseText.
12164  */
12165 Roo.UpdateManager.BasicRenderer = function(){};
12166
12167 Roo.UpdateManager.BasicRenderer.prototype = {
12168     /**
12169      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12170      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12171      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12172      * @param {Roo.Element} el The element being rendered
12173      * @param {Object} response The YUI Connect response object
12174      * @param {UpdateManager} updateManager The calling update manager
12175      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12176      */
12177      render : function(el, response, updateManager, callback){
12178         el.update(response.responseText, updateManager.loadScripts, callback);
12179     }
12180 };
12181 /*
12182  * Based on:
12183  * Roo JS
12184  * (c)) Alan Knowles
12185  * Licence : LGPL
12186  */
12187
12188
12189 /**
12190  * @class Roo.DomTemplate
12191  * @extends Roo.Template
12192  * An effort at a dom based template engine..
12193  *
12194  * Similar to XTemplate, except it uses dom parsing to create the template..
12195  *
12196  * Supported features:
12197  *
12198  *  Tags:
12199
12200 <pre><code>
12201       {a_variable} - output encoded.
12202       {a_variable.format:("Y-m-d")} - call a method on the variable
12203       {a_variable:raw} - unencoded output
12204       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12205       {a_variable:this.method_on_template(...)} - call a method on the template object.
12206  
12207 </code></pre>
12208  *  The tpl tag:
12209 <pre><code>
12210         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12211         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12212         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12213         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12214   
12215 </code></pre>
12216  *      
12217  */
12218 Roo.DomTemplate = function()
12219 {
12220      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12221      if (this.html) {
12222         this.compile();
12223      }
12224 };
12225
12226
12227 Roo.extend(Roo.DomTemplate, Roo.Template, {
12228     /**
12229      * id counter for sub templates.
12230      */
12231     id : 0,
12232     /**
12233      * flag to indicate if dom parser is inside a pre,
12234      * it will strip whitespace if not.
12235      */
12236     inPre : false,
12237     
12238     /**
12239      * The various sub templates
12240      */
12241     tpls : false,
12242     
12243     
12244     
12245     /**
12246      *
12247      * basic tag replacing syntax
12248      * WORD:WORD()
12249      *
12250      * // you can fake an object call by doing this
12251      *  x.t:(test,tesT) 
12252      * 
12253      */
12254     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12255     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12256     
12257     iterChild : function (node, method) {
12258         
12259         var oldPre = this.inPre;
12260         if (node.tagName == 'PRE') {
12261             this.inPre = true;
12262         }
12263         for( var i = 0; i < node.childNodes.length; i++) {
12264             method.call(this, node.childNodes[i]);
12265         }
12266         this.inPre = oldPre;
12267     },
12268     
12269     
12270     
12271     /**
12272      * compile the template
12273      *
12274      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12275      *
12276      */
12277     compile: function()
12278     {
12279         var s = this.html;
12280         
12281         // covert the html into DOM...
12282         var doc = false;
12283         var div =false;
12284         try {
12285             doc = document.implementation.createHTMLDocument("");
12286             doc.documentElement.innerHTML =   this.html  ;
12287             div = doc.documentElement;
12288         } catch (e) {
12289             // old IE... - nasty -- it causes all sorts of issues.. with
12290             // images getting pulled from server..
12291             div = document.createElement('div');
12292             div.innerHTML = this.html;
12293         }
12294         //doc.documentElement.innerHTML = htmlBody
12295          
12296         
12297         
12298         this.tpls = [];
12299         var _t = this;
12300         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12301         
12302         var tpls = this.tpls;
12303         
12304         // create a top level template from the snippet..
12305         
12306         //Roo.log(div.innerHTML);
12307         
12308         var tpl = {
12309             uid : 'master',
12310             id : this.id++,
12311             attr : false,
12312             value : false,
12313             body : div.innerHTML,
12314             
12315             forCall : false,
12316             execCall : false,
12317             dom : div,
12318             isTop : true
12319             
12320         };
12321         tpls.unshift(tpl);
12322         
12323         
12324         // compile them...
12325         this.tpls = [];
12326         Roo.each(tpls, function(tp){
12327             this.compileTpl(tp);
12328             this.tpls[tp.id] = tp;
12329         }, this);
12330         
12331         this.master = tpls[0];
12332         return this;
12333         
12334         
12335     },
12336     
12337     compileNode : function(node, istop) {
12338         // test for
12339         //Roo.log(node);
12340         
12341         
12342         // skip anything not a tag..
12343         if (node.nodeType != 1) {
12344             if (node.nodeType == 3 && !this.inPre) {
12345                 // reduce white space..
12346                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12347                 
12348             }
12349             return;
12350         }
12351         
12352         var tpl = {
12353             uid : false,
12354             id : false,
12355             attr : false,
12356             value : false,
12357             body : '',
12358             
12359             forCall : false,
12360             execCall : false,
12361             dom : false,
12362             isTop : istop
12363             
12364             
12365         };
12366         
12367         
12368         switch(true) {
12369             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12370             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12371             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12372             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12373             // no default..
12374         }
12375         
12376         
12377         if (!tpl.attr) {
12378             // just itterate children..
12379             this.iterChild(node,this.compileNode);
12380             return;
12381         }
12382         tpl.uid = this.id++;
12383         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12384         node.removeAttribute('roo-'+ tpl.attr);
12385         if (tpl.attr != 'name') {
12386             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12387             node.parentNode.replaceChild(placeholder,  node);
12388         } else {
12389             
12390             var placeholder =  document.createElement('span');
12391             placeholder.className = 'roo-tpl-' + tpl.value;
12392             node.parentNode.replaceChild(placeholder,  node);
12393         }
12394         
12395         // parent now sees '{domtplXXXX}
12396         this.iterChild(node,this.compileNode);
12397         
12398         // we should now have node body...
12399         var div = document.createElement('div');
12400         div.appendChild(node);
12401         tpl.dom = node;
12402         // this has the unfortunate side effect of converting tagged attributes
12403         // eg. href="{...}" into %7C...%7D
12404         // this has been fixed by searching for those combo's although it's a bit hacky..
12405         
12406         
12407         tpl.body = div.innerHTML;
12408         
12409         
12410          
12411         tpl.id = tpl.uid;
12412         switch(tpl.attr) {
12413             case 'for' :
12414                 switch (tpl.value) {
12415                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12416                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12417                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12418                 }
12419                 break;
12420             
12421             case 'exec':
12422                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12423                 break;
12424             
12425             case 'if':     
12426                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12427                 break;
12428             
12429             case 'name':
12430                 tpl.id  = tpl.value; // replace non characters???
12431                 break;
12432             
12433         }
12434         
12435         
12436         this.tpls.push(tpl);
12437         
12438         
12439         
12440     },
12441     
12442     
12443     
12444     
12445     /**
12446      * Compile a segment of the template into a 'sub-template'
12447      *
12448      * 
12449      * 
12450      *
12451      */
12452     compileTpl : function(tpl)
12453     {
12454         var fm = Roo.util.Format;
12455         var useF = this.disableFormats !== true;
12456         
12457         var sep = Roo.isGecko ? "+\n" : ",\n";
12458         
12459         var undef = function(str) {
12460             Roo.debug && Roo.log("Property not found :"  + str);
12461             return '';
12462         };
12463           
12464         //Roo.log(tpl.body);
12465         
12466         
12467         
12468         var fn = function(m, lbrace, name, format, args)
12469         {
12470             //Roo.log("ARGS");
12471             //Roo.log(arguments);
12472             args = args ? args.replace(/\\'/g,"'") : args;
12473             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12474             if (typeof(format) == 'undefined') {
12475                 format =  'htmlEncode'; 
12476             }
12477             if (format == 'raw' ) {
12478                 format = false;
12479             }
12480             
12481             if(name.substr(0, 6) == 'domtpl'){
12482                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12483             }
12484             
12485             // build an array of options to determine if value is undefined..
12486             
12487             // basically get 'xxxx.yyyy' then do
12488             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12489             //    (function () { Roo.log("Property not found"); return ''; })() :
12490             //    ......
12491             
12492             var udef_ar = [];
12493             var lookfor = '';
12494             Roo.each(name.split('.'), function(st) {
12495                 lookfor += (lookfor.length ? '.': '') + st;
12496                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12497             });
12498             
12499             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12500             
12501             
12502             if(format && useF){
12503                 
12504                 args = args ? ',' + args : "";
12505                  
12506                 if(format.substr(0, 5) != "this."){
12507                     format = "fm." + format + '(';
12508                 }else{
12509                     format = 'this.call("'+ format.substr(5) + '", ';
12510                     args = ", values";
12511                 }
12512                 
12513                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12514             }
12515              
12516             if (args && args.length) {
12517                 // called with xxyx.yuu:(test,test)
12518                 // change to ()
12519                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12520             }
12521             // raw.. - :raw modifier..
12522             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12523             
12524         };
12525         var body;
12526         // branched to use + in gecko and [].join() in others
12527         if(Roo.isGecko){
12528             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12529                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12530                     "';};};";
12531         }else{
12532             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12533             body.push(tpl.body.replace(/(\r\n|\n)/g,
12534                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12535             body.push("'].join('');};};");
12536             body = body.join('');
12537         }
12538         
12539         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12540        
12541         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12542         eval(body);
12543         
12544         return this;
12545     },
12546      
12547     /**
12548      * same as applyTemplate, except it's done to one of the subTemplates
12549      * when using named templates, you can do:
12550      *
12551      * var str = pl.applySubTemplate('your-name', values);
12552      *
12553      * 
12554      * @param {Number} id of the template
12555      * @param {Object} values to apply to template
12556      * @param {Object} parent (normaly the instance of this object)
12557      */
12558     applySubTemplate : function(id, values, parent)
12559     {
12560         
12561         
12562         var t = this.tpls[id];
12563         
12564         
12565         try { 
12566             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12567                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12568                 return '';
12569             }
12570         } catch(e) {
12571             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12572             Roo.log(values);
12573           
12574             return '';
12575         }
12576         try { 
12577             
12578             if(t.execCall && t.execCall.call(this, values, parent)){
12579                 return '';
12580             }
12581         } catch(e) {
12582             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12583             Roo.log(values);
12584             return '';
12585         }
12586         
12587         try {
12588             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12589             parent = t.target ? values : parent;
12590             if(t.forCall && vs instanceof Array){
12591                 var buf = [];
12592                 for(var i = 0, len = vs.length; i < len; i++){
12593                     try {
12594                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12595                     } catch (e) {
12596                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12597                         Roo.log(e.body);
12598                         //Roo.log(t.compiled);
12599                         Roo.log(vs[i]);
12600                     }   
12601                 }
12602                 return buf.join('');
12603             }
12604         } catch (e) {
12605             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12606             Roo.log(values);
12607             return '';
12608         }
12609         try {
12610             return t.compiled.call(this, vs, parent);
12611         } catch (e) {
12612             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12613             Roo.log(e.body);
12614             //Roo.log(t.compiled);
12615             Roo.log(values);
12616             return '';
12617         }
12618     },
12619
12620    
12621
12622     applyTemplate : function(values){
12623         return this.master.compiled.call(this, values, {});
12624         //var s = this.subs;
12625     },
12626
12627     apply : function(){
12628         return this.applyTemplate.apply(this, arguments);
12629     }
12630
12631  });
12632
12633 Roo.DomTemplate.from = function(el){
12634     el = Roo.getDom(el);
12635     return new Roo.Domtemplate(el.value || el.innerHTML);
12636 };/*
12637  * Based on:
12638  * Ext JS Library 1.1.1
12639  * Copyright(c) 2006-2007, Ext JS, LLC.
12640  *
12641  * Originally Released Under LGPL - original licence link has changed is not relivant.
12642  *
12643  * Fork - LGPL
12644  * <script type="text/javascript">
12645  */
12646
12647 /**
12648  * @class Roo.util.DelayedTask
12649  * Provides a convenient method of performing setTimeout where a new
12650  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12651  * You can use this class to buffer
12652  * the keypress events for a certain number of milliseconds, and perform only if they stop
12653  * for that amount of time.
12654  * @constructor The parameters to this constructor serve as defaults and are not required.
12655  * @param {Function} fn (optional) The default function to timeout
12656  * @param {Object} scope (optional) The default scope of that timeout
12657  * @param {Array} args (optional) The default Array of arguments
12658  */
12659 Roo.util.DelayedTask = function(fn, scope, args){
12660     var id = null, d, t;
12661
12662     var call = function(){
12663         var now = new Date().getTime();
12664         if(now - t >= d){
12665             clearInterval(id);
12666             id = null;
12667             fn.apply(scope, args || []);
12668         }
12669     };
12670     /**
12671      * Cancels any pending timeout and queues a new one
12672      * @param {Number} delay The milliseconds to delay
12673      * @param {Function} newFn (optional) Overrides function passed to constructor
12674      * @param {Object} newScope (optional) Overrides scope passed to constructor
12675      * @param {Array} newArgs (optional) Overrides args passed to constructor
12676      */
12677     this.delay = function(delay, newFn, newScope, newArgs){
12678         if(id && delay != d){
12679             this.cancel();
12680         }
12681         d = delay;
12682         t = new Date().getTime();
12683         fn = newFn || fn;
12684         scope = newScope || scope;
12685         args = newArgs || args;
12686         if(!id){
12687             id = setInterval(call, d);
12688         }
12689     };
12690
12691     /**
12692      * Cancel the last queued timeout
12693      */
12694     this.cancel = function(){
12695         if(id){
12696             clearInterval(id);
12697             id = null;
12698         }
12699     };
12700 };/*
12701  * Based on:
12702  * Ext JS Library 1.1.1
12703  * Copyright(c) 2006-2007, Ext JS, LLC.
12704  *
12705  * Originally Released Under LGPL - original licence link has changed is not relivant.
12706  *
12707  * Fork - LGPL
12708  * <script type="text/javascript">
12709  */
12710  
12711  
12712 Roo.util.TaskRunner = function(interval){
12713     interval = interval || 10;
12714     var tasks = [], removeQueue = [];
12715     var id = 0;
12716     var running = false;
12717
12718     var stopThread = function(){
12719         running = false;
12720         clearInterval(id);
12721         id = 0;
12722     };
12723
12724     var startThread = function(){
12725         if(!running){
12726             running = true;
12727             id = setInterval(runTasks, interval);
12728         }
12729     };
12730
12731     var removeTask = function(task){
12732         removeQueue.push(task);
12733         if(task.onStop){
12734             task.onStop();
12735         }
12736     };
12737
12738     var runTasks = function(){
12739         if(removeQueue.length > 0){
12740             for(var i = 0, len = removeQueue.length; i < len; i++){
12741                 tasks.remove(removeQueue[i]);
12742             }
12743             removeQueue = [];
12744             if(tasks.length < 1){
12745                 stopThread();
12746                 return;
12747             }
12748         }
12749         var now = new Date().getTime();
12750         for(var i = 0, len = tasks.length; i < len; ++i){
12751             var t = tasks[i];
12752             var itime = now - t.taskRunTime;
12753             if(t.interval <= itime){
12754                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12755                 t.taskRunTime = now;
12756                 if(rt === false || t.taskRunCount === t.repeat){
12757                     removeTask(t);
12758                     return;
12759                 }
12760             }
12761             if(t.duration && t.duration <= (now - t.taskStartTime)){
12762                 removeTask(t);
12763             }
12764         }
12765     };
12766
12767     /**
12768      * Queues a new task.
12769      * @param {Object} task
12770      */
12771     this.start = function(task){
12772         tasks.push(task);
12773         task.taskStartTime = new Date().getTime();
12774         task.taskRunTime = 0;
12775         task.taskRunCount = 0;
12776         startThread();
12777         return task;
12778     };
12779
12780     this.stop = function(task){
12781         removeTask(task);
12782         return task;
12783     };
12784
12785     this.stopAll = function(){
12786         stopThread();
12787         for(var i = 0, len = tasks.length; i < len; i++){
12788             if(tasks[i].onStop){
12789                 tasks[i].onStop();
12790             }
12791         }
12792         tasks = [];
12793         removeQueue = [];
12794     };
12795 };
12796
12797 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12798  * Based on:
12799  * Ext JS Library 1.1.1
12800  * Copyright(c) 2006-2007, Ext JS, LLC.
12801  *
12802  * Originally Released Under LGPL - original licence link has changed is not relivant.
12803  *
12804  * Fork - LGPL
12805  * <script type="text/javascript">
12806  */
12807
12808  
12809 /**
12810  * @class Roo.util.MixedCollection
12811  * @extends Roo.util.Observable
12812  * A Collection class that maintains both numeric indexes and keys and exposes events.
12813  * @constructor
12814  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12815  * collection (defaults to false)
12816  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12817  * and return the key value for that item.  This is used when available to look up the key on items that
12818  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12819  * equivalent to providing an implementation for the {@link #getKey} method.
12820  */
12821 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12822     this.items = [];
12823     this.map = {};
12824     this.keys = [];
12825     this.length = 0;
12826     this.addEvents({
12827         /**
12828          * @event clear
12829          * Fires when the collection is cleared.
12830          */
12831         "clear" : true,
12832         /**
12833          * @event add
12834          * Fires when an item is added to the collection.
12835          * @param {Number} index The index at which the item was added.
12836          * @param {Object} o The item added.
12837          * @param {String} key The key associated with the added item.
12838          */
12839         "add" : true,
12840         /**
12841          * @event replace
12842          * Fires when an item is replaced in the collection.
12843          * @param {String} key he key associated with the new added.
12844          * @param {Object} old The item being replaced.
12845          * @param {Object} new The new item.
12846          */
12847         "replace" : true,
12848         /**
12849          * @event remove
12850          * Fires when an item is removed from the collection.
12851          * @param {Object} o The item being removed.
12852          * @param {String} key (optional) The key associated with the removed item.
12853          */
12854         "remove" : true,
12855         "sort" : true
12856     });
12857     this.allowFunctions = allowFunctions === true;
12858     if(keyFn){
12859         this.getKey = keyFn;
12860     }
12861     Roo.util.MixedCollection.superclass.constructor.call(this);
12862 };
12863
12864 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12865     allowFunctions : false,
12866     
12867 /**
12868  * Adds an item to the collection.
12869  * @param {String} key The key to associate with the item
12870  * @param {Object} o The item to add.
12871  * @return {Object} The item added.
12872  */
12873     add : function(key, o){
12874         if(arguments.length == 1){
12875             o = arguments[0];
12876             key = this.getKey(o);
12877         }
12878         if(typeof key == "undefined" || key === null){
12879             this.length++;
12880             this.items.push(o);
12881             this.keys.push(null);
12882         }else{
12883             var old = this.map[key];
12884             if(old){
12885                 return this.replace(key, o);
12886             }
12887             this.length++;
12888             this.items.push(o);
12889             this.map[key] = o;
12890             this.keys.push(key);
12891         }
12892         this.fireEvent("add", this.length-1, o, key);
12893         return o;
12894     },
12895        
12896 /**
12897   * MixedCollection has a generic way to fetch keys if you implement getKey.
12898 <pre><code>
12899 // normal way
12900 var mc = new Roo.util.MixedCollection();
12901 mc.add(someEl.dom.id, someEl);
12902 mc.add(otherEl.dom.id, otherEl);
12903 //and so on
12904
12905 // using getKey
12906 var mc = new Roo.util.MixedCollection();
12907 mc.getKey = function(el){
12908    return el.dom.id;
12909 };
12910 mc.add(someEl);
12911 mc.add(otherEl);
12912
12913 // or via the constructor
12914 var mc = new Roo.util.MixedCollection(false, function(el){
12915    return el.dom.id;
12916 });
12917 mc.add(someEl);
12918 mc.add(otherEl);
12919 </code></pre>
12920  * @param o {Object} The item for which to find the key.
12921  * @return {Object} The key for the passed item.
12922  */
12923     getKey : function(o){
12924          return o.id; 
12925     },
12926    
12927 /**
12928  * Replaces an item in the collection.
12929  * @param {String} key The key associated with the item to replace, or the item to replace.
12930  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12931  * @return {Object}  The new item.
12932  */
12933     replace : function(key, o){
12934         if(arguments.length == 1){
12935             o = arguments[0];
12936             key = this.getKey(o);
12937         }
12938         var old = this.item(key);
12939         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12940              return this.add(key, o);
12941         }
12942         var index = this.indexOfKey(key);
12943         this.items[index] = o;
12944         this.map[key] = o;
12945         this.fireEvent("replace", key, old, o);
12946         return o;
12947     },
12948    
12949 /**
12950  * Adds all elements of an Array or an Object to the collection.
12951  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12952  * an Array of values, each of which are added to the collection.
12953  */
12954     addAll : function(objs){
12955         if(arguments.length > 1 || objs instanceof Array){
12956             var args = arguments.length > 1 ? arguments : objs;
12957             for(var i = 0, len = args.length; i < len; i++){
12958                 this.add(args[i]);
12959             }
12960         }else{
12961             for(var key in objs){
12962                 if(this.allowFunctions || typeof objs[key] != "function"){
12963                     this.add(key, objs[key]);
12964                 }
12965             }
12966         }
12967     },
12968    
12969 /**
12970  * Executes the specified function once for every item in the collection, passing each
12971  * item as the first and only parameter. returning false from the function will stop the iteration.
12972  * @param {Function} fn The function to execute for each item.
12973  * @param {Object} scope (optional) The scope in which to execute the function.
12974  */
12975     each : function(fn, scope){
12976         var items = [].concat(this.items); // each safe for removal
12977         for(var i = 0, len = items.length; i < len; i++){
12978             if(fn.call(scope || items[i], items[i], i, len) === false){
12979                 break;
12980             }
12981         }
12982     },
12983    
12984 /**
12985  * Executes the specified function once for every key in the collection, passing each
12986  * key, and its associated item as the first two parameters.
12987  * @param {Function} fn The function to execute for each item.
12988  * @param {Object} scope (optional) The scope in which to execute the function.
12989  */
12990     eachKey : function(fn, scope){
12991         for(var i = 0, len = this.keys.length; i < len; i++){
12992             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12993         }
12994     },
12995    
12996 /**
12997  * Returns the first item in the collection which elicits a true return value from the
12998  * passed selection function.
12999  * @param {Function} fn The selection function to execute for each item.
13000  * @param {Object} scope (optional) The scope in which to execute the function.
13001  * @return {Object} The first item in the collection which returned true from the selection function.
13002  */
13003     find : function(fn, scope){
13004         for(var i = 0, len = this.items.length; i < len; i++){
13005             if(fn.call(scope || window, this.items[i], this.keys[i])){
13006                 return this.items[i];
13007             }
13008         }
13009         return null;
13010     },
13011    
13012 /**
13013  * Inserts an item at the specified index in the collection.
13014  * @param {Number} index The index to insert the item at.
13015  * @param {String} key The key to associate with the new item, or the item itself.
13016  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13017  * @return {Object} The item inserted.
13018  */
13019     insert : function(index, key, o){
13020         if(arguments.length == 2){
13021             o = arguments[1];
13022             key = this.getKey(o);
13023         }
13024         if(index >= this.length){
13025             return this.add(key, o);
13026         }
13027         this.length++;
13028         this.items.splice(index, 0, o);
13029         if(typeof key != "undefined" && key != null){
13030             this.map[key] = o;
13031         }
13032         this.keys.splice(index, 0, key);
13033         this.fireEvent("add", index, o, key);
13034         return o;
13035     },
13036    
13037 /**
13038  * Removed an item from the collection.
13039  * @param {Object} o The item to remove.
13040  * @return {Object} The item removed.
13041  */
13042     remove : function(o){
13043         return this.removeAt(this.indexOf(o));
13044     },
13045    
13046 /**
13047  * Remove an item from a specified index in the collection.
13048  * @param {Number} index The index within the collection of the item to remove.
13049  */
13050     removeAt : function(index){
13051         if(index < this.length && index >= 0){
13052             this.length--;
13053             var o = this.items[index];
13054             this.items.splice(index, 1);
13055             var key = this.keys[index];
13056             if(typeof key != "undefined"){
13057                 delete this.map[key];
13058             }
13059             this.keys.splice(index, 1);
13060             this.fireEvent("remove", o, key);
13061         }
13062     },
13063    
13064 /**
13065  * Removed an item associated with the passed key fom the collection.
13066  * @param {String} key The key of the item to remove.
13067  */
13068     removeKey : function(key){
13069         return this.removeAt(this.indexOfKey(key));
13070     },
13071    
13072 /**
13073  * Returns the number of items in the collection.
13074  * @return {Number} the number of items in the collection.
13075  */
13076     getCount : function(){
13077         return this.length; 
13078     },
13079    
13080 /**
13081  * Returns index within the collection of the passed Object.
13082  * @param {Object} o The item to find the index of.
13083  * @return {Number} index of the item.
13084  */
13085     indexOf : function(o){
13086         if(!this.items.indexOf){
13087             for(var i = 0, len = this.items.length; i < len; i++){
13088                 if(this.items[i] == o) return i;
13089             }
13090             return -1;
13091         }else{
13092             return this.items.indexOf(o);
13093         }
13094     },
13095    
13096 /**
13097  * Returns index within the collection of the passed key.
13098  * @param {String} key The key to find the index of.
13099  * @return {Number} index of the key.
13100  */
13101     indexOfKey : function(key){
13102         if(!this.keys.indexOf){
13103             for(var i = 0, len = this.keys.length; i < len; i++){
13104                 if(this.keys[i] == key) return i;
13105             }
13106             return -1;
13107         }else{
13108             return this.keys.indexOf(key);
13109         }
13110     },
13111    
13112 /**
13113  * Returns the item associated with the passed key OR index. Key has priority over index.
13114  * @param {String/Number} key The key or index of the item.
13115  * @return {Object} The item associated with the passed key.
13116  */
13117     item : function(key){
13118         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13119         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13120     },
13121     
13122 /**
13123  * Returns the item at the specified index.
13124  * @param {Number} index The index of the item.
13125  * @return {Object}
13126  */
13127     itemAt : function(index){
13128         return this.items[index];
13129     },
13130     
13131 /**
13132  * Returns the item associated with the passed key.
13133  * @param {String/Number} key The key of the item.
13134  * @return {Object} The item associated with the passed key.
13135  */
13136     key : function(key){
13137         return this.map[key];
13138     },
13139    
13140 /**
13141  * Returns true if the collection contains the passed Object as an item.
13142  * @param {Object} o  The Object to look for in the collection.
13143  * @return {Boolean} True if the collection contains the Object as an item.
13144  */
13145     contains : function(o){
13146         return this.indexOf(o) != -1;
13147     },
13148    
13149 /**
13150  * Returns true if the collection contains the passed Object as a key.
13151  * @param {String} key The key to look for in the collection.
13152  * @return {Boolean} True if the collection contains the Object as a key.
13153  */
13154     containsKey : function(key){
13155         return typeof this.map[key] != "undefined";
13156     },
13157    
13158 /**
13159  * Removes all items from the collection.
13160  */
13161     clear : function(){
13162         this.length = 0;
13163         this.items = [];
13164         this.keys = [];
13165         this.map = {};
13166         this.fireEvent("clear");
13167     },
13168    
13169 /**
13170  * Returns the first item in the collection.
13171  * @return {Object} the first item in the collection..
13172  */
13173     first : function(){
13174         return this.items[0]; 
13175     },
13176    
13177 /**
13178  * Returns the last item in the collection.
13179  * @return {Object} the last item in the collection..
13180  */
13181     last : function(){
13182         return this.items[this.length-1];   
13183     },
13184     
13185     _sort : function(property, dir, fn){
13186         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13187         fn = fn || function(a, b){
13188             return a-b;
13189         };
13190         var c = [], k = this.keys, items = this.items;
13191         for(var i = 0, len = items.length; i < len; i++){
13192             c[c.length] = {key: k[i], value: items[i], index: i};
13193         }
13194         c.sort(function(a, b){
13195             var v = fn(a[property], b[property]) * dsc;
13196             if(v == 0){
13197                 v = (a.index < b.index ? -1 : 1);
13198             }
13199             return v;
13200         });
13201         for(var i = 0, len = c.length; i < len; i++){
13202             items[i] = c[i].value;
13203             k[i] = c[i].key;
13204         }
13205         this.fireEvent("sort", this);
13206     },
13207     
13208     /**
13209      * Sorts this collection with the passed comparison function
13210      * @param {String} direction (optional) "ASC" or "DESC"
13211      * @param {Function} fn (optional) comparison function
13212      */
13213     sort : function(dir, fn){
13214         this._sort("value", dir, fn);
13215     },
13216     
13217     /**
13218      * Sorts this collection by keys
13219      * @param {String} direction (optional) "ASC" or "DESC"
13220      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13221      */
13222     keySort : function(dir, fn){
13223         this._sort("key", dir, fn || function(a, b){
13224             return String(a).toUpperCase()-String(b).toUpperCase();
13225         });
13226     },
13227     
13228     /**
13229      * Returns a range of items in this collection
13230      * @param {Number} startIndex (optional) defaults to 0
13231      * @param {Number} endIndex (optional) default to the last item
13232      * @return {Array} An array of items
13233      */
13234     getRange : function(start, end){
13235         var items = this.items;
13236         if(items.length < 1){
13237             return [];
13238         }
13239         start = start || 0;
13240         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13241         var r = [];
13242         if(start <= end){
13243             for(var i = start; i <= end; i++) {
13244                     r[r.length] = items[i];
13245             }
13246         }else{
13247             for(var i = start; i >= end; i--) {
13248                     r[r.length] = items[i];
13249             }
13250         }
13251         return r;
13252     },
13253         
13254     /**
13255      * Filter the <i>objects</i> in this collection by a specific property. 
13256      * Returns a new collection that has been filtered.
13257      * @param {String} property A property on your objects
13258      * @param {String/RegExp} value Either string that the property values 
13259      * should start with or a RegExp to test against the property
13260      * @return {MixedCollection} The new filtered collection
13261      */
13262     filter : function(property, value){
13263         if(!value.exec){ // not a regex
13264             value = String(value);
13265             if(value.length == 0){
13266                 return this.clone();
13267             }
13268             value = new RegExp("^" + Roo.escapeRe(value), "i");
13269         }
13270         return this.filterBy(function(o){
13271             return o && value.test(o[property]);
13272         });
13273         },
13274     
13275     /**
13276      * Filter by a function. * Returns a new collection that has been filtered.
13277      * The passed function will be called with each 
13278      * object in the collection. If the function returns true, the value is included 
13279      * otherwise it is filtered.
13280      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13281      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13282      * @return {MixedCollection} The new filtered collection
13283      */
13284     filterBy : function(fn, scope){
13285         var r = new Roo.util.MixedCollection();
13286         r.getKey = this.getKey;
13287         var k = this.keys, it = this.items;
13288         for(var i = 0, len = it.length; i < len; i++){
13289             if(fn.call(scope||this, it[i], k[i])){
13290                                 r.add(k[i], it[i]);
13291                         }
13292         }
13293         return r;
13294     },
13295     
13296     /**
13297      * Creates a duplicate of this collection
13298      * @return {MixedCollection}
13299      */
13300     clone : function(){
13301         var r = new Roo.util.MixedCollection();
13302         var k = this.keys, it = this.items;
13303         for(var i = 0, len = it.length; i < len; i++){
13304             r.add(k[i], it[i]);
13305         }
13306         r.getKey = this.getKey;
13307         return r;
13308     }
13309 });
13310 /**
13311  * Returns the item associated with the passed key or index.
13312  * @method
13313  * @param {String/Number} key The key or index of the item.
13314  * @return {Object} The item associated with the passed key.
13315  */
13316 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13317  * Based on:
13318  * Ext JS Library 1.1.1
13319  * Copyright(c) 2006-2007, Ext JS, LLC.
13320  *
13321  * Originally Released Under LGPL - original licence link has changed is not relivant.
13322  *
13323  * Fork - LGPL
13324  * <script type="text/javascript">
13325  */
13326 /**
13327  * @class Roo.util.JSON
13328  * Modified version of Douglas Crockford"s json.js that doesn"t
13329  * mess with the Object prototype 
13330  * http://www.json.org/js.html
13331  * @singleton
13332  */
13333 Roo.util.JSON = new (function(){
13334     var useHasOwn = {}.hasOwnProperty ? true : false;
13335     
13336     // crashes Safari in some instances
13337     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13338     
13339     var pad = function(n) {
13340         return n < 10 ? "0" + n : n;
13341     };
13342     
13343     var m = {
13344         "\b": '\\b',
13345         "\t": '\\t',
13346         "\n": '\\n',
13347         "\f": '\\f',
13348         "\r": '\\r',
13349         '"' : '\\"',
13350         "\\": '\\\\'
13351     };
13352
13353     var encodeString = function(s){
13354         if (/["\\\x00-\x1f]/.test(s)) {
13355             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13356                 var c = m[b];
13357                 if(c){
13358                     return c;
13359                 }
13360                 c = b.charCodeAt();
13361                 return "\\u00" +
13362                     Math.floor(c / 16).toString(16) +
13363                     (c % 16).toString(16);
13364             }) + '"';
13365         }
13366         return '"' + s + '"';
13367     };
13368     
13369     var encodeArray = function(o){
13370         var a = ["["], b, i, l = o.length, v;
13371             for (i = 0; i < l; i += 1) {
13372                 v = o[i];
13373                 switch (typeof v) {
13374                     case "undefined":
13375                     case "function":
13376                     case "unknown":
13377                         break;
13378                     default:
13379                         if (b) {
13380                             a.push(',');
13381                         }
13382                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13383                         b = true;
13384                 }
13385             }
13386             a.push("]");
13387             return a.join("");
13388     };
13389     
13390     var encodeDate = function(o){
13391         return '"' + o.getFullYear() + "-" +
13392                 pad(o.getMonth() + 1) + "-" +
13393                 pad(o.getDate()) + "T" +
13394                 pad(o.getHours()) + ":" +
13395                 pad(o.getMinutes()) + ":" +
13396                 pad(o.getSeconds()) + '"';
13397     };
13398     
13399     /**
13400      * Encodes an Object, Array or other value
13401      * @param {Mixed} o The variable to encode
13402      * @return {String} The JSON string
13403      */
13404     this.encode = function(o)
13405     {
13406         // should this be extended to fully wrap stringify..
13407         
13408         if(typeof o == "undefined" || o === null){
13409             return "null";
13410         }else if(o instanceof Array){
13411             return encodeArray(o);
13412         }else if(o instanceof Date){
13413             return encodeDate(o);
13414         }else if(typeof o == "string"){
13415             return encodeString(o);
13416         }else if(typeof o == "number"){
13417             return isFinite(o) ? String(o) : "null";
13418         }else if(typeof o == "boolean"){
13419             return String(o);
13420         }else {
13421             var a = ["{"], b, i, v;
13422             for (i in o) {
13423                 if(!useHasOwn || o.hasOwnProperty(i)) {
13424                     v = o[i];
13425                     switch (typeof v) {
13426                     case "undefined":
13427                     case "function":
13428                     case "unknown":
13429                         break;
13430                     default:
13431                         if(b){
13432                             a.push(',');
13433                         }
13434                         a.push(this.encode(i), ":",
13435                                 v === null ? "null" : this.encode(v));
13436                         b = true;
13437                     }
13438                 }
13439             }
13440             a.push("}");
13441             return a.join("");
13442         }
13443     };
13444     
13445     /**
13446      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13447      * @param {String} json The JSON string
13448      * @return {Object} The resulting object
13449      */
13450     this.decode = function(json){
13451         
13452         return  /** eval:var:json */ eval("(" + json + ')');
13453     };
13454 })();
13455 /** 
13456  * Shorthand for {@link Roo.util.JSON#encode}
13457  * @member Roo encode 
13458  * @method */
13459 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13460 /** 
13461  * Shorthand for {@link Roo.util.JSON#decode}
13462  * @member Roo decode 
13463  * @method */
13464 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13465 /*
13466  * Based on:
13467  * Ext JS Library 1.1.1
13468  * Copyright(c) 2006-2007, Ext JS, LLC.
13469  *
13470  * Originally Released Under LGPL - original licence link has changed is not relivant.
13471  *
13472  * Fork - LGPL
13473  * <script type="text/javascript">
13474  */
13475  
13476 /**
13477  * @class Roo.util.Format
13478  * Reusable data formatting functions
13479  * @singleton
13480  */
13481 Roo.util.Format = function(){
13482     var trimRe = /^\s+|\s+$/g;
13483     return {
13484         /**
13485          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13486          * @param {String} value The string to truncate
13487          * @param {Number} length The maximum length to allow before truncating
13488          * @return {String} The converted text
13489          */
13490         ellipsis : function(value, len){
13491             if(value && value.length > len){
13492                 return value.substr(0, len-3)+"...";
13493             }
13494             return value;
13495         },
13496
13497         /**
13498          * Checks a reference and converts it to empty string if it is undefined
13499          * @param {Mixed} value Reference to check
13500          * @return {Mixed} Empty string if converted, otherwise the original value
13501          */
13502         undef : function(value){
13503             return typeof value != "undefined" ? value : "";
13504         },
13505
13506         /**
13507          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13508          * @param {String} value The string to encode
13509          * @return {String} The encoded text
13510          */
13511         htmlEncode : function(value){
13512             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13513         },
13514
13515         /**
13516          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13517          * @param {String} value The string to decode
13518          * @return {String} The decoded text
13519          */
13520         htmlDecode : function(value){
13521             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13522         },
13523
13524         /**
13525          * Trims any whitespace from either side of a string
13526          * @param {String} value The text to trim
13527          * @return {String} The trimmed text
13528          */
13529         trim : function(value){
13530             return String(value).replace(trimRe, "");
13531         },
13532
13533         /**
13534          * Returns a substring from within an original string
13535          * @param {String} value The original text
13536          * @param {Number} start The start index of the substring
13537          * @param {Number} length The length of the substring
13538          * @return {String} The substring
13539          */
13540         substr : function(value, start, length){
13541             return String(value).substr(start, length);
13542         },
13543
13544         /**
13545          * Converts a string to all lower case letters
13546          * @param {String} value The text to convert
13547          * @return {String} The converted text
13548          */
13549         lowercase : function(value){
13550             return String(value).toLowerCase();
13551         },
13552
13553         /**
13554          * Converts a string to all upper case letters
13555          * @param {String} value The text to convert
13556          * @return {String} The converted text
13557          */
13558         uppercase : function(value){
13559             return String(value).toUpperCase();
13560         },
13561
13562         /**
13563          * Converts the first character only of a string to upper case
13564          * @param {String} value The text to convert
13565          * @return {String} The converted text
13566          */
13567         capitalize : function(value){
13568             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13569         },
13570
13571         // private
13572         call : function(value, fn){
13573             if(arguments.length > 2){
13574                 var args = Array.prototype.slice.call(arguments, 2);
13575                 args.unshift(value);
13576                  
13577                 return /** eval:var:value */  eval(fn).apply(window, args);
13578             }else{
13579                 /** eval:var:value */
13580                 return /** eval:var:value */ eval(fn).call(window, value);
13581             }
13582         },
13583
13584        
13585         /**
13586          * safer version of Math.toFixed..??/
13587          * @param {Number/String} value The numeric value to format
13588          * @param {Number/String} value Decimal places 
13589          * @return {String} The formatted currency string
13590          */
13591         toFixed : function(v, n)
13592         {
13593             // why not use to fixed - precision is buggered???
13594             if (!n) {
13595                 return Math.round(v-0);
13596             }
13597             var fact = Math.pow(10,n+1);
13598             v = (Math.round((v-0)*fact))/fact;
13599             var z = (''+fact).substring(2);
13600             if (v == Math.floor(v)) {
13601                 return Math.floor(v) + '.' + z;
13602             }
13603             
13604             // now just padd decimals..
13605             var ps = String(v).split('.');
13606             var fd = (ps[1] + z);
13607             var r = fd.substring(0,n); 
13608             var rm = fd.substring(n); 
13609             if (rm < 5) {
13610                 return ps[0] + '.' + r;
13611             }
13612             r*=1; // turn it into a number;
13613             r++;
13614             if (String(r).length != n) {
13615                 ps[0]*=1;
13616                 ps[0]++;
13617                 r = String(r).substring(1); // chop the end off.
13618             }
13619             
13620             return ps[0] + '.' + r;
13621              
13622         },
13623         
13624         /**
13625          * Format a number as US currency
13626          * @param {Number/String} value The numeric value to format
13627          * @return {String} The formatted currency string
13628          */
13629         usMoney : function(v){
13630             return '$' + Roo.util.Format.number(v);
13631         },
13632         
13633         /**
13634          * Format a number
13635          * eventually this should probably emulate php's number_format
13636          * @param {Number/String} value The numeric value to format
13637          * @param {Number} decimals number of decimal places
13638          * @return {String} The formatted currency string
13639          */
13640         number : function(v,decimals)
13641         {
13642             // multiply and round.
13643             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13644             var mul = Math.pow(10, decimals);
13645             var zero = String(mul).substring(1);
13646             v = (Math.round((v-0)*mul))/mul;
13647             
13648             // if it's '0' number.. then
13649             
13650             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13651             v = String(v);
13652             var ps = v.split('.');
13653             var whole = ps[0];
13654             
13655             
13656             var r = /(\d+)(\d{3})/;
13657             // add comma's
13658             while (r.test(whole)) {
13659                 whole = whole.replace(r, '$1' + ',' + '$2');
13660             }
13661             
13662             
13663             var sub = ps[1] ?
13664                     // has decimals..
13665                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13666                     // does not have decimals
13667                     (decimals ? ('.' + zero) : '');
13668             
13669             
13670             return whole + sub ;
13671         },
13672         
13673         /**
13674          * Parse a value into a formatted date using the specified format pattern.
13675          * @param {Mixed} value The value to format
13676          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13677          * @return {String} The formatted date string
13678          */
13679         date : function(v, format){
13680             if(!v){
13681                 return "";
13682             }
13683             if(!(v instanceof Date)){
13684                 v = new Date(Date.parse(v));
13685             }
13686             return v.dateFormat(format || Roo.util.Format.defaults.date);
13687         },
13688
13689         /**
13690          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13691          * @param {String} format Any valid date format string
13692          * @return {Function} The date formatting function
13693          */
13694         dateRenderer : function(format){
13695             return function(v){
13696                 return Roo.util.Format.date(v, format);  
13697             };
13698         },
13699
13700         // private
13701         stripTagsRE : /<\/?[^>]+>/gi,
13702         
13703         /**
13704          * Strips all HTML tags
13705          * @param {Mixed} value The text from which to strip tags
13706          * @return {String} The stripped text
13707          */
13708         stripTags : function(v){
13709             return !v ? v : String(v).replace(this.stripTagsRE, "");
13710         }
13711     };
13712 }();
13713 Roo.util.Format.defaults = {
13714     date : 'd/M/Y'
13715 };/*
13716  * Based on:
13717  * Ext JS Library 1.1.1
13718  * Copyright(c) 2006-2007, Ext JS, LLC.
13719  *
13720  * Originally Released Under LGPL - original licence link has changed is not relivant.
13721  *
13722  * Fork - LGPL
13723  * <script type="text/javascript">
13724  */
13725
13726
13727  
13728
13729 /**
13730  * @class Roo.MasterTemplate
13731  * @extends Roo.Template
13732  * Provides a template that can have child templates. The syntax is:
13733 <pre><code>
13734 var t = new Roo.MasterTemplate(
13735         '&lt;select name="{name}"&gt;',
13736                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13737         '&lt;/select&gt;'
13738 );
13739 t.add('options', {value: 'foo', text: 'bar'});
13740 // or you can add multiple child elements in one shot
13741 t.addAll('options', [
13742     {value: 'foo', text: 'bar'},
13743     {value: 'foo2', text: 'bar2'},
13744     {value: 'foo3', text: 'bar3'}
13745 ]);
13746 // then append, applying the master template values
13747 t.append('my-form', {name: 'my-select'});
13748 </code></pre>
13749 * A name attribute for the child template is not required if you have only one child
13750 * template or you want to refer to them by index.
13751  */
13752 Roo.MasterTemplate = function(){
13753     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13754     this.originalHtml = this.html;
13755     var st = {};
13756     var m, re = this.subTemplateRe;
13757     re.lastIndex = 0;
13758     var subIndex = 0;
13759     while(m = re.exec(this.html)){
13760         var name = m[1], content = m[2];
13761         st[subIndex] = {
13762             name: name,
13763             index: subIndex,
13764             buffer: [],
13765             tpl : new Roo.Template(content)
13766         };
13767         if(name){
13768             st[name] = st[subIndex];
13769         }
13770         st[subIndex].tpl.compile();
13771         st[subIndex].tpl.call = this.call.createDelegate(this);
13772         subIndex++;
13773     }
13774     this.subCount = subIndex;
13775     this.subs = st;
13776 };
13777 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13778     /**
13779     * The regular expression used to match sub templates
13780     * @type RegExp
13781     * @property
13782     */
13783     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13784
13785     /**
13786      * Applies the passed values to a child template.
13787      * @param {String/Number} name (optional) The name or index of the child template
13788      * @param {Array/Object} values The values to be applied to the template
13789      * @return {MasterTemplate} this
13790      */
13791      add : function(name, values){
13792         if(arguments.length == 1){
13793             values = arguments[0];
13794             name = 0;
13795         }
13796         var s = this.subs[name];
13797         s.buffer[s.buffer.length] = s.tpl.apply(values);
13798         return this;
13799     },
13800
13801     /**
13802      * Applies all the passed values to a child template.
13803      * @param {String/Number} name (optional) The name or index of the child template
13804      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13805      * @param {Boolean} reset (optional) True to reset the template first
13806      * @return {MasterTemplate} this
13807      */
13808     fill : function(name, values, reset){
13809         var a = arguments;
13810         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13811             values = a[0];
13812             name = 0;
13813             reset = a[1];
13814         }
13815         if(reset){
13816             this.reset();
13817         }
13818         for(var i = 0, len = values.length; i < len; i++){
13819             this.add(name, values[i]);
13820         }
13821         return this;
13822     },
13823
13824     /**
13825      * Resets the template for reuse
13826      * @return {MasterTemplate} this
13827      */
13828      reset : function(){
13829         var s = this.subs;
13830         for(var i = 0; i < this.subCount; i++){
13831             s[i].buffer = [];
13832         }
13833         return this;
13834     },
13835
13836     applyTemplate : function(values){
13837         var s = this.subs;
13838         var replaceIndex = -1;
13839         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13840             return s[++replaceIndex].buffer.join("");
13841         });
13842         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13843     },
13844
13845     apply : function(){
13846         return this.applyTemplate.apply(this, arguments);
13847     },
13848
13849     compile : function(){return this;}
13850 });
13851
13852 /**
13853  * Alias for fill().
13854  * @method
13855  */
13856 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13857  /**
13858  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13859  * var tpl = Roo.MasterTemplate.from('element-id');
13860  * @param {String/HTMLElement} el
13861  * @param {Object} config
13862  * @static
13863  */
13864 Roo.MasterTemplate.from = function(el, config){
13865     el = Roo.getDom(el);
13866     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13867 };/*
13868  * Based on:
13869  * Ext JS Library 1.1.1
13870  * Copyright(c) 2006-2007, Ext JS, LLC.
13871  *
13872  * Originally Released Under LGPL - original licence link has changed is not relivant.
13873  *
13874  * Fork - LGPL
13875  * <script type="text/javascript">
13876  */
13877
13878  
13879 /**
13880  * @class Roo.util.CSS
13881  * Utility class for manipulating CSS rules
13882  * @singleton
13883  */
13884 Roo.util.CSS = function(){
13885         var rules = null;
13886         var doc = document;
13887
13888     var camelRe = /(-[a-z])/gi;
13889     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13890
13891    return {
13892    /**
13893     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13894     * tag and appended to the HEAD of the document.
13895     * @param {String|Object} cssText The text containing the css rules
13896     * @param {String} id An id to add to the stylesheet for later removal
13897     * @return {StyleSheet}
13898     */
13899     createStyleSheet : function(cssText, id){
13900         var ss;
13901         var head = doc.getElementsByTagName("head")[0];
13902         var nrules = doc.createElement("style");
13903         nrules.setAttribute("type", "text/css");
13904         if(id){
13905             nrules.setAttribute("id", id);
13906         }
13907         if (typeof(cssText) != 'string') {
13908             // support object maps..
13909             // not sure if this a good idea.. 
13910             // perhaps it should be merged with the general css handling
13911             // and handle js style props.
13912             var cssTextNew = [];
13913             for(var n in cssText) {
13914                 var citems = [];
13915                 for(var k in cssText[n]) {
13916                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13917                 }
13918                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13919                 
13920             }
13921             cssText = cssTextNew.join("\n");
13922             
13923         }
13924        
13925        
13926        if(Roo.isIE){
13927            head.appendChild(nrules);
13928            ss = nrules.styleSheet;
13929            ss.cssText = cssText;
13930        }else{
13931            try{
13932                 nrules.appendChild(doc.createTextNode(cssText));
13933            }catch(e){
13934                nrules.cssText = cssText; 
13935            }
13936            head.appendChild(nrules);
13937            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13938        }
13939        this.cacheStyleSheet(ss);
13940        return ss;
13941    },
13942
13943    /**
13944     * Removes a style or link tag by id
13945     * @param {String} id The id of the tag
13946     */
13947    removeStyleSheet : function(id){
13948        var existing = doc.getElementById(id);
13949        if(existing){
13950            existing.parentNode.removeChild(existing);
13951        }
13952    },
13953
13954    /**
13955     * Dynamically swaps an existing stylesheet reference for a new one
13956     * @param {String} id The id of an existing link tag to remove
13957     * @param {String} url The href of the new stylesheet to include
13958     */
13959    swapStyleSheet : function(id, url){
13960        this.removeStyleSheet(id);
13961        var ss = doc.createElement("link");
13962        ss.setAttribute("rel", "stylesheet");
13963        ss.setAttribute("type", "text/css");
13964        ss.setAttribute("id", id);
13965        ss.setAttribute("href", url);
13966        doc.getElementsByTagName("head")[0].appendChild(ss);
13967    },
13968    
13969    /**
13970     * Refresh the rule cache if you have dynamically added stylesheets
13971     * @return {Object} An object (hash) of rules indexed by selector
13972     */
13973    refreshCache : function(){
13974        return this.getRules(true);
13975    },
13976
13977    // private
13978    cacheStyleSheet : function(stylesheet){
13979        if(!rules){
13980            rules = {};
13981        }
13982        try{// try catch for cross domain access issue
13983            var ssRules = stylesheet.cssRules || stylesheet.rules;
13984            for(var j = ssRules.length-1; j >= 0; --j){
13985                rules[ssRules[j].selectorText] = ssRules[j];
13986            }
13987        }catch(e){}
13988    },
13989    
13990    /**
13991     * Gets all css rules for the document
13992     * @param {Boolean} refreshCache true to refresh the internal cache
13993     * @return {Object} An object (hash) of rules indexed by selector
13994     */
13995    getRules : function(refreshCache){
13996                 if(rules == null || refreshCache){
13997                         rules = {};
13998                         var ds = doc.styleSheets;
13999                         for(var i =0, len = ds.length; i < len; i++){
14000                             try{
14001                         this.cacheStyleSheet(ds[i]);
14002                     }catch(e){} 
14003                 }
14004                 }
14005                 return rules;
14006         },
14007         
14008         /**
14009     * Gets an an individual CSS rule by selector(s)
14010     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14011     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14012     * @return {CSSRule} The CSS rule or null if one is not found
14013     */
14014    getRule : function(selector, refreshCache){
14015                 var rs = this.getRules(refreshCache);
14016                 if(!(selector instanceof Array)){
14017                     return rs[selector];
14018                 }
14019                 for(var i = 0; i < selector.length; i++){
14020                         if(rs[selector[i]]){
14021                                 return rs[selector[i]];
14022                         }
14023                 }
14024                 return null;
14025         },
14026         
14027         
14028         /**
14029     * Updates a rule property
14030     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14031     * @param {String} property The css property
14032     * @param {String} value The new value for the property
14033     * @return {Boolean} true If a rule was found and updated
14034     */
14035    updateRule : function(selector, property, value){
14036                 if(!(selector instanceof Array)){
14037                         var rule = this.getRule(selector);
14038                         if(rule){
14039                                 rule.style[property.replace(camelRe, camelFn)] = value;
14040                                 return true;
14041                         }
14042                 }else{
14043                         for(var i = 0; i < selector.length; i++){
14044                                 if(this.updateRule(selector[i], property, value)){
14045                                         return true;
14046                                 }
14047                         }
14048                 }
14049                 return false;
14050         }
14051    };   
14052 }();/*
14053  * Based on:
14054  * Ext JS Library 1.1.1
14055  * Copyright(c) 2006-2007, Ext JS, LLC.
14056  *
14057  * Originally Released Under LGPL - original licence link has changed is not relivant.
14058  *
14059  * Fork - LGPL
14060  * <script type="text/javascript">
14061  */
14062
14063  
14064
14065 /**
14066  * @class Roo.util.ClickRepeater
14067  * @extends Roo.util.Observable
14068  * 
14069  * A wrapper class which can be applied to any element. Fires a "click" event while the
14070  * mouse is pressed. The interval between firings may be specified in the config but
14071  * defaults to 10 milliseconds.
14072  * 
14073  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14074  * 
14075  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14076  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14077  * Similar to an autorepeat key delay.
14078  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14079  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14080  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14081  *           "interval" and "delay" are ignored. "immediate" is honored.
14082  * @cfg {Boolean} preventDefault True to prevent the default click event
14083  * @cfg {Boolean} stopDefault True to stop the default click event
14084  * 
14085  * @history
14086  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14087  *     2007-02-02 jvs Renamed to ClickRepeater
14088  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14089  *
14090  *  @constructor
14091  * @param {String/HTMLElement/Element} el The element to listen on
14092  * @param {Object} config
14093  **/
14094 Roo.util.ClickRepeater = function(el, config)
14095 {
14096     this.el = Roo.get(el);
14097     this.el.unselectable();
14098
14099     Roo.apply(this, config);
14100
14101     this.addEvents({
14102     /**
14103      * @event mousedown
14104      * Fires when the mouse button is depressed.
14105      * @param {Roo.util.ClickRepeater} this
14106      */
14107         "mousedown" : true,
14108     /**
14109      * @event click
14110      * Fires on a specified interval during the time the element is pressed.
14111      * @param {Roo.util.ClickRepeater} this
14112      */
14113         "click" : true,
14114     /**
14115      * @event mouseup
14116      * Fires when the mouse key is released.
14117      * @param {Roo.util.ClickRepeater} this
14118      */
14119         "mouseup" : true
14120     });
14121
14122     this.el.on("mousedown", this.handleMouseDown, this);
14123     if(this.preventDefault || this.stopDefault){
14124         this.el.on("click", function(e){
14125             if(this.preventDefault){
14126                 e.preventDefault();
14127             }
14128             if(this.stopDefault){
14129                 e.stopEvent();
14130             }
14131         }, this);
14132     }
14133
14134     // allow inline handler
14135     if(this.handler){
14136         this.on("click", this.handler,  this.scope || this);
14137     }
14138
14139     Roo.util.ClickRepeater.superclass.constructor.call(this);
14140 };
14141
14142 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14143     interval : 20,
14144     delay: 250,
14145     preventDefault : true,
14146     stopDefault : false,
14147     timer : 0,
14148
14149     // private
14150     handleMouseDown : function(){
14151         clearTimeout(this.timer);
14152         this.el.blur();
14153         if(this.pressClass){
14154             this.el.addClass(this.pressClass);
14155         }
14156         this.mousedownTime = new Date();
14157
14158         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14159         this.el.on("mouseout", this.handleMouseOut, this);
14160
14161         this.fireEvent("mousedown", this);
14162         this.fireEvent("click", this);
14163         
14164         this.timer = this.click.defer(this.delay || this.interval, this);
14165     },
14166
14167     // private
14168     click : function(){
14169         this.fireEvent("click", this);
14170         this.timer = this.click.defer(this.getInterval(), this);
14171     },
14172
14173     // private
14174     getInterval: function(){
14175         if(!this.accelerate){
14176             return this.interval;
14177         }
14178         var pressTime = this.mousedownTime.getElapsed();
14179         if(pressTime < 500){
14180             return 400;
14181         }else if(pressTime < 1700){
14182             return 320;
14183         }else if(pressTime < 2600){
14184             return 250;
14185         }else if(pressTime < 3500){
14186             return 180;
14187         }else if(pressTime < 4400){
14188             return 140;
14189         }else if(pressTime < 5300){
14190             return 80;
14191         }else if(pressTime < 6200){
14192             return 50;
14193         }else{
14194             return 10;
14195         }
14196     },
14197
14198     // private
14199     handleMouseOut : function(){
14200         clearTimeout(this.timer);
14201         if(this.pressClass){
14202             this.el.removeClass(this.pressClass);
14203         }
14204         this.el.on("mouseover", this.handleMouseReturn, this);
14205     },
14206
14207     // private
14208     handleMouseReturn : function(){
14209         this.el.un("mouseover", this.handleMouseReturn);
14210         if(this.pressClass){
14211             this.el.addClass(this.pressClass);
14212         }
14213         this.click();
14214     },
14215
14216     // private
14217     handleMouseUp : function(){
14218         clearTimeout(this.timer);
14219         this.el.un("mouseover", this.handleMouseReturn);
14220         this.el.un("mouseout", this.handleMouseOut);
14221         Roo.get(document).un("mouseup", this.handleMouseUp);
14222         this.el.removeClass(this.pressClass);
14223         this.fireEvent("mouseup", this);
14224     }
14225 });/*
14226  * Based on:
14227  * Ext JS Library 1.1.1
14228  * Copyright(c) 2006-2007, Ext JS, LLC.
14229  *
14230  * Originally Released Under LGPL - original licence link has changed is not relivant.
14231  *
14232  * Fork - LGPL
14233  * <script type="text/javascript">
14234  */
14235
14236  
14237 /**
14238  * @class Roo.KeyNav
14239  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14240  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14241  * way to implement custom navigation schemes for any UI component.</p>
14242  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14243  * pageUp, pageDown, del, home, end.  Usage:</p>
14244  <pre><code>
14245 var nav = new Roo.KeyNav("my-element", {
14246     "left" : function(e){
14247         this.moveLeft(e.ctrlKey);
14248     },
14249     "right" : function(e){
14250         this.moveRight(e.ctrlKey);
14251     },
14252     "enter" : function(e){
14253         this.save();
14254     },
14255     scope : this
14256 });
14257 </code></pre>
14258  * @constructor
14259  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14260  * @param {Object} config The config
14261  */
14262 Roo.KeyNav = function(el, config){
14263     this.el = Roo.get(el);
14264     Roo.apply(this, config);
14265     if(!this.disabled){
14266         this.disabled = true;
14267         this.enable();
14268     }
14269 };
14270
14271 Roo.KeyNav.prototype = {
14272     /**
14273      * @cfg {Boolean} disabled
14274      * True to disable this KeyNav instance (defaults to false)
14275      */
14276     disabled : false,
14277     /**
14278      * @cfg {String} defaultEventAction
14279      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14280      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14281      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14282      */
14283     defaultEventAction: "stopEvent",
14284     /**
14285      * @cfg {Boolean} forceKeyDown
14286      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14287      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14288      * handle keydown instead of keypress.
14289      */
14290     forceKeyDown : false,
14291
14292     // private
14293     prepareEvent : function(e){
14294         var k = e.getKey();
14295         var h = this.keyToHandler[k];
14296         //if(h && this[h]){
14297         //    e.stopPropagation();
14298         //}
14299         if(Roo.isSafari && h && k >= 37 && k <= 40){
14300             e.stopEvent();
14301         }
14302     },
14303
14304     // private
14305     relay : function(e){
14306         var k = e.getKey();
14307         var h = this.keyToHandler[k];
14308         if(h && this[h]){
14309             if(this.doRelay(e, this[h], h) !== true){
14310                 e[this.defaultEventAction]();
14311             }
14312         }
14313     },
14314
14315     // private
14316     doRelay : function(e, h, hname){
14317         return h.call(this.scope || this, e);
14318     },
14319
14320     // possible handlers
14321     enter : false,
14322     left : false,
14323     right : false,
14324     up : false,
14325     down : false,
14326     tab : false,
14327     esc : false,
14328     pageUp : false,
14329     pageDown : false,
14330     del : false,
14331     home : false,
14332     end : false,
14333
14334     // quick lookup hash
14335     keyToHandler : {
14336         37 : "left",
14337         39 : "right",
14338         38 : "up",
14339         40 : "down",
14340         33 : "pageUp",
14341         34 : "pageDown",
14342         46 : "del",
14343         36 : "home",
14344         35 : "end",
14345         13 : "enter",
14346         27 : "esc",
14347         9  : "tab"
14348     },
14349
14350         /**
14351          * Enable this KeyNav
14352          */
14353         enable: function(){
14354                 if(this.disabled){
14355             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14356             // the EventObject will normalize Safari automatically
14357             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14358                 this.el.on("keydown", this.relay,  this);
14359             }else{
14360                 this.el.on("keydown", this.prepareEvent,  this);
14361                 this.el.on("keypress", this.relay,  this);
14362             }
14363                     this.disabled = false;
14364                 }
14365         },
14366
14367         /**
14368          * Disable this KeyNav
14369          */
14370         disable: function(){
14371                 if(!this.disabled){
14372                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14373                 this.el.un("keydown", this.relay);
14374             }else{
14375                 this.el.un("keydown", this.prepareEvent);
14376                 this.el.un("keypress", this.relay);
14377             }
14378                     this.disabled = true;
14379                 }
14380         }
14381 };/*
14382  * Based on:
14383  * Ext JS Library 1.1.1
14384  * Copyright(c) 2006-2007, Ext JS, LLC.
14385  *
14386  * Originally Released Under LGPL - original licence link has changed is not relivant.
14387  *
14388  * Fork - LGPL
14389  * <script type="text/javascript">
14390  */
14391
14392  
14393 /**
14394  * @class Roo.KeyMap
14395  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14396  * The constructor accepts the same config object as defined by {@link #addBinding}.
14397  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14398  * combination it will call the function with this signature (if the match is a multi-key
14399  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14400  * A KeyMap can also handle a string representation of keys.<br />
14401  * Usage:
14402  <pre><code>
14403 // map one key by key code
14404 var map = new Roo.KeyMap("my-element", {
14405     key: 13, // or Roo.EventObject.ENTER
14406     fn: myHandler,
14407     scope: myObject
14408 });
14409
14410 // map multiple keys to one action by string
14411 var map = new Roo.KeyMap("my-element", {
14412     key: "a\r\n\t",
14413     fn: myHandler,
14414     scope: myObject
14415 });
14416
14417 // map multiple keys to multiple actions by strings and array of codes
14418 var map = new Roo.KeyMap("my-element", [
14419     {
14420         key: [10,13],
14421         fn: function(){ alert("Return was pressed"); }
14422     }, {
14423         key: "abc",
14424         fn: function(){ alert('a, b or c was pressed'); }
14425     }, {
14426         key: "\t",
14427         ctrl:true,
14428         shift:true,
14429         fn: function(){ alert('Control + shift + tab was pressed.'); }
14430     }
14431 ]);
14432 </code></pre>
14433  * <b>Note: A KeyMap starts enabled</b>
14434  * @constructor
14435  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14436  * @param {Object} config The config (see {@link #addBinding})
14437  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14438  */
14439 Roo.KeyMap = function(el, config, eventName){
14440     this.el  = Roo.get(el);
14441     this.eventName = eventName || "keydown";
14442     this.bindings = [];
14443     if(config){
14444         this.addBinding(config);
14445     }
14446     this.enable();
14447 };
14448
14449 Roo.KeyMap.prototype = {
14450     /**
14451      * True to stop the event from bubbling and prevent the default browser action if the
14452      * key was handled by the KeyMap (defaults to false)
14453      * @type Boolean
14454      */
14455     stopEvent : false,
14456
14457     /**
14458      * Add a new binding to this KeyMap. The following config object properties are supported:
14459      * <pre>
14460 Property    Type             Description
14461 ----------  ---------------  ----------------------------------------------------------------------
14462 key         String/Array     A single keycode or an array of keycodes to handle
14463 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14464 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14465 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14466 fn          Function         The function to call when KeyMap finds the expected key combination
14467 scope       Object           The scope of the callback function
14468 </pre>
14469      *
14470      * Usage:
14471      * <pre><code>
14472 // Create a KeyMap
14473 var map = new Roo.KeyMap(document, {
14474     key: Roo.EventObject.ENTER,
14475     fn: handleKey,
14476     scope: this
14477 });
14478
14479 //Add a new binding to the existing KeyMap later
14480 map.addBinding({
14481     key: 'abc',
14482     shift: true,
14483     fn: handleKey,
14484     scope: this
14485 });
14486 </code></pre>
14487      * @param {Object/Array} config A single KeyMap config or an array of configs
14488      */
14489         addBinding : function(config){
14490         if(config instanceof Array){
14491             for(var i = 0, len = config.length; i < len; i++){
14492                 this.addBinding(config[i]);
14493             }
14494             return;
14495         }
14496         var keyCode = config.key,
14497             shift = config.shift, 
14498             ctrl = config.ctrl, 
14499             alt = config.alt,
14500             fn = config.fn,
14501             scope = config.scope;
14502         if(typeof keyCode == "string"){
14503             var ks = [];
14504             var keyString = keyCode.toUpperCase();
14505             for(var j = 0, len = keyString.length; j < len; j++){
14506                 ks.push(keyString.charCodeAt(j));
14507             }
14508             keyCode = ks;
14509         }
14510         var keyArray = keyCode instanceof Array;
14511         var handler = function(e){
14512             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14513                 var k = e.getKey();
14514                 if(keyArray){
14515                     for(var i = 0, len = keyCode.length; i < len; i++){
14516                         if(keyCode[i] == k){
14517                           if(this.stopEvent){
14518                               e.stopEvent();
14519                           }
14520                           fn.call(scope || window, k, e);
14521                           return;
14522                         }
14523                     }
14524                 }else{
14525                     if(k == keyCode){
14526                         if(this.stopEvent){
14527                            e.stopEvent();
14528                         }
14529                         fn.call(scope || window, k, e);
14530                     }
14531                 }
14532             }
14533         };
14534         this.bindings.push(handler);  
14535         },
14536
14537     /**
14538      * Shorthand for adding a single key listener
14539      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14540      * following options:
14541      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14542      * @param {Function} fn The function to call
14543      * @param {Object} scope (optional) The scope of the function
14544      */
14545     on : function(key, fn, scope){
14546         var keyCode, shift, ctrl, alt;
14547         if(typeof key == "object" && !(key instanceof Array)){
14548             keyCode = key.key;
14549             shift = key.shift;
14550             ctrl = key.ctrl;
14551             alt = key.alt;
14552         }else{
14553             keyCode = key;
14554         }
14555         this.addBinding({
14556             key: keyCode,
14557             shift: shift,
14558             ctrl: ctrl,
14559             alt: alt,
14560             fn: fn,
14561             scope: scope
14562         })
14563     },
14564
14565     // private
14566     handleKeyDown : function(e){
14567             if(this.enabled){ //just in case
14568             var b = this.bindings;
14569             for(var i = 0, len = b.length; i < len; i++){
14570                 b[i].call(this, e);
14571             }
14572             }
14573         },
14574         
14575         /**
14576          * Returns true if this KeyMap is enabled
14577          * @return {Boolean} 
14578          */
14579         isEnabled : function(){
14580             return this.enabled;  
14581         },
14582         
14583         /**
14584          * Enables this KeyMap
14585          */
14586         enable: function(){
14587                 if(!this.enabled){
14588                     this.el.on(this.eventName, this.handleKeyDown, this);
14589                     this.enabled = true;
14590                 }
14591         },
14592
14593         /**
14594          * Disable this KeyMap
14595          */
14596         disable: function(){
14597                 if(this.enabled){
14598                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14599                     this.enabled = false;
14600                 }
14601         }
14602 };/*
14603  * Based on:
14604  * Ext JS Library 1.1.1
14605  * Copyright(c) 2006-2007, Ext JS, LLC.
14606  *
14607  * Originally Released Under LGPL - original licence link has changed is not relivant.
14608  *
14609  * Fork - LGPL
14610  * <script type="text/javascript">
14611  */
14612
14613  
14614 /**
14615  * @class Roo.util.TextMetrics
14616  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14617  * wide, in pixels, a given block of text will be.
14618  * @singleton
14619  */
14620 Roo.util.TextMetrics = function(){
14621     var shared;
14622     return {
14623         /**
14624          * Measures the size of the specified text
14625          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14626          * that can affect the size of the rendered text
14627          * @param {String} text The text to measure
14628          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14629          * in order to accurately measure the text height
14630          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14631          */
14632         measure : function(el, text, fixedWidth){
14633             if(!shared){
14634                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14635             }
14636             shared.bind(el);
14637             shared.setFixedWidth(fixedWidth || 'auto');
14638             return shared.getSize(text);
14639         },
14640
14641         /**
14642          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14643          * the overhead of multiple calls to initialize the style properties on each measurement.
14644          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14645          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14646          * in order to accurately measure the text height
14647          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14648          */
14649         createInstance : function(el, fixedWidth){
14650             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14651         }
14652     };
14653 }();
14654
14655  
14656
14657 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14658     var ml = new Roo.Element(document.createElement('div'));
14659     document.body.appendChild(ml.dom);
14660     ml.position('absolute');
14661     ml.setLeftTop(-1000, -1000);
14662     ml.hide();
14663
14664     if(fixedWidth){
14665         ml.setWidth(fixedWidth);
14666     }
14667      
14668     var instance = {
14669         /**
14670          * Returns the size of the specified text based on the internal element's style and width properties
14671          * @memberOf Roo.util.TextMetrics.Instance#
14672          * @param {String} text The text to measure
14673          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14674          */
14675         getSize : function(text){
14676             ml.update(text);
14677             var s = ml.getSize();
14678             ml.update('');
14679             return s;
14680         },
14681
14682         /**
14683          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14684          * that can affect the size of the rendered text
14685          * @memberOf Roo.util.TextMetrics.Instance#
14686          * @param {String/HTMLElement} el The element, dom node or id
14687          */
14688         bind : function(el){
14689             ml.setStyle(
14690                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14691             );
14692         },
14693
14694         /**
14695          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14696          * to set a fixed width in order to accurately measure the text height.
14697          * @memberOf Roo.util.TextMetrics.Instance#
14698          * @param {Number} width The width to set on the element
14699          */
14700         setFixedWidth : function(width){
14701             ml.setWidth(width);
14702         },
14703
14704         /**
14705          * Returns the measured width of the specified text
14706          * @memberOf Roo.util.TextMetrics.Instance#
14707          * @param {String} text The text to measure
14708          * @return {Number} width The width in pixels
14709          */
14710         getWidth : function(text){
14711             ml.dom.style.width = 'auto';
14712             return this.getSize(text).width;
14713         },
14714
14715         /**
14716          * Returns the measured height of the specified text.  For multiline text, be sure to call
14717          * {@link #setFixedWidth} if necessary.
14718          * @memberOf Roo.util.TextMetrics.Instance#
14719          * @param {String} text The text to measure
14720          * @return {Number} height The height in pixels
14721          */
14722         getHeight : function(text){
14723             return this.getSize(text).height;
14724         }
14725     };
14726
14727     instance.bind(bindTo);
14728
14729     return instance;
14730 };
14731
14732 // backwards compat
14733 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14734  * Based on:
14735  * Ext JS Library 1.1.1
14736  * Copyright(c) 2006-2007, Ext JS, LLC.
14737  *
14738  * Originally Released Under LGPL - original licence link has changed is not relivant.
14739  *
14740  * Fork - LGPL
14741  * <script type="text/javascript">
14742  */
14743
14744 /**
14745  * @class Roo.state.Provider
14746  * Abstract base class for state provider implementations. This class provides methods
14747  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14748  * Provider interface.
14749  */
14750 Roo.state.Provider = function(){
14751     /**
14752      * @event statechange
14753      * Fires when a state change occurs.
14754      * @param {Provider} this This state provider
14755      * @param {String} key The state key which was changed
14756      * @param {String} value The encoded value for the state
14757      */
14758     this.addEvents({
14759         "statechange": true
14760     });
14761     this.state = {};
14762     Roo.state.Provider.superclass.constructor.call(this);
14763 };
14764 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14765     /**
14766      * Returns the current value for a key
14767      * @param {String} name The key name
14768      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14769      * @return {Mixed} The state data
14770      */
14771     get : function(name, defaultValue){
14772         return typeof this.state[name] == "undefined" ?
14773             defaultValue : this.state[name];
14774     },
14775     
14776     /**
14777      * Clears a value from the state
14778      * @param {String} name The key name
14779      */
14780     clear : function(name){
14781         delete this.state[name];
14782         this.fireEvent("statechange", this, name, null);
14783     },
14784     
14785     /**
14786      * Sets the value for a key
14787      * @param {String} name The key name
14788      * @param {Mixed} value The value to set
14789      */
14790     set : function(name, value){
14791         this.state[name] = value;
14792         this.fireEvent("statechange", this, name, value);
14793     },
14794     
14795     /**
14796      * Decodes a string previously encoded with {@link #encodeValue}.
14797      * @param {String} value The value to decode
14798      * @return {Mixed} The decoded value
14799      */
14800     decodeValue : function(cookie){
14801         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14802         var matches = re.exec(unescape(cookie));
14803         if(!matches || !matches[1]) return; // non state cookie
14804         var type = matches[1];
14805         var v = matches[2];
14806         switch(type){
14807             case "n":
14808                 return parseFloat(v);
14809             case "d":
14810                 return new Date(Date.parse(v));
14811             case "b":
14812                 return (v == "1");
14813             case "a":
14814                 var all = [];
14815                 var values = v.split("^");
14816                 for(var i = 0, len = values.length; i < len; i++){
14817                     all.push(this.decodeValue(values[i]));
14818                 }
14819                 return all;
14820            case "o":
14821                 var all = {};
14822                 var values = v.split("^");
14823                 for(var i = 0, len = values.length; i < len; i++){
14824                     var kv = values[i].split("=");
14825                     all[kv[0]] = this.decodeValue(kv[1]);
14826                 }
14827                 return all;
14828            default:
14829                 return v;
14830         }
14831     },
14832     
14833     /**
14834      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14835      * @param {Mixed} value The value to encode
14836      * @return {String} The encoded value
14837      */
14838     encodeValue : function(v){
14839         var enc;
14840         if(typeof v == "number"){
14841             enc = "n:" + v;
14842         }else if(typeof v == "boolean"){
14843             enc = "b:" + (v ? "1" : "0");
14844         }else if(v instanceof Date){
14845             enc = "d:" + v.toGMTString();
14846         }else if(v instanceof Array){
14847             var flat = "";
14848             for(var i = 0, len = v.length; i < len; i++){
14849                 flat += this.encodeValue(v[i]);
14850                 if(i != len-1) flat += "^";
14851             }
14852             enc = "a:" + flat;
14853         }else if(typeof v == "object"){
14854             var flat = "";
14855             for(var key in v){
14856                 if(typeof v[key] != "function"){
14857                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14858                 }
14859             }
14860             enc = "o:" + flat.substring(0, flat.length-1);
14861         }else{
14862             enc = "s:" + v;
14863         }
14864         return escape(enc);        
14865     }
14866 });
14867
14868 /*
14869  * Based on:
14870  * Ext JS Library 1.1.1
14871  * Copyright(c) 2006-2007, Ext JS, LLC.
14872  *
14873  * Originally Released Under LGPL - original licence link has changed is not relivant.
14874  *
14875  * Fork - LGPL
14876  * <script type="text/javascript">
14877  */
14878 /**
14879  * @class Roo.state.Manager
14880  * This is the global state manager. By default all components that are "state aware" check this class
14881  * for state information if you don't pass them a custom state provider. In order for this class
14882  * to be useful, it must be initialized with a provider when your application initializes.
14883  <pre><code>
14884 // in your initialization function
14885 init : function(){
14886    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14887    ...
14888    // supposed you have a {@link Roo.BorderLayout}
14889    var layout = new Roo.BorderLayout(...);
14890    layout.restoreState();
14891    // or a {Roo.BasicDialog}
14892    var dialog = new Roo.BasicDialog(...);
14893    dialog.restoreState();
14894  </code></pre>
14895  * @singleton
14896  */
14897 Roo.state.Manager = function(){
14898     var provider = new Roo.state.Provider();
14899     
14900     return {
14901         /**
14902          * Configures the default state provider for your application
14903          * @param {Provider} stateProvider The state provider to set
14904          */
14905         setProvider : function(stateProvider){
14906             provider = stateProvider;
14907         },
14908         
14909         /**
14910          * Returns the current value for a key
14911          * @param {String} name The key name
14912          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14913          * @return {Mixed} The state data
14914          */
14915         get : function(key, defaultValue){
14916             return provider.get(key, defaultValue);
14917         },
14918         
14919         /**
14920          * Sets the value for a key
14921          * @param {String} name The key name
14922          * @param {Mixed} value The state data
14923          */
14924          set : function(key, value){
14925             provider.set(key, value);
14926         },
14927         
14928         /**
14929          * Clears a value from the state
14930          * @param {String} name The key name
14931          */
14932         clear : function(key){
14933             provider.clear(key);
14934         },
14935         
14936         /**
14937          * Gets the currently configured state provider
14938          * @return {Provider} The state provider
14939          */
14940         getProvider : function(){
14941             return provider;
14942         }
14943     };
14944 }();
14945 /*
14946  * Based on:
14947  * Ext JS Library 1.1.1
14948  * Copyright(c) 2006-2007, Ext JS, LLC.
14949  *
14950  * Originally Released Under LGPL - original licence link has changed is not relivant.
14951  *
14952  * Fork - LGPL
14953  * <script type="text/javascript">
14954  */
14955 /**
14956  * @class Roo.state.CookieProvider
14957  * @extends Roo.state.Provider
14958  * The default Provider implementation which saves state via cookies.
14959  * <br />Usage:
14960  <pre><code>
14961    var cp = new Roo.state.CookieProvider({
14962        path: "/cgi-bin/",
14963        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14964        domain: "roojs.com"
14965    })
14966    Roo.state.Manager.setProvider(cp);
14967  </code></pre>
14968  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14969  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14970  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14971  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14972  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14973  * domain the page is running on including the 'www' like 'www.roojs.com')
14974  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14975  * @constructor
14976  * Create a new CookieProvider
14977  * @param {Object} config The configuration object
14978  */
14979 Roo.state.CookieProvider = function(config){
14980     Roo.state.CookieProvider.superclass.constructor.call(this);
14981     this.path = "/";
14982     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14983     this.domain = null;
14984     this.secure = false;
14985     Roo.apply(this, config);
14986     this.state = this.readCookies();
14987 };
14988
14989 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14990     // private
14991     set : function(name, value){
14992         if(typeof value == "undefined" || value === null){
14993             this.clear(name);
14994             return;
14995         }
14996         this.setCookie(name, value);
14997         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14998     },
14999
15000     // private
15001     clear : function(name){
15002         this.clearCookie(name);
15003         Roo.state.CookieProvider.superclass.clear.call(this, name);
15004     },
15005
15006     // private
15007     readCookies : function(){
15008         var cookies = {};
15009         var c = document.cookie + ";";
15010         var re = /\s?(.*?)=(.*?);/g;
15011         var matches;
15012         while((matches = re.exec(c)) != null){
15013             var name = matches[1];
15014             var value = matches[2];
15015             if(name && name.substring(0,3) == "ys-"){
15016                 cookies[name.substr(3)] = this.decodeValue(value);
15017             }
15018         }
15019         return cookies;
15020     },
15021
15022     // private
15023     setCookie : function(name, value){
15024         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15025            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15026            ((this.path == null) ? "" : ("; path=" + this.path)) +
15027            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15028            ((this.secure == true) ? "; secure" : "");
15029     },
15030
15031     // private
15032     clearCookie : function(name){
15033         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15034            ((this.path == null) ? "" : ("; path=" + this.path)) +
15035            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15036            ((this.secure == true) ? "; secure" : "");
15037     }
15038 });/*
15039  * Based on:
15040  * Ext JS Library 1.1.1
15041  * Copyright(c) 2006-2007, Ext JS, LLC.
15042  *
15043  * Originally Released Under LGPL - original licence link has changed is not relivant.
15044  *
15045  * Fork - LGPL
15046  * <script type="text/javascript">
15047  */
15048  
15049
15050 /**
15051  * @class Roo.ComponentMgr
15052  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15053  * @singleton
15054  */
15055 Roo.ComponentMgr = function(){
15056     var all = new Roo.util.MixedCollection();
15057
15058     return {
15059         /**
15060          * Registers a component.
15061          * @param {Roo.Component} c The component
15062          */
15063         register : function(c){
15064             all.add(c);
15065         },
15066
15067         /**
15068          * Unregisters a component.
15069          * @param {Roo.Component} c The component
15070          */
15071         unregister : function(c){
15072             all.remove(c);
15073         },
15074
15075         /**
15076          * Returns a component by id
15077          * @param {String} id The component id
15078          */
15079         get : function(id){
15080             return all.get(id);
15081         },
15082
15083         /**
15084          * Registers a function that will be called when a specified component is added to ComponentMgr
15085          * @param {String} id The component id
15086          * @param {Funtction} fn The callback function
15087          * @param {Object} scope The scope of the callback
15088          */
15089         onAvailable : function(id, fn, scope){
15090             all.on("add", function(index, o){
15091                 if(o.id == id){
15092                     fn.call(scope || o, o);
15093                     all.un("add", fn, scope);
15094                 }
15095             });
15096         }
15097     };
15098 }();/*
15099  * Based on:
15100  * Ext JS Library 1.1.1
15101  * Copyright(c) 2006-2007, Ext JS, LLC.
15102  *
15103  * Originally Released Under LGPL - original licence link has changed is not relivant.
15104  *
15105  * Fork - LGPL
15106  * <script type="text/javascript">
15107  */
15108  
15109 /**
15110  * @class Roo.Component
15111  * @extends Roo.util.Observable
15112  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15113  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15114  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15115  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15116  * All visual components (widgets) that require rendering into a layout should subclass Component.
15117  * @constructor
15118  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15119  * 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
15120  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15121  */
15122 Roo.Component = function(config){
15123     config = config || {};
15124     if(config.tagName || config.dom || typeof config == "string"){ // element object
15125         config = {el: config, id: config.id || config};
15126     }
15127     this.initialConfig = config;
15128
15129     Roo.apply(this, config);
15130     this.addEvents({
15131         /**
15132          * @event disable
15133          * Fires after the component is disabled.
15134              * @param {Roo.Component} this
15135              */
15136         disable : true,
15137         /**
15138          * @event enable
15139          * Fires after the component is enabled.
15140              * @param {Roo.Component} this
15141              */
15142         enable : true,
15143         /**
15144          * @event beforeshow
15145          * Fires before the component is shown.  Return false to stop the show.
15146              * @param {Roo.Component} this
15147              */
15148         beforeshow : true,
15149         /**
15150          * @event show
15151          * Fires after the component is shown.
15152              * @param {Roo.Component} this
15153              */
15154         show : true,
15155         /**
15156          * @event beforehide
15157          * Fires before the component is hidden. Return false to stop the hide.
15158              * @param {Roo.Component} this
15159              */
15160         beforehide : true,
15161         /**
15162          * @event hide
15163          * Fires after the component is hidden.
15164              * @param {Roo.Component} this
15165              */
15166         hide : true,
15167         /**
15168          * @event beforerender
15169          * Fires before the component is rendered. Return false to stop the render.
15170              * @param {Roo.Component} this
15171              */
15172         beforerender : true,
15173         /**
15174          * @event render
15175          * Fires after the component is rendered.
15176              * @param {Roo.Component} this
15177              */
15178         render : true,
15179         /**
15180          * @event beforedestroy
15181          * Fires before the component is destroyed. Return false to stop the destroy.
15182              * @param {Roo.Component} this
15183              */
15184         beforedestroy : true,
15185         /**
15186          * @event destroy
15187          * Fires after the component is destroyed.
15188              * @param {Roo.Component} this
15189              */
15190         destroy : true
15191     });
15192     if(!this.id){
15193         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15194     }
15195     Roo.ComponentMgr.register(this);
15196     Roo.Component.superclass.constructor.call(this);
15197     this.initComponent();
15198     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15199         this.render(this.renderTo);
15200         delete this.renderTo;
15201     }
15202 };
15203
15204 /** @private */
15205 Roo.Component.AUTO_ID = 1000;
15206
15207 Roo.extend(Roo.Component, Roo.util.Observable, {
15208     /**
15209      * @scope Roo.Component.prototype
15210      * @type {Boolean}
15211      * true if this component is hidden. Read-only.
15212      */
15213     hidden : false,
15214     /**
15215      * @type {Boolean}
15216      * true if this component is disabled. Read-only.
15217      */
15218     disabled : false,
15219     /**
15220      * @type {Boolean}
15221      * true if this component has been rendered. Read-only.
15222      */
15223     rendered : false,
15224     
15225     /** @cfg {String} disableClass
15226      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15227      */
15228     disabledClass : "x-item-disabled",
15229         /** @cfg {Boolean} allowDomMove
15230          * Whether the component can move the Dom node when rendering (defaults to true).
15231          */
15232     allowDomMove : true,
15233     /** @cfg {String} hideMode (display|visibility)
15234      * How this component should hidden. Supported values are
15235      * "visibility" (css visibility), "offsets" (negative offset position) and
15236      * "display" (css display) - defaults to "display".
15237      */
15238     hideMode: 'display',
15239
15240     /** @private */
15241     ctype : "Roo.Component",
15242
15243     /**
15244      * @cfg {String} actionMode 
15245      * which property holds the element that used for  hide() / show() / disable() / enable()
15246      * default is 'el' 
15247      */
15248     actionMode : "el",
15249
15250     /** @private */
15251     getActionEl : function(){
15252         return this[this.actionMode];
15253     },
15254
15255     initComponent : Roo.emptyFn,
15256     /**
15257      * If this is a lazy rendering component, render it to its container element.
15258      * @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.
15259      */
15260     render : function(container, position){
15261         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15262             if(!container && this.el){
15263                 this.el = Roo.get(this.el);
15264                 container = this.el.dom.parentNode;
15265                 this.allowDomMove = false;
15266             }
15267             this.container = Roo.get(container);
15268             this.rendered = true;
15269             if(position !== undefined){
15270                 if(typeof position == 'number'){
15271                     position = this.container.dom.childNodes[position];
15272                 }else{
15273                     position = Roo.getDom(position);
15274                 }
15275             }
15276             this.onRender(this.container, position || null);
15277             if(this.cls){
15278                 this.el.addClass(this.cls);
15279                 delete this.cls;
15280             }
15281             if(this.style){
15282                 this.el.applyStyles(this.style);
15283                 delete this.style;
15284             }
15285             this.fireEvent("render", this);
15286             this.afterRender(this.container);
15287             if(this.hidden){
15288                 this.hide();
15289             }
15290             if(this.disabled){
15291                 this.disable();
15292             }
15293         }
15294         return this;
15295     },
15296
15297     /** @private */
15298     // default function is not really useful
15299     onRender : function(ct, position){
15300         if(this.el){
15301             this.el = Roo.get(this.el);
15302             if(this.allowDomMove !== false){
15303                 ct.dom.insertBefore(this.el.dom, position);
15304             }
15305         }
15306     },
15307
15308     /** @private */
15309     getAutoCreate : function(){
15310         var cfg = typeof this.autoCreate == "object" ?
15311                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15312         if(this.id && !cfg.id){
15313             cfg.id = this.id;
15314         }
15315         return cfg;
15316     },
15317
15318     /** @private */
15319     afterRender : Roo.emptyFn,
15320
15321     /**
15322      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15323      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15324      */
15325     destroy : function(){
15326         if(this.fireEvent("beforedestroy", this) !== false){
15327             this.purgeListeners();
15328             this.beforeDestroy();
15329             if(this.rendered){
15330                 this.el.removeAllListeners();
15331                 this.el.remove();
15332                 if(this.actionMode == "container"){
15333                     this.container.remove();
15334                 }
15335             }
15336             this.onDestroy();
15337             Roo.ComponentMgr.unregister(this);
15338             this.fireEvent("destroy", this);
15339         }
15340     },
15341
15342         /** @private */
15343     beforeDestroy : function(){
15344
15345     },
15346
15347         /** @private */
15348         onDestroy : function(){
15349
15350     },
15351
15352     /**
15353      * Returns the underlying {@link Roo.Element}.
15354      * @return {Roo.Element} The element
15355      */
15356     getEl : function(){
15357         return this.el;
15358     },
15359
15360     /**
15361      * Returns the id of this component.
15362      * @return {String}
15363      */
15364     getId : function(){
15365         return this.id;
15366     },
15367
15368     /**
15369      * Try to focus this component.
15370      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15371      * @return {Roo.Component} this
15372      */
15373     focus : function(selectText){
15374         if(this.rendered){
15375             this.el.focus();
15376             if(selectText === true){
15377                 this.el.dom.select();
15378             }
15379         }
15380         return this;
15381     },
15382
15383     /** @private */
15384     blur : function(){
15385         if(this.rendered){
15386             this.el.blur();
15387         }
15388         return this;
15389     },
15390
15391     /**
15392      * Disable this component.
15393      * @return {Roo.Component} this
15394      */
15395     disable : function(){
15396         if(this.rendered){
15397             this.onDisable();
15398         }
15399         this.disabled = true;
15400         this.fireEvent("disable", this);
15401         return this;
15402     },
15403
15404         // private
15405     onDisable : function(){
15406         this.getActionEl().addClass(this.disabledClass);
15407         this.el.dom.disabled = true;
15408     },
15409
15410     /**
15411      * Enable this component.
15412      * @return {Roo.Component} this
15413      */
15414     enable : function(){
15415         if(this.rendered){
15416             this.onEnable();
15417         }
15418         this.disabled = false;
15419         this.fireEvent("enable", this);
15420         return this;
15421     },
15422
15423         // private
15424     onEnable : function(){
15425         this.getActionEl().removeClass(this.disabledClass);
15426         this.el.dom.disabled = false;
15427     },
15428
15429     /**
15430      * Convenience function for setting disabled/enabled by boolean.
15431      * @param {Boolean} disabled
15432      */
15433     setDisabled : function(disabled){
15434         this[disabled ? "disable" : "enable"]();
15435     },
15436
15437     /**
15438      * Show this component.
15439      * @return {Roo.Component} this
15440      */
15441     show: function(){
15442         if(this.fireEvent("beforeshow", this) !== false){
15443             this.hidden = false;
15444             if(this.rendered){
15445                 this.onShow();
15446             }
15447             this.fireEvent("show", this);
15448         }
15449         return this;
15450     },
15451
15452     // private
15453     onShow : function(){
15454         var ae = this.getActionEl();
15455         if(this.hideMode == 'visibility'){
15456             ae.dom.style.visibility = "visible";
15457         }else if(this.hideMode == 'offsets'){
15458             ae.removeClass('x-hidden');
15459         }else{
15460             ae.dom.style.display = "";
15461         }
15462     },
15463
15464     /**
15465      * Hide this component.
15466      * @return {Roo.Component} this
15467      */
15468     hide: function(){
15469         if(this.fireEvent("beforehide", this) !== false){
15470             this.hidden = true;
15471             if(this.rendered){
15472                 this.onHide();
15473             }
15474             this.fireEvent("hide", this);
15475         }
15476         return this;
15477     },
15478
15479     // private
15480     onHide : function(){
15481         var ae = this.getActionEl();
15482         if(this.hideMode == 'visibility'){
15483             ae.dom.style.visibility = "hidden";
15484         }else if(this.hideMode == 'offsets'){
15485             ae.addClass('x-hidden');
15486         }else{
15487             ae.dom.style.display = "none";
15488         }
15489     },
15490
15491     /**
15492      * Convenience function to hide or show this component by boolean.
15493      * @param {Boolean} visible True to show, false to hide
15494      * @return {Roo.Component} this
15495      */
15496     setVisible: function(visible){
15497         if(visible) {
15498             this.show();
15499         }else{
15500             this.hide();
15501         }
15502         return this;
15503     },
15504
15505     /**
15506      * Returns true if this component is visible.
15507      */
15508     isVisible : function(){
15509         return this.getActionEl().isVisible();
15510     },
15511
15512     cloneConfig : function(overrides){
15513         overrides = overrides || {};
15514         var id = overrides.id || Roo.id();
15515         var cfg = Roo.applyIf(overrides, this.initialConfig);
15516         cfg.id = id; // prevent dup id
15517         return new this.constructor(cfg);
15518     }
15519 });/*
15520  * Based on:
15521  * Ext JS Library 1.1.1
15522  * Copyright(c) 2006-2007, Ext JS, LLC.
15523  *
15524  * Originally Released Under LGPL - original licence link has changed is not relivant.
15525  *
15526  * Fork - LGPL
15527  * <script type="text/javascript">
15528  */
15529
15530 /**
15531  * @class Roo.BoxComponent
15532  * @extends Roo.Component
15533  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15534  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15535  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15536  * layout containers.
15537  * @constructor
15538  * @param {Roo.Element/String/Object} config The configuration options.
15539  */
15540 Roo.BoxComponent = function(config){
15541     Roo.Component.call(this, config);
15542     this.addEvents({
15543         /**
15544          * @event resize
15545          * Fires after the component is resized.
15546              * @param {Roo.Component} this
15547              * @param {Number} adjWidth The box-adjusted width that was set
15548              * @param {Number} adjHeight The box-adjusted height that was set
15549              * @param {Number} rawWidth The width that was originally specified
15550              * @param {Number} rawHeight The height that was originally specified
15551              */
15552         resize : true,
15553         /**
15554          * @event move
15555          * Fires after the component is moved.
15556              * @param {Roo.Component} this
15557              * @param {Number} x The new x position
15558              * @param {Number} y The new y position
15559              */
15560         move : true
15561     });
15562 };
15563
15564 Roo.extend(Roo.BoxComponent, Roo.Component, {
15565     // private, set in afterRender to signify that the component has been rendered
15566     boxReady : false,
15567     // private, used to defer height settings to subclasses
15568     deferHeight: false,
15569     /** @cfg {Number} width
15570      * width (optional) size of component
15571      */
15572      /** @cfg {Number} height
15573      * height (optional) size of component
15574      */
15575      
15576     /**
15577      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15578      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15579      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15580      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15581      * @return {Roo.BoxComponent} this
15582      */
15583     setSize : function(w, h){
15584         // support for standard size objects
15585         if(typeof w == 'object'){
15586             h = w.height;
15587             w = w.width;
15588         }
15589         // not rendered
15590         if(!this.boxReady){
15591             this.width = w;
15592             this.height = h;
15593             return this;
15594         }
15595
15596         // prevent recalcs when not needed
15597         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15598             return this;
15599         }
15600         this.lastSize = {width: w, height: h};
15601
15602         var adj = this.adjustSize(w, h);
15603         var aw = adj.width, ah = adj.height;
15604         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15605             var rz = this.getResizeEl();
15606             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15607                 rz.setSize(aw, ah);
15608             }else if(!this.deferHeight && ah !== undefined){
15609                 rz.setHeight(ah);
15610             }else if(aw !== undefined){
15611                 rz.setWidth(aw);
15612             }
15613             this.onResize(aw, ah, w, h);
15614             this.fireEvent('resize', this, aw, ah, w, h);
15615         }
15616         return this;
15617     },
15618
15619     /**
15620      * Gets the current size of the component's underlying element.
15621      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15622      */
15623     getSize : function(){
15624         return this.el.getSize();
15625     },
15626
15627     /**
15628      * Gets the current XY position of the component's underlying element.
15629      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15630      * @return {Array} The XY position of the element (e.g., [100, 200])
15631      */
15632     getPosition : function(local){
15633         if(local === true){
15634             return [this.el.getLeft(true), this.el.getTop(true)];
15635         }
15636         return this.xy || this.el.getXY();
15637     },
15638
15639     /**
15640      * Gets the current box measurements of the component's underlying element.
15641      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15642      * @returns {Object} box An object in the format {x, y, width, height}
15643      */
15644     getBox : function(local){
15645         var s = this.el.getSize();
15646         if(local){
15647             s.x = this.el.getLeft(true);
15648             s.y = this.el.getTop(true);
15649         }else{
15650             var xy = this.xy || this.el.getXY();
15651             s.x = xy[0];
15652             s.y = xy[1];
15653         }
15654         return s;
15655     },
15656
15657     /**
15658      * Sets the current box measurements of the component's underlying element.
15659      * @param {Object} box An object in the format {x, y, width, height}
15660      * @returns {Roo.BoxComponent} this
15661      */
15662     updateBox : function(box){
15663         this.setSize(box.width, box.height);
15664         this.setPagePosition(box.x, box.y);
15665         return this;
15666     },
15667
15668     // protected
15669     getResizeEl : function(){
15670         return this.resizeEl || this.el;
15671     },
15672
15673     // protected
15674     getPositionEl : function(){
15675         return this.positionEl || this.el;
15676     },
15677
15678     /**
15679      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15680      * This method fires the move event.
15681      * @param {Number} left The new left
15682      * @param {Number} top The new top
15683      * @returns {Roo.BoxComponent} this
15684      */
15685     setPosition : function(x, y){
15686         this.x = x;
15687         this.y = y;
15688         if(!this.boxReady){
15689             return this;
15690         }
15691         var adj = this.adjustPosition(x, y);
15692         var ax = adj.x, ay = adj.y;
15693
15694         var el = this.getPositionEl();
15695         if(ax !== undefined || ay !== undefined){
15696             if(ax !== undefined && ay !== undefined){
15697                 el.setLeftTop(ax, ay);
15698             }else if(ax !== undefined){
15699                 el.setLeft(ax);
15700             }else if(ay !== undefined){
15701                 el.setTop(ay);
15702             }
15703             this.onPosition(ax, ay);
15704             this.fireEvent('move', this, ax, ay);
15705         }
15706         return this;
15707     },
15708
15709     /**
15710      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15711      * This method fires the move event.
15712      * @param {Number} x The new x position
15713      * @param {Number} y The new y position
15714      * @returns {Roo.BoxComponent} this
15715      */
15716     setPagePosition : function(x, y){
15717         this.pageX = x;
15718         this.pageY = y;
15719         if(!this.boxReady){
15720             return;
15721         }
15722         if(x === undefined || y === undefined){ // cannot translate undefined points
15723             return;
15724         }
15725         var p = this.el.translatePoints(x, y);
15726         this.setPosition(p.left, p.top);
15727         return this;
15728     },
15729
15730     // private
15731     onRender : function(ct, position){
15732         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15733         if(this.resizeEl){
15734             this.resizeEl = Roo.get(this.resizeEl);
15735         }
15736         if(this.positionEl){
15737             this.positionEl = Roo.get(this.positionEl);
15738         }
15739     },
15740
15741     // private
15742     afterRender : function(){
15743         Roo.BoxComponent.superclass.afterRender.call(this);
15744         this.boxReady = true;
15745         this.setSize(this.width, this.height);
15746         if(this.x || this.y){
15747             this.setPosition(this.x, this.y);
15748         }
15749         if(this.pageX || this.pageY){
15750             this.setPagePosition(this.pageX, this.pageY);
15751         }
15752     },
15753
15754     /**
15755      * Force the component's size to recalculate based on the underlying element's current height and width.
15756      * @returns {Roo.BoxComponent} this
15757      */
15758     syncSize : function(){
15759         delete this.lastSize;
15760         this.setSize(this.el.getWidth(), this.el.getHeight());
15761         return this;
15762     },
15763
15764     /**
15765      * Called after the component is resized, this method is empty by default but can be implemented by any
15766      * subclass that needs to perform custom logic after a resize occurs.
15767      * @param {Number} adjWidth The box-adjusted width that was set
15768      * @param {Number} adjHeight The box-adjusted height that was set
15769      * @param {Number} rawWidth The width that was originally specified
15770      * @param {Number} rawHeight The height that was originally specified
15771      */
15772     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15773
15774     },
15775
15776     /**
15777      * Called after the component is moved, this method is empty by default but can be implemented by any
15778      * subclass that needs to perform custom logic after a move occurs.
15779      * @param {Number} x The new x position
15780      * @param {Number} y The new y position
15781      */
15782     onPosition : function(x, y){
15783
15784     },
15785
15786     // private
15787     adjustSize : function(w, h){
15788         if(this.autoWidth){
15789             w = 'auto';
15790         }
15791         if(this.autoHeight){
15792             h = 'auto';
15793         }
15794         return {width : w, height: h};
15795     },
15796
15797     // private
15798     adjustPosition : function(x, y){
15799         return {x : x, y: y};
15800     }
15801 });/*
15802  * Original code for Roojs - LGPL
15803  * <script type="text/javascript">
15804  */
15805  
15806 /**
15807  * @class Roo.XComponent
15808  * A delayed Element creator...
15809  * Or a way to group chunks of interface together.
15810  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15811  *  used in conjunction with XComponent.build() it will create an instance of each element,
15812  *  then call addxtype() to build the User interface.
15813  * 
15814  * Mypart.xyx = new Roo.XComponent({
15815
15816     parent : 'Mypart.xyz', // empty == document.element.!!
15817     order : '001',
15818     name : 'xxxx'
15819     region : 'xxxx'
15820     disabled : function() {} 
15821      
15822     tree : function() { // return an tree of xtype declared components
15823         var MODULE = this;
15824         return 
15825         {
15826             xtype : 'NestedLayoutPanel',
15827             // technicall
15828         }
15829      ]
15830  *})
15831  *
15832  *
15833  * It can be used to build a big heiracy, with parent etc.
15834  * or you can just use this to render a single compoent to a dom element
15835  * MYPART.render(Roo.Element | String(id) | dom_element )
15836  *
15837  *
15838  * Usage patterns.
15839  *
15840  * Classic Roo
15841  *
15842  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15843  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15844  *
15845  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15846  *
15847  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15848  * - if mulitple topModules exist, the last one is defined as the top module.
15849  *
15850  * Embeded Roo
15851  * 
15852  * When the top level or multiple modules are to embedded into a existing HTML page,
15853  * the parent element can container '#id' of the element where the module will be drawn.
15854  *
15855  * Bootstrap Roo
15856  *
15857  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15858  * it relies more on a include mechanism, where sub modules are included into an outer page.
15859  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15860  * 
15861  * Bootstrap Roo Included elements
15862  *
15863  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15864  * hence confusing the component builder as it thinks there are multiple top level elements. 
15865  *
15866  * 
15867  * 
15868  * @extends Roo.util.Observable
15869  * @constructor
15870  * @param cfg {Object} configuration of component
15871  * 
15872  */
15873 Roo.XComponent = function(cfg) {
15874     Roo.apply(this, cfg);
15875     this.addEvents({ 
15876         /**
15877              * @event built
15878              * Fires when this the componnt is built
15879              * @param {Roo.XComponent} c the component
15880              */
15881         'built' : true
15882         
15883     });
15884     this.region = this.region || 'center'; // default..
15885     Roo.XComponent.register(this);
15886     this.modules = false;
15887     this.el = false; // where the layout goes..
15888     
15889     
15890 }
15891 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15892     /**
15893      * @property el
15894      * The created element (with Roo.factory())
15895      * @type {Roo.Layout}
15896      */
15897     el  : false,
15898     
15899     /**
15900      * @property el
15901      * for BC  - use el in new code
15902      * @type {Roo.Layout}
15903      */
15904     panel : false,
15905     
15906     /**
15907      * @property layout
15908      * for BC  - use el in new code
15909      * @type {Roo.Layout}
15910      */
15911     layout : false,
15912     
15913      /**
15914      * @cfg {Function|boolean} disabled
15915      * If this module is disabled by some rule, return true from the funtion
15916      */
15917     disabled : false,
15918     
15919     /**
15920      * @cfg {String} parent 
15921      * Name of parent element which it get xtype added to..
15922      */
15923     parent: false,
15924     
15925     /**
15926      * @cfg {String} order
15927      * Used to set the order in which elements are created (usefull for multiple tabs)
15928      */
15929     
15930     order : false,
15931     /**
15932      * @cfg {String} name
15933      * String to display while loading.
15934      */
15935     name : false,
15936     /**
15937      * @cfg {String} region
15938      * Region to render component to (defaults to center)
15939      */
15940     region : 'center',
15941     
15942     /**
15943      * @cfg {Array} items
15944      * A single item array - the first element is the root of the tree..
15945      * It's done this way to stay compatible with the Xtype system...
15946      */
15947     items : false,
15948     
15949     /**
15950      * @property _tree
15951      * The method that retuns the tree of parts that make up this compoennt 
15952      * @type {function}
15953      */
15954     _tree  : false,
15955     
15956      /**
15957      * render
15958      * render element to dom or tree
15959      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15960      */
15961     
15962     render : function(el)
15963     {
15964         
15965         el = el || false;
15966         var hp = this.parent ? 1 : 0;
15967         Roo.debug &&  Roo.log(this);
15968         
15969         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15970             // if parent is a '#.....' string, then let's use that..
15971             var ename = this.parent.substr(1);
15972             this.parent = false;
15973             Roo.debug && Roo.log(ename);
15974             switch (ename) {
15975                 case 'bootstrap-body' :
15976                     if (typeof(Roo.bootstrap.Body) != 'undefined') {
15977                         this.parent = { el :  new  Roo.bootstrap.Body() };
15978                         Roo.debug && Roo.log("setting el to doc body");
15979                          
15980                     } else {
15981                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
15982                     }
15983                     break;
15984                 case 'bootstrap':
15985                     this.parent = { el : true};
15986                     // fall through
15987                 default:
15988                     el = Roo.get(ename);
15989                     break;
15990             }
15991                 
15992             
15993             if (!el && !this.parent) {
15994                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
15995                 return;
15996             }
15997         }
15998         Roo.debug && Roo.log("EL:");
15999         Roo.debug && Roo.log(el);
16000         Roo.debug && Roo.log("this.parent.el:");
16001         Roo.debug && Roo.log(this.parent.el);
16002         
16003         var tree = this._tree ? this._tree() : this.tree();
16004
16005         // altertive root elements ??? - we need a better way to indicate these.
16006         var is_alt = (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16007                         (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16008         
16009         if (!this.parent && is_alt) {
16010             //el = Roo.get(document.body);
16011             this.parent = { el : true };
16012         }
16013             
16014             
16015         
16016         if (!this.parent) {
16017             
16018             Roo.debug && Roo.log("no parent - creating one");
16019             
16020             el = el ? Roo.get(el) : false;      
16021             
16022             // it's a top level one..
16023             this.parent =  {
16024                 el : new Roo.BorderLayout(el || document.body, {
16025                 
16026                      center: {
16027                          titlebar: false,
16028                          autoScroll:false,
16029                          closeOnTab: true,
16030                          tabPosition: 'top',
16031                           //resizeTabs: true,
16032                          alwaysShowTabs: el && hp? false :  true,
16033                          hideTabs: el || !hp ? true :  false,
16034                          minTabWidth: 140
16035                      }
16036                  })
16037             }
16038         }
16039         
16040         if (!this.parent.el) {
16041                 // probably an old style ctor, which has been disabled.
16042                 return;
16043
16044         }
16045                 // The 'tree' method is  '_tree now' 
16046             
16047         tree.region = tree.region || this.region;
16048         
16049         if (this.parent.el === true) {
16050             // bootstrap... - body..
16051             this.parent.el = Roo.factory(tree);
16052         }
16053         
16054         this.el = this.parent.el.addxtype(tree);
16055         this.fireEvent('built', this);
16056         
16057         this.panel = this.el;
16058         this.layout = this.panel.layout;
16059         this.parentLayout = this.parent.layout  || false;  
16060          
16061     }
16062     
16063 });
16064
16065 Roo.apply(Roo.XComponent, {
16066     /**
16067      * @property  hideProgress
16068      * true to disable the building progress bar.. usefull on single page renders.
16069      * @type Boolean
16070      */
16071     hideProgress : false,
16072     /**
16073      * @property  buildCompleted
16074      * True when the builder has completed building the interface.
16075      * @type Boolean
16076      */
16077     buildCompleted : false,
16078      
16079     /**
16080      * @property  topModule
16081      * the upper most module - uses document.element as it's constructor.
16082      * @type Object
16083      */
16084      
16085     topModule  : false,
16086       
16087     /**
16088      * @property  modules
16089      * array of modules to be created by registration system.
16090      * @type {Array} of Roo.XComponent
16091      */
16092     
16093     modules : [],
16094     /**
16095      * @property  elmodules
16096      * array of modules to be created by which use #ID 
16097      * @type {Array} of Roo.XComponent
16098      */
16099      
16100     elmodules : [],
16101
16102      /**
16103      * @property  build_from_html
16104      * Build elements from html - used by bootstrap HTML stuff 
16105      *    - this is cleared after build is completed
16106      * @type {boolean} true  (default false)
16107      */
16108      
16109     build_from_html : false,
16110
16111     /**
16112      * Register components to be built later.
16113      *
16114      * This solves the following issues
16115      * - Building is not done on page load, but after an authentication process has occured.
16116      * - Interface elements are registered on page load
16117      * - Parent Interface elements may not be loaded before child, so this handles that..
16118      * 
16119      *
16120      * example:
16121      * 
16122      * MyApp.register({
16123           order : '000001',
16124           module : 'Pman.Tab.projectMgr',
16125           region : 'center',
16126           parent : 'Pman.layout',
16127           disabled : false,  // or use a function..
16128         })
16129      
16130      * * @param {Object} details about module
16131      */
16132     register : function(obj) {
16133                 
16134         Roo.XComponent.event.fireEvent('register', obj);
16135         switch(typeof(obj.disabled) ) {
16136                 
16137             case 'undefined':
16138                 break;
16139             
16140             case 'function':
16141                 if ( obj.disabled() ) {
16142                         return;
16143                 }
16144                 break;
16145             
16146             default:
16147                 if (obj.disabled) {
16148                         return;
16149                 }
16150                 break;
16151         }
16152                 
16153         this.modules.push(obj);
16154          
16155     },
16156     /**
16157      * convert a string to an object..
16158      * eg. 'AAA.BBB' -> finds AAA.BBB
16159
16160      */
16161     
16162     toObject : function(str)
16163     {
16164         if (!str || typeof(str) == 'object') {
16165             return str;
16166         }
16167         if (str.substring(0,1) == '#') {
16168             return str;
16169         }
16170
16171         var ar = str.split('.');
16172         var rt, o;
16173         rt = ar.shift();
16174             /** eval:var:o */
16175         try {
16176             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16177         } catch (e) {
16178             throw "Module not found : " + str;
16179         }
16180         
16181         if (o === false) {
16182             throw "Module not found : " + str;
16183         }
16184         Roo.each(ar, function(e) {
16185             if (typeof(o[e]) == 'undefined') {
16186                 throw "Module not found : " + str;
16187             }
16188             o = o[e];
16189         });
16190         
16191         return o;
16192         
16193     },
16194     
16195     
16196     /**
16197      * move modules into their correct place in the tree..
16198      * 
16199      */
16200     preBuild : function ()
16201     {
16202         var _t = this;
16203         Roo.each(this.modules , function (obj)
16204         {
16205             Roo.XComponent.event.fireEvent('beforebuild', obj);
16206             
16207             var opar = obj.parent;
16208             try { 
16209                 obj.parent = this.toObject(opar);
16210             } catch(e) {
16211                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16212                 return;
16213             }
16214             
16215             if (!obj.parent) {
16216                 Roo.debug && Roo.log("GOT top level module");
16217                 Roo.debug && Roo.log(obj);
16218                 obj.modules = new Roo.util.MixedCollection(false, 
16219                     function(o) { return o.order + '' }
16220                 );
16221                 this.topModule = obj;
16222                 return;
16223             }
16224                         // parent is a string (usually a dom element name..)
16225             if (typeof(obj.parent) == 'string') {
16226                 this.elmodules.push(obj);
16227                 return;
16228             }
16229             if (obj.parent.constructor != Roo.XComponent) {
16230                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16231             }
16232             if (!obj.parent.modules) {
16233                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16234                     function(o) { return o.order + '' }
16235                 );
16236             }
16237             if (obj.parent.disabled) {
16238                 obj.disabled = true;
16239             }
16240             obj.parent.modules.add(obj);
16241         }, this);
16242     },
16243     
16244      /**
16245      * make a list of modules to build.
16246      * @return {Array} list of modules. 
16247      */ 
16248     
16249     buildOrder : function()
16250     {
16251         var _this = this;
16252         var cmp = function(a,b) {   
16253             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16254         };
16255         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16256             throw "No top level modules to build";
16257         }
16258         
16259         // make a flat list in order of modules to build.
16260         var mods = this.topModule ? [ this.topModule ] : [];
16261                 
16262         
16263         // elmodules (is a list of DOM based modules )
16264         Roo.each(this.elmodules, function(e) {
16265             mods.push(e);
16266             if (!this.topModule &&
16267                 typeof(e.parent) == 'string' &&
16268                 e.parent.substring(0,1) == '#' &&
16269                 Roo.get(e.parent.substr(1))
16270                ) {
16271                 
16272                 _this.topModule = e;
16273             }
16274             
16275         });
16276
16277         
16278         // add modules to their parents..
16279         var addMod = function(m) {
16280             Roo.debug && Roo.log("build Order: add: " + m.name);
16281                 
16282             mods.push(m);
16283             if (m.modules && !m.disabled) {
16284                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16285                 m.modules.keySort('ASC',  cmp );
16286                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16287     
16288                 m.modules.each(addMod);
16289             } else {
16290                 Roo.debug && Roo.log("build Order: no child modules");
16291             }
16292             // not sure if this is used any more..
16293             if (m.finalize) {
16294                 m.finalize.name = m.name + " (clean up) ";
16295                 mods.push(m.finalize);
16296             }
16297             
16298         }
16299         if (this.topModule && this.topModule.modules) { 
16300             this.topModule.modules.keySort('ASC',  cmp );
16301             this.topModule.modules.each(addMod);
16302         } 
16303         return mods;
16304     },
16305     
16306      /**
16307      * Build the registered modules.
16308      * @param {Object} parent element.
16309      * @param {Function} optional method to call after module has been added.
16310      * 
16311      */ 
16312    
16313     build : function(opts) 
16314     {
16315         
16316         if (typeof(opts) != 'undefined') {
16317             Roo.apply(this,opts);
16318         }
16319         
16320         this.preBuild();
16321         var mods = this.buildOrder();
16322       
16323         //this.allmods = mods;
16324         //Roo.debug && Roo.log(mods);
16325         //return;
16326         if (!mods.length) { // should not happen
16327             throw "NO modules!!!";
16328         }
16329         
16330         
16331         var msg = "Building Interface...";
16332         // flash it up as modal - so we store the mask!?
16333         if (!this.hideProgress && Roo.MessageBox) {
16334             Roo.MessageBox.show({ title: 'loading' });
16335             Roo.MessageBox.show({
16336                title: "Please wait...",
16337                msg: msg,
16338                width:450,
16339                progress:true,
16340                closable:false,
16341                modal: false
16342               
16343             });
16344         }
16345         var total = mods.length;
16346         
16347         var _this = this;
16348         var progressRun = function() {
16349             if (!mods.length) {
16350                 Roo.debug && Roo.log('hide?');
16351                 if (!this.hideProgress && Roo.MessageBox) {
16352                     Roo.MessageBox.hide();
16353                 }
16354                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16355                 
16356                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16357                 
16358                 // THE END...
16359                 return false;   
16360             }
16361             
16362             var m = mods.shift();
16363             
16364             
16365             Roo.debug && Roo.log(m);
16366             // not sure if this is supported any more.. - modules that are are just function
16367             if (typeof(m) == 'function') { 
16368                 m.call(this);
16369                 return progressRun.defer(10, _this);
16370             } 
16371             
16372             
16373             msg = "Building Interface " + (total  - mods.length) + 
16374                     " of " + total + 
16375                     (m.name ? (' - ' + m.name) : '');
16376                         Roo.debug && Roo.log(msg);
16377             if (!this.hideProgress &&  Roo.MessageBox) { 
16378                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16379             }
16380             
16381          
16382             // is the module disabled?
16383             var disabled = (typeof(m.disabled) == 'function') ?
16384                 m.disabled.call(m.module.disabled) : m.disabled;    
16385             
16386             
16387             if (disabled) {
16388                 return progressRun(); // we do not update the display!
16389             }
16390             
16391             // now build 
16392             
16393                         
16394                         
16395             m.render();
16396             // it's 10 on top level, and 1 on others??? why...
16397             return progressRun.defer(10, _this);
16398              
16399         }
16400         progressRun.defer(1, _this);
16401      
16402         
16403         
16404     },
16405         
16406         
16407         /**
16408          * Event Object.
16409          *
16410          *
16411          */
16412         event: false, 
16413     /**
16414          * wrapper for event.on - aliased later..  
16415          * Typically use to register a event handler for register:
16416          *
16417          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16418          *
16419          */
16420     on : false
16421    
16422     
16423     
16424 });
16425
16426 Roo.XComponent.event = new Roo.util.Observable({
16427                 events : { 
16428                         /**
16429                          * @event register
16430                          * Fires when an Component is registered,
16431                          * set the disable property on the Component to stop registration.
16432                          * @param {Roo.XComponent} c the component being registerd.
16433                          * 
16434                          */
16435                         'register' : true,
16436             /**
16437                          * @event beforebuild
16438                          * Fires before each Component is built
16439                          * can be used to apply permissions.
16440                          * @param {Roo.XComponent} c the component being registerd.
16441                          * 
16442                          */
16443                         'beforebuild' : true,
16444                         /**
16445                          * @event buildcomplete
16446                          * Fires on the top level element when all elements have been built
16447                          * @param {Roo.XComponent} the top level component.
16448                          */
16449                         'buildcomplete' : true
16450                         
16451                 }
16452 });
16453
16454 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16455  /*
16456  * Based on:
16457  * Ext JS Library 1.1.1
16458  * Copyright(c) 2006-2007, Ext JS, LLC.
16459  *
16460  * Originally Released Under LGPL - original licence link has changed is not relivant.
16461  *
16462  * Fork - LGPL
16463  * <script type="text/javascript">
16464  */
16465
16466
16467
16468 /*
16469  * These classes are derivatives of the similarly named classes in the YUI Library.
16470  * The original license:
16471  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16472  * Code licensed under the BSD License:
16473  * http://developer.yahoo.net/yui/license.txt
16474  */
16475
16476 (function() {
16477
16478 var Event=Roo.EventManager;
16479 var Dom=Roo.lib.Dom;
16480
16481 /**
16482  * @class Roo.dd.DragDrop
16483  * @extends Roo.util.Observable
16484  * Defines the interface and base operation of items that that can be
16485  * dragged or can be drop targets.  It was designed to be extended, overriding
16486  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16487  * Up to three html elements can be associated with a DragDrop instance:
16488  * <ul>
16489  * <li>linked element: the element that is passed into the constructor.
16490  * This is the element which defines the boundaries for interaction with
16491  * other DragDrop objects.</li>
16492  * <li>handle element(s): The drag operation only occurs if the element that
16493  * was clicked matches a handle element.  By default this is the linked
16494  * element, but there are times that you will want only a portion of the
16495  * linked element to initiate the drag operation, and the setHandleElId()
16496  * method provides a way to define this.</li>
16497  * <li>drag element: this represents the element that would be moved along
16498  * with the cursor during a drag operation.  By default, this is the linked
16499  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16500  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16501  * </li>
16502  * </ul>
16503  * This class should not be instantiated until the onload event to ensure that
16504  * the associated elements are available.
16505  * The following would define a DragDrop obj that would interact with any
16506  * other DragDrop obj in the "group1" group:
16507  * <pre>
16508  *  dd = new Roo.dd.DragDrop("div1", "group1");
16509  * </pre>
16510  * Since none of the event handlers have been implemented, nothing would
16511  * actually happen if you were to run the code above.  Normally you would
16512  * override this class or one of the default implementations, but you can
16513  * also override the methods you want on an instance of the class...
16514  * <pre>
16515  *  dd.onDragDrop = function(e, id) {
16516  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16517  *  }
16518  * </pre>
16519  * @constructor
16520  * @param {String} id of the element that is linked to this instance
16521  * @param {String} sGroup the group of related DragDrop objects
16522  * @param {object} config an object containing configurable attributes
16523  *                Valid properties for DragDrop:
16524  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16525  */
16526 Roo.dd.DragDrop = function(id, sGroup, config) {
16527     if (id) {
16528         this.init(id, sGroup, config);
16529     }
16530     
16531 };
16532
16533 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16534
16535     /**
16536      * The id of the element associated with this object.  This is what we
16537      * refer to as the "linked element" because the size and position of
16538      * this element is used to determine when the drag and drop objects have
16539      * interacted.
16540      * @property id
16541      * @type String
16542      */
16543     id: null,
16544
16545     /**
16546      * Configuration attributes passed into the constructor
16547      * @property config
16548      * @type object
16549      */
16550     config: null,
16551
16552     /**
16553      * The id of the element that will be dragged.  By default this is same
16554      * as the linked element , but could be changed to another element. Ex:
16555      * Roo.dd.DDProxy
16556      * @property dragElId
16557      * @type String
16558      * @private
16559      */
16560     dragElId: null,
16561
16562     /**
16563      * the id of the element that initiates the drag operation.  By default
16564      * this is the linked element, but could be changed to be a child of this
16565      * element.  This lets us do things like only starting the drag when the
16566      * header element within the linked html element is clicked.
16567      * @property handleElId
16568      * @type String
16569      * @private
16570      */
16571     handleElId: null,
16572
16573     /**
16574      * An associative array of HTML tags that will be ignored if clicked.
16575      * @property invalidHandleTypes
16576      * @type {string: string}
16577      */
16578     invalidHandleTypes: null,
16579
16580     /**
16581      * An associative array of ids for elements that will be ignored if clicked
16582      * @property invalidHandleIds
16583      * @type {string: string}
16584      */
16585     invalidHandleIds: null,
16586
16587     /**
16588      * An indexted array of css class names for elements that will be ignored
16589      * if clicked.
16590      * @property invalidHandleClasses
16591      * @type string[]
16592      */
16593     invalidHandleClasses: null,
16594
16595     /**
16596      * The linked element's absolute X position at the time the drag was
16597      * started
16598      * @property startPageX
16599      * @type int
16600      * @private
16601      */
16602     startPageX: 0,
16603
16604     /**
16605      * The linked element's absolute X position at the time the drag was
16606      * started
16607      * @property startPageY
16608      * @type int
16609      * @private
16610      */
16611     startPageY: 0,
16612
16613     /**
16614      * The group defines a logical collection of DragDrop objects that are
16615      * related.  Instances only get events when interacting with other
16616      * DragDrop object in the same group.  This lets us define multiple
16617      * groups using a single DragDrop subclass if we want.
16618      * @property groups
16619      * @type {string: string}
16620      */
16621     groups: null,
16622
16623     /**
16624      * Individual drag/drop instances can be locked.  This will prevent
16625      * onmousedown start drag.
16626      * @property locked
16627      * @type boolean
16628      * @private
16629      */
16630     locked: false,
16631
16632     /**
16633      * Lock this instance
16634      * @method lock
16635      */
16636     lock: function() { this.locked = true; },
16637
16638     /**
16639      * Unlock this instace
16640      * @method unlock
16641      */
16642     unlock: function() { this.locked = false; },
16643
16644     /**
16645      * By default, all insances can be a drop target.  This can be disabled by
16646      * setting isTarget to false.
16647      * @method isTarget
16648      * @type boolean
16649      */
16650     isTarget: true,
16651
16652     /**
16653      * The padding configured for this drag and drop object for calculating
16654      * the drop zone intersection with this object.
16655      * @method padding
16656      * @type int[]
16657      */
16658     padding: null,
16659
16660     /**
16661      * Cached reference to the linked element
16662      * @property _domRef
16663      * @private
16664      */
16665     _domRef: null,
16666
16667     /**
16668      * Internal typeof flag
16669      * @property __ygDragDrop
16670      * @private
16671      */
16672     __ygDragDrop: true,
16673
16674     /**
16675      * Set to true when horizontal contraints are applied
16676      * @property constrainX
16677      * @type boolean
16678      * @private
16679      */
16680     constrainX: false,
16681
16682     /**
16683      * Set to true when vertical contraints are applied
16684      * @property constrainY
16685      * @type boolean
16686      * @private
16687      */
16688     constrainY: false,
16689
16690     /**
16691      * The left constraint
16692      * @property minX
16693      * @type int
16694      * @private
16695      */
16696     minX: 0,
16697
16698     /**
16699      * The right constraint
16700      * @property maxX
16701      * @type int
16702      * @private
16703      */
16704     maxX: 0,
16705
16706     /**
16707      * The up constraint
16708      * @property minY
16709      * @type int
16710      * @type int
16711      * @private
16712      */
16713     minY: 0,
16714
16715     /**
16716      * The down constraint
16717      * @property maxY
16718      * @type int
16719      * @private
16720      */
16721     maxY: 0,
16722
16723     /**
16724      * Maintain offsets when we resetconstraints.  Set to true when you want
16725      * the position of the element relative to its parent to stay the same
16726      * when the page changes
16727      *
16728      * @property maintainOffset
16729      * @type boolean
16730      */
16731     maintainOffset: false,
16732
16733     /**
16734      * Array of pixel locations the element will snap to if we specified a
16735      * horizontal graduation/interval.  This array is generated automatically
16736      * when you define a tick interval.
16737      * @property xTicks
16738      * @type int[]
16739      */
16740     xTicks: null,
16741
16742     /**
16743      * Array of pixel locations the element will snap to if we specified a
16744      * vertical graduation/interval.  This array is generated automatically
16745      * when you define a tick interval.
16746      * @property yTicks
16747      * @type int[]
16748      */
16749     yTicks: null,
16750
16751     /**
16752      * By default the drag and drop instance will only respond to the primary
16753      * button click (left button for a right-handed mouse).  Set to true to
16754      * allow drag and drop to start with any mouse click that is propogated
16755      * by the browser
16756      * @property primaryButtonOnly
16757      * @type boolean
16758      */
16759     primaryButtonOnly: true,
16760
16761     /**
16762      * The availabe property is false until the linked dom element is accessible.
16763      * @property available
16764      * @type boolean
16765      */
16766     available: false,
16767
16768     /**
16769      * By default, drags can only be initiated if the mousedown occurs in the
16770      * region the linked element is.  This is done in part to work around a
16771      * bug in some browsers that mis-report the mousedown if the previous
16772      * mouseup happened outside of the window.  This property is set to true
16773      * if outer handles are defined.
16774      *
16775      * @property hasOuterHandles
16776      * @type boolean
16777      * @default false
16778      */
16779     hasOuterHandles: false,
16780
16781     /**
16782      * Code that executes immediately before the startDrag event
16783      * @method b4StartDrag
16784      * @private
16785      */
16786     b4StartDrag: function(x, y) { },
16787
16788     /**
16789      * Abstract method called after a drag/drop object is clicked
16790      * and the drag or mousedown time thresholds have beeen met.
16791      * @method startDrag
16792      * @param {int} X click location
16793      * @param {int} Y click location
16794      */
16795     startDrag: function(x, y) { /* override this */ },
16796
16797     /**
16798      * Code that executes immediately before the onDrag event
16799      * @method b4Drag
16800      * @private
16801      */
16802     b4Drag: function(e) { },
16803
16804     /**
16805      * Abstract method called during the onMouseMove event while dragging an
16806      * object.
16807      * @method onDrag
16808      * @param {Event} e the mousemove event
16809      */
16810     onDrag: function(e) { /* override this */ },
16811
16812     /**
16813      * Abstract method called when this element fist begins hovering over
16814      * another DragDrop obj
16815      * @method onDragEnter
16816      * @param {Event} e the mousemove event
16817      * @param {String|DragDrop[]} id In POINT mode, the element
16818      * id this is hovering over.  In INTERSECT mode, an array of one or more
16819      * dragdrop items being hovered over.
16820      */
16821     onDragEnter: function(e, id) { /* override this */ },
16822
16823     /**
16824      * Code that executes immediately before the onDragOver event
16825      * @method b4DragOver
16826      * @private
16827      */
16828     b4DragOver: function(e) { },
16829
16830     /**
16831      * Abstract method called when this element is hovering over another
16832      * DragDrop obj
16833      * @method onDragOver
16834      * @param {Event} e the mousemove event
16835      * @param {String|DragDrop[]} id In POINT mode, the element
16836      * id this is hovering over.  In INTERSECT mode, an array of dd items
16837      * being hovered over.
16838      */
16839     onDragOver: function(e, id) { /* override this */ },
16840
16841     /**
16842      * Code that executes immediately before the onDragOut event
16843      * @method b4DragOut
16844      * @private
16845      */
16846     b4DragOut: function(e) { },
16847
16848     /**
16849      * Abstract method called when we are no longer hovering over an element
16850      * @method onDragOut
16851      * @param {Event} e the mousemove event
16852      * @param {String|DragDrop[]} id In POINT mode, the element
16853      * id this was hovering over.  In INTERSECT mode, an array of dd items
16854      * that the mouse is no longer over.
16855      */
16856     onDragOut: function(e, id) { /* override this */ },
16857
16858     /**
16859      * Code that executes immediately before the onDragDrop event
16860      * @method b4DragDrop
16861      * @private
16862      */
16863     b4DragDrop: function(e) { },
16864
16865     /**
16866      * Abstract method called when this item is dropped on another DragDrop
16867      * obj
16868      * @method onDragDrop
16869      * @param {Event} e the mouseup event
16870      * @param {String|DragDrop[]} id In POINT mode, the element
16871      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16872      * was dropped on.
16873      */
16874     onDragDrop: function(e, id) { /* override this */ },
16875
16876     /**
16877      * Abstract method called when this item is dropped on an area with no
16878      * drop target
16879      * @method onInvalidDrop
16880      * @param {Event} e the mouseup event
16881      */
16882     onInvalidDrop: function(e) { /* override this */ },
16883
16884     /**
16885      * Code that executes immediately before the endDrag event
16886      * @method b4EndDrag
16887      * @private
16888      */
16889     b4EndDrag: function(e) { },
16890
16891     /**
16892      * Fired when we are done dragging the object
16893      * @method endDrag
16894      * @param {Event} e the mouseup event
16895      */
16896     endDrag: function(e) { /* override this */ },
16897
16898     /**
16899      * Code executed immediately before the onMouseDown event
16900      * @method b4MouseDown
16901      * @param {Event} e the mousedown event
16902      * @private
16903      */
16904     b4MouseDown: function(e) {  },
16905
16906     /**
16907      * Event handler that fires when a drag/drop obj gets a mousedown
16908      * @method onMouseDown
16909      * @param {Event} e the mousedown event
16910      */
16911     onMouseDown: function(e) { /* override this */ },
16912
16913     /**
16914      * Event handler that fires when a drag/drop obj gets a mouseup
16915      * @method onMouseUp
16916      * @param {Event} e the mouseup event
16917      */
16918     onMouseUp: function(e) { /* override this */ },
16919
16920     /**
16921      * Override the onAvailable method to do what is needed after the initial
16922      * position was determined.
16923      * @method onAvailable
16924      */
16925     onAvailable: function () {
16926     },
16927
16928     /*
16929      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16930      * @type Object
16931      */
16932     defaultPadding : {left:0, right:0, top:0, bottom:0},
16933
16934     /*
16935      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16936  *
16937  * Usage:
16938  <pre><code>
16939  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16940                 { dragElId: "existingProxyDiv" });
16941  dd.startDrag = function(){
16942      this.constrainTo("parent-id");
16943  };
16944  </code></pre>
16945  * Or you can initalize it using the {@link Roo.Element} object:
16946  <pre><code>
16947  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16948      startDrag : function(){
16949          this.constrainTo("parent-id");
16950      }
16951  });
16952  </code></pre>
16953      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16954      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16955      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16956      * an object containing the sides to pad. For example: {right:10, bottom:10}
16957      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16958      */
16959     constrainTo : function(constrainTo, pad, inContent){
16960         if(typeof pad == "number"){
16961             pad = {left: pad, right:pad, top:pad, bottom:pad};
16962         }
16963         pad = pad || this.defaultPadding;
16964         var b = Roo.get(this.getEl()).getBox();
16965         var ce = Roo.get(constrainTo);
16966         var s = ce.getScroll();
16967         var c, cd = ce.dom;
16968         if(cd == document.body){
16969             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16970         }else{
16971             xy = ce.getXY();
16972             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16973         }
16974
16975
16976         var topSpace = b.y - c.y;
16977         var leftSpace = b.x - c.x;
16978
16979         this.resetConstraints();
16980         this.setXConstraint(leftSpace - (pad.left||0), // left
16981                 c.width - leftSpace - b.width - (pad.right||0) //right
16982         );
16983         this.setYConstraint(topSpace - (pad.top||0), //top
16984                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16985         );
16986     },
16987
16988     /**
16989      * Returns a reference to the linked element
16990      * @method getEl
16991      * @return {HTMLElement} the html element
16992      */
16993     getEl: function() {
16994         if (!this._domRef) {
16995             this._domRef = Roo.getDom(this.id);
16996         }
16997
16998         return this._domRef;
16999     },
17000
17001     /**
17002      * Returns a reference to the actual element to drag.  By default this is
17003      * the same as the html element, but it can be assigned to another
17004      * element. An example of this can be found in Roo.dd.DDProxy
17005      * @method getDragEl
17006      * @return {HTMLElement} the html element
17007      */
17008     getDragEl: function() {
17009         return Roo.getDom(this.dragElId);
17010     },
17011
17012     /**
17013      * Sets up the DragDrop object.  Must be called in the constructor of any
17014      * Roo.dd.DragDrop subclass
17015      * @method init
17016      * @param id the id of the linked element
17017      * @param {String} sGroup the group of related items
17018      * @param {object} config configuration attributes
17019      */
17020     init: function(id, sGroup, config) {
17021         this.initTarget(id, sGroup, config);
17022         if (!Roo.isTouch) {
17023             Event.on(this.id, "mousedown", this.handleMouseDown, this);
17024         }
17025         Event.on(this.id, "touchstart", this.handleMouseDown, this);
17026         // Event.on(this.id, "selectstart", Event.preventDefault);
17027     },
17028
17029     /**
17030      * Initializes Targeting functionality only... the object does not
17031      * get a mousedown handler.
17032      * @method initTarget
17033      * @param id the id of the linked element
17034      * @param {String} sGroup the group of related items
17035      * @param {object} config configuration attributes
17036      */
17037     initTarget: function(id, sGroup, config) {
17038
17039         // configuration attributes
17040         this.config = config || {};
17041
17042         // create a local reference to the drag and drop manager
17043         this.DDM = Roo.dd.DDM;
17044         // initialize the groups array
17045         this.groups = {};
17046
17047         // assume that we have an element reference instead of an id if the
17048         // parameter is not a string
17049         if (typeof id !== "string") {
17050             id = Roo.id(id);
17051         }
17052
17053         // set the id
17054         this.id = id;
17055
17056         // add to an interaction group
17057         this.addToGroup((sGroup) ? sGroup : "default");
17058
17059         // We don't want to register this as the handle with the manager
17060         // so we just set the id rather than calling the setter.
17061         this.handleElId = id;
17062
17063         // the linked element is the element that gets dragged by default
17064         this.setDragElId(id);
17065
17066         // by default, clicked anchors will not start drag operations.
17067         this.invalidHandleTypes = { A: "A" };
17068         this.invalidHandleIds = {};
17069         this.invalidHandleClasses = [];
17070
17071         this.applyConfig();
17072
17073         this.handleOnAvailable();
17074     },
17075
17076     /**
17077      * Applies the configuration parameters that were passed into the constructor.
17078      * This is supposed to happen at each level through the inheritance chain.  So
17079      * a DDProxy implentation will execute apply config on DDProxy, DD, and
17080      * DragDrop in order to get all of the parameters that are available in
17081      * each object.
17082      * @method applyConfig
17083      */
17084     applyConfig: function() {
17085
17086         // configurable properties:
17087         //    padding, isTarget, maintainOffset, primaryButtonOnly
17088         this.padding           = this.config.padding || [0, 0, 0, 0];
17089         this.isTarget          = (this.config.isTarget !== false);
17090         this.maintainOffset    = (this.config.maintainOffset);
17091         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
17092
17093     },
17094
17095     /**
17096      * Executed when the linked element is available
17097      * @method handleOnAvailable
17098      * @private
17099      */
17100     handleOnAvailable: function() {
17101         this.available = true;
17102         this.resetConstraints();
17103         this.onAvailable();
17104     },
17105
17106      /**
17107      * Configures the padding for the target zone in px.  Effectively expands
17108      * (or reduces) the virtual object size for targeting calculations.
17109      * Supports css-style shorthand; if only one parameter is passed, all sides
17110      * will have that padding, and if only two are passed, the top and bottom
17111      * will have the first param, the left and right the second.
17112      * @method setPadding
17113      * @param {int} iTop    Top pad
17114      * @param {int} iRight  Right pad
17115      * @param {int} iBot    Bot pad
17116      * @param {int} iLeft   Left pad
17117      */
17118     setPadding: function(iTop, iRight, iBot, iLeft) {
17119         // this.padding = [iLeft, iRight, iTop, iBot];
17120         if (!iRight && 0 !== iRight) {
17121             this.padding = [iTop, iTop, iTop, iTop];
17122         } else if (!iBot && 0 !== iBot) {
17123             this.padding = [iTop, iRight, iTop, iRight];
17124         } else {
17125             this.padding = [iTop, iRight, iBot, iLeft];
17126         }
17127     },
17128
17129     /**
17130      * Stores the initial placement of the linked element.
17131      * @method setInitialPosition
17132      * @param {int} diffX   the X offset, default 0
17133      * @param {int} diffY   the Y offset, default 0
17134      */
17135     setInitPosition: function(diffX, diffY) {
17136         var el = this.getEl();
17137
17138         if (!this.DDM.verifyEl(el)) {
17139             return;
17140         }
17141
17142         var dx = diffX || 0;
17143         var dy = diffY || 0;
17144
17145         var p = Dom.getXY( el );
17146
17147         this.initPageX = p[0] - dx;
17148         this.initPageY = p[1] - dy;
17149
17150         this.lastPageX = p[0];
17151         this.lastPageY = p[1];
17152
17153
17154         this.setStartPosition(p);
17155     },
17156
17157     /**
17158      * Sets the start position of the element.  This is set when the obj
17159      * is initialized, the reset when a drag is started.
17160      * @method setStartPosition
17161      * @param pos current position (from previous lookup)
17162      * @private
17163      */
17164     setStartPosition: function(pos) {
17165         var p = pos || Dom.getXY( this.getEl() );
17166         this.deltaSetXY = null;
17167
17168         this.startPageX = p[0];
17169         this.startPageY = p[1];
17170     },
17171
17172     /**
17173      * Add this instance to a group of related drag/drop objects.  All
17174      * instances belong to at least one group, and can belong to as many
17175      * groups as needed.
17176      * @method addToGroup
17177      * @param sGroup {string} the name of the group
17178      */
17179     addToGroup: function(sGroup) {
17180         this.groups[sGroup] = true;
17181         this.DDM.regDragDrop(this, sGroup);
17182     },
17183
17184     /**
17185      * Remove's this instance from the supplied interaction group
17186      * @method removeFromGroup
17187      * @param {string}  sGroup  The group to drop
17188      */
17189     removeFromGroup: function(sGroup) {
17190         if (this.groups[sGroup]) {
17191             delete this.groups[sGroup];
17192         }
17193
17194         this.DDM.removeDDFromGroup(this, sGroup);
17195     },
17196
17197     /**
17198      * Allows you to specify that an element other than the linked element
17199      * will be moved with the cursor during a drag
17200      * @method setDragElId
17201      * @param id {string} the id of the element that will be used to initiate the drag
17202      */
17203     setDragElId: function(id) {
17204         this.dragElId = id;
17205     },
17206
17207     /**
17208      * Allows you to specify a child of the linked element that should be
17209      * used to initiate the drag operation.  An example of this would be if
17210      * you have a content div with text and links.  Clicking anywhere in the
17211      * content area would normally start the drag operation.  Use this method
17212      * to specify that an element inside of the content div is the element
17213      * that starts the drag operation.
17214      * @method setHandleElId
17215      * @param id {string} the id of the element that will be used to
17216      * initiate the drag.
17217      */
17218     setHandleElId: function(id) {
17219         if (typeof id !== "string") {
17220             id = Roo.id(id);
17221         }
17222         this.handleElId = id;
17223         this.DDM.regHandle(this.id, id);
17224     },
17225
17226     /**
17227      * Allows you to set an element outside of the linked element as a drag
17228      * handle
17229      * @method setOuterHandleElId
17230      * @param id the id of the element that will be used to initiate the drag
17231      */
17232     setOuterHandleElId: function(id) {
17233         if (typeof id !== "string") {
17234             id = Roo.id(id);
17235         }
17236         Event.on(id, "mousedown",
17237                 this.handleMouseDown, this);
17238         this.setHandleElId(id);
17239
17240         this.hasOuterHandles = true;
17241     },
17242
17243     /**
17244      * Remove all drag and drop hooks for this element
17245      * @method unreg
17246      */
17247     unreg: function() {
17248         Event.un(this.id, "mousedown",
17249                 this.handleMouseDown);
17250         Event.un(this.id, "touchstart",
17251                 this.handleMouseDown);
17252         this._domRef = null;
17253         this.DDM._remove(this);
17254     },
17255
17256     destroy : function(){
17257         this.unreg();
17258     },
17259
17260     /**
17261      * Returns true if this instance is locked, or the drag drop mgr is locked
17262      * (meaning that all drag/drop is disabled on the page.)
17263      * @method isLocked
17264      * @return {boolean} true if this obj or all drag/drop is locked, else
17265      * false
17266      */
17267     isLocked: function() {
17268         return (this.DDM.isLocked() || this.locked);
17269     },
17270
17271     /**
17272      * Fired when this object is clicked
17273      * @method handleMouseDown
17274      * @param {Event} e
17275      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17276      * @private
17277      */
17278     handleMouseDown: function(e, oDD){
17279      
17280         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17281             //Roo.log('not touch/ button !=0');
17282             return;
17283         }
17284         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17285             return; // double touch..
17286         }
17287         
17288
17289         if (this.isLocked()) {
17290             //Roo.log('locked');
17291             return;
17292         }
17293
17294         this.DDM.refreshCache(this.groups);
17295 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17296         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17297         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17298             //Roo.log('no outer handes or not over target');
17299                 // do nothing.
17300         } else {
17301 //            Roo.log('check validator');
17302             if (this.clickValidator(e)) {
17303 //                Roo.log('validate success');
17304                 // set the initial element position
17305                 this.setStartPosition();
17306
17307
17308                 this.b4MouseDown(e);
17309                 this.onMouseDown(e);
17310
17311                 this.DDM.handleMouseDown(e, this);
17312
17313                 this.DDM.stopEvent(e);
17314             } else {
17315
17316
17317             }
17318         }
17319     },
17320
17321     clickValidator: function(e) {
17322         var target = e.getTarget();
17323         return ( this.isValidHandleChild(target) &&
17324                     (this.id == this.handleElId ||
17325                         this.DDM.handleWasClicked(target, this.id)) );
17326     },
17327
17328     /**
17329      * Allows you to specify a tag name that should not start a drag operation
17330      * when clicked.  This is designed to facilitate embedding links within a
17331      * drag handle that do something other than start the drag.
17332      * @method addInvalidHandleType
17333      * @param {string} tagName the type of element to exclude
17334      */
17335     addInvalidHandleType: function(tagName) {
17336         var type = tagName.toUpperCase();
17337         this.invalidHandleTypes[type] = type;
17338     },
17339
17340     /**
17341      * Lets you to specify an element id for a child of a drag handle
17342      * that should not initiate a drag
17343      * @method addInvalidHandleId
17344      * @param {string} id the element id of the element you wish to ignore
17345      */
17346     addInvalidHandleId: function(id) {
17347         if (typeof id !== "string") {
17348             id = Roo.id(id);
17349         }
17350         this.invalidHandleIds[id] = id;
17351     },
17352
17353     /**
17354      * Lets you specify a css class of elements that will not initiate a drag
17355      * @method addInvalidHandleClass
17356      * @param {string} cssClass the class of the elements you wish to ignore
17357      */
17358     addInvalidHandleClass: function(cssClass) {
17359         this.invalidHandleClasses.push(cssClass);
17360     },
17361
17362     /**
17363      * Unsets an excluded tag name set by addInvalidHandleType
17364      * @method removeInvalidHandleType
17365      * @param {string} tagName the type of element to unexclude
17366      */
17367     removeInvalidHandleType: function(tagName) {
17368         var type = tagName.toUpperCase();
17369         // this.invalidHandleTypes[type] = null;
17370         delete this.invalidHandleTypes[type];
17371     },
17372
17373     /**
17374      * Unsets an invalid handle id
17375      * @method removeInvalidHandleId
17376      * @param {string} id the id of the element to re-enable
17377      */
17378     removeInvalidHandleId: function(id) {
17379         if (typeof id !== "string") {
17380             id = Roo.id(id);
17381         }
17382         delete this.invalidHandleIds[id];
17383     },
17384
17385     /**
17386      * Unsets an invalid css class
17387      * @method removeInvalidHandleClass
17388      * @param {string} cssClass the class of the element(s) you wish to
17389      * re-enable
17390      */
17391     removeInvalidHandleClass: function(cssClass) {
17392         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17393             if (this.invalidHandleClasses[i] == cssClass) {
17394                 delete this.invalidHandleClasses[i];
17395             }
17396         }
17397     },
17398
17399     /**
17400      * Checks the tag exclusion list to see if this click should be ignored
17401      * @method isValidHandleChild
17402      * @param {HTMLElement} node the HTMLElement to evaluate
17403      * @return {boolean} true if this is a valid tag type, false if not
17404      */
17405     isValidHandleChild: function(node) {
17406
17407         var valid = true;
17408         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17409         var nodeName;
17410         try {
17411             nodeName = node.nodeName.toUpperCase();
17412         } catch(e) {
17413             nodeName = node.nodeName;
17414         }
17415         valid = valid && !this.invalidHandleTypes[nodeName];
17416         valid = valid && !this.invalidHandleIds[node.id];
17417
17418         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17419             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17420         }
17421
17422
17423         return valid;
17424
17425     },
17426
17427     /**
17428      * Create the array of horizontal tick marks if an interval was specified
17429      * in setXConstraint().
17430      * @method setXTicks
17431      * @private
17432      */
17433     setXTicks: function(iStartX, iTickSize) {
17434         this.xTicks = [];
17435         this.xTickSize = iTickSize;
17436
17437         var tickMap = {};
17438
17439         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17440             if (!tickMap[i]) {
17441                 this.xTicks[this.xTicks.length] = i;
17442                 tickMap[i] = true;
17443             }
17444         }
17445
17446         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17447             if (!tickMap[i]) {
17448                 this.xTicks[this.xTicks.length] = i;
17449                 tickMap[i] = true;
17450             }
17451         }
17452
17453         this.xTicks.sort(this.DDM.numericSort) ;
17454     },
17455
17456     /**
17457      * Create the array of vertical tick marks if an interval was specified in
17458      * setYConstraint().
17459      * @method setYTicks
17460      * @private
17461      */
17462     setYTicks: function(iStartY, iTickSize) {
17463         this.yTicks = [];
17464         this.yTickSize = iTickSize;
17465
17466         var tickMap = {};
17467
17468         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17469             if (!tickMap[i]) {
17470                 this.yTicks[this.yTicks.length] = i;
17471                 tickMap[i] = true;
17472             }
17473         }
17474
17475         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17476             if (!tickMap[i]) {
17477                 this.yTicks[this.yTicks.length] = i;
17478                 tickMap[i] = true;
17479             }
17480         }
17481
17482         this.yTicks.sort(this.DDM.numericSort) ;
17483     },
17484
17485     /**
17486      * By default, the element can be dragged any place on the screen.  Use
17487      * this method to limit the horizontal travel of the element.  Pass in
17488      * 0,0 for the parameters if you want to lock the drag to the y axis.
17489      * @method setXConstraint
17490      * @param {int} iLeft the number of pixels the element can move to the left
17491      * @param {int} iRight the number of pixels the element can move to the
17492      * right
17493      * @param {int} iTickSize optional parameter for specifying that the
17494      * element
17495      * should move iTickSize pixels at a time.
17496      */
17497     setXConstraint: function(iLeft, iRight, iTickSize) {
17498         this.leftConstraint = iLeft;
17499         this.rightConstraint = iRight;
17500
17501         this.minX = this.initPageX - iLeft;
17502         this.maxX = this.initPageX + iRight;
17503         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17504
17505         this.constrainX = true;
17506     },
17507
17508     /**
17509      * Clears any constraints applied to this instance.  Also clears ticks
17510      * since they can't exist independent of a constraint at this time.
17511      * @method clearConstraints
17512      */
17513     clearConstraints: function() {
17514         this.constrainX = false;
17515         this.constrainY = false;
17516         this.clearTicks();
17517     },
17518
17519     /**
17520      * Clears any tick interval defined for this instance
17521      * @method clearTicks
17522      */
17523     clearTicks: function() {
17524         this.xTicks = null;
17525         this.yTicks = null;
17526         this.xTickSize = 0;
17527         this.yTickSize = 0;
17528     },
17529
17530     /**
17531      * By default, the element can be dragged any place on the screen.  Set
17532      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17533      * parameters if you want to lock the drag to the x axis.
17534      * @method setYConstraint
17535      * @param {int} iUp the number of pixels the element can move up
17536      * @param {int} iDown the number of pixels the element can move down
17537      * @param {int} iTickSize optional parameter for specifying that the
17538      * element should move iTickSize pixels at a time.
17539      */
17540     setYConstraint: function(iUp, iDown, iTickSize) {
17541         this.topConstraint = iUp;
17542         this.bottomConstraint = iDown;
17543
17544         this.minY = this.initPageY - iUp;
17545         this.maxY = this.initPageY + iDown;
17546         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17547
17548         this.constrainY = true;
17549
17550     },
17551
17552     /**
17553      * resetConstraints must be called if you manually reposition a dd element.
17554      * @method resetConstraints
17555      * @param {boolean} maintainOffset
17556      */
17557     resetConstraints: function() {
17558
17559
17560         // Maintain offsets if necessary
17561         if (this.initPageX || this.initPageX === 0) {
17562             // figure out how much this thing has moved
17563             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17564             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17565
17566             this.setInitPosition(dx, dy);
17567
17568         // This is the first time we have detected the element's position
17569         } else {
17570             this.setInitPosition();
17571         }
17572
17573         if (this.constrainX) {
17574             this.setXConstraint( this.leftConstraint,
17575                                  this.rightConstraint,
17576                                  this.xTickSize        );
17577         }
17578
17579         if (this.constrainY) {
17580             this.setYConstraint( this.topConstraint,
17581                                  this.bottomConstraint,
17582                                  this.yTickSize         );
17583         }
17584     },
17585
17586     /**
17587      * Normally the drag element is moved pixel by pixel, but we can specify
17588      * that it move a number of pixels at a time.  This method resolves the
17589      * location when we have it set up like this.
17590      * @method getTick
17591      * @param {int} val where we want to place the object
17592      * @param {int[]} tickArray sorted array of valid points
17593      * @return {int} the closest tick
17594      * @private
17595      */
17596     getTick: function(val, tickArray) {
17597
17598         if (!tickArray) {
17599             // If tick interval is not defined, it is effectively 1 pixel,
17600             // so we return the value passed to us.
17601             return val;
17602         } else if (tickArray[0] >= val) {
17603             // The value is lower than the first tick, so we return the first
17604             // tick.
17605             return tickArray[0];
17606         } else {
17607             for (var i=0, len=tickArray.length; i<len; ++i) {
17608                 var next = i + 1;
17609                 if (tickArray[next] && tickArray[next] >= val) {
17610                     var diff1 = val - tickArray[i];
17611                     var diff2 = tickArray[next] - val;
17612                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17613                 }
17614             }
17615
17616             // The value is larger than the last tick, so we return the last
17617             // tick.
17618             return tickArray[tickArray.length - 1];
17619         }
17620     },
17621
17622     /**
17623      * toString method
17624      * @method toString
17625      * @return {string} string representation of the dd obj
17626      */
17627     toString: function() {
17628         return ("DragDrop " + this.id);
17629     }
17630
17631 });
17632
17633 })();
17634 /*
17635  * Based on:
17636  * Ext JS Library 1.1.1
17637  * Copyright(c) 2006-2007, Ext JS, LLC.
17638  *
17639  * Originally Released Under LGPL - original licence link has changed is not relivant.
17640  *
17641  * Fork - LGPL
17642  * <script type="text/javascript">
17643  */
17644
17645
17646 /**
17647  * The drag and drop utility provides a framework for building drag and drop
17648  * applications.  In addition to enabling drag and drop for specific elements,
17649  * the drag and drop elements are tracked by the manager class, and the
17650  * interactions between the various elements are tracked during the drag and
17651  * the implementing code is notified about these important moments.
17652  */
17653
17654 // Only load the library once.  Rewriting the manager class would orphan
17655 // existing drag and drop instances.
17656 if (!Roo.dd.DragDropMgr) {
17657
17658 /**
17659  * @class Roo.dd.DragDropMgr
17660  * DragDropMgr is a singleton that tracks the element interaction for
17661  * all DragDrop items in the window.  Generally, you will not call
17662  * this class directly, but it does have helper methods that could
17663  * be useful in your DragDrop implementations.
17664  * @singleton
17665  */
17666 Roo.dd.DragDropMgr = function() {
17667
17668     var Event = Roo.EventManager;
17669
17670     return {
17671
17672         /**
17673          * Two dimensional Array of registered DragDrop objects.  The first
17674          * dimension is the DragDrop item group, the second the DragDrop
17675          * object.
17676          * @property ids
17677          * @type {string: string}
17678          * @private
17679          * @static
17680          */
17681         ids: {},
17682
17683         /**
17684          * Array of element ids defined as drag handles.  Used to determine
17685          * if the element that generated the mousedown event is actually the
17686          * handle and not the html element itself.
17687          * @property handleIds
17688          * @type {string: string}
17689          * @private
17690          * @static
17691          */
17692         handleIds: {},
17693
17694         /**
17695          * the DragDrop object that is currently being dragged
17696          * @property dragCurrent
17697          * @type DragDrop
17698          * @private
17699          * @static
17700          **/
17701         dragCurrent: null,
17702
17703         /**
17704          * the DragDrop object(s) that are being hovered over
17705          * @property dragOvers
17706          * @type Array
17707          * @private
17708          * @static
17709          */
17710         dragOvers: {},
17711
17712         /**
17713          * the X distance between the cursor and the object being dragged
17714          * @property deltaX
17715          * @type int
17716          * @private
17717          * @static
17718          */
17719         deltaX: 0,
17720
17721         /**
17722          * the Y distance between the cursor and the object being dragged
17723          * @property deltaY
17724          * @type int
17725          * @private
17726          * @static
17727          */
17728         deltaY: 0,
17729
17730         /**
17731          * Flag to determine if we should prevent the default behavior of the
17732          * events we define. By default this is true, but this can be set to
17733          * false if you need the default behavior (not recommended)
17734          * @property preventDefault
17735          * @type boolean
17736          * @static
17737          */
17738         preventDefault: true,
17739
17740         /**
17741          * Flag to determine if we should stop the propagation of the events
17742          * we generate. This is true by default but you may want to set it to
17743          * false if the html element contains other features that require the
17744          * mouse click.
17745          * @property stopPropagation
17746          * @type boolean
17747          * @static
17748          */
17749         stopPropagation: true,
17750
17751         /**
17752          * Internal flag that is set to true when drag and drop has been
17753          * intialized
17754          * @property initialized
17755          * @private
17756          * @static
17757          */
17758         initalized: false,
17759
17760         /**
17761          * All drag and drop can be disabled.
17762          * @property locked
17763          * @private
17764          * @static
17765          */
17766         locked: false,
17767
17768         /**
17769          * Called the first time an element is registered.
17770          * @method init
17771          * @private
17772          * @static
17773          */
17774         init: function() {
17775             this.initialized = true;
17776         },
17777
17778         /**
17779          * In point mode, drag and drop interaction is defined by the
17780          * location of the cursor during the drag/drop
17781          * @property POINT
17782          * @type int
17783          * @static
17784          */
17785         POINT: 0,
17786
17787         /**
17788          * In intersect mode, drag and drop interactio nis defined by the
17789          * overlap of two or more drag and drop objects.
17790          * @property INTERSECT
17791          * @type int
17792          * @static
17793          */
17794         INTERSECT: 1,
17795
17796         /**
17797          * The current drag and drop mode.  Default: POINT
17798          * @property mode
17799          * @type int
17800          * @static
17801          */
17802         mode: 0,
17803
17804         /**
17805          * Runs method on all drag and drop objects
17806          * @method _execOnAll
17807          * @private
17808          * @static
17809          */
17810         _execOnAll: function(sMethod, args) {
17811             for (var i in this.ids) {
17812                 for (var j in this.ids[i]) {
17813                     var oDD = this.ids[i][j];
17814                     if (! this.isTypeOfDD(oDD)) {
17815                         continue;
17816                     }
17817                     oDD[sMethod].apply(oDD, args);
17818                 }
17819             }
17820         },
17821
17822         /**
17823          * Drag and drop initialization.  Sets up the global event handlers
17824          * @method _onLoad
17825          * @private
17826          * @static
17827          */
17828         _onLoad: function() {
17829
17830             this.init();
17831
17832             if (!Roo.isTouch) {
17833                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17834                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17835             }
17836             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17837             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17838             
17839             Event.on(window,   "unload",    this._onUnload, this, true);
17840             Event.on(window,   "resize",    this._onResize, this, true);
17841             // Event.on(window,   "mouseout",    this._test);
17842
17843         },
17844
17845         /**
17846          * Reset constraints on all drag and drop objs
17847          * @method _onResize
17848          * @private
17849          * @static
17850          */
17851         _onResize: function(e) {
17852             this._execOnAll("resetConstraints", []);
17853         },
17854
17855         /**
17856          * Lock all drag and drop functionality
17857          * @method lock
17858          * @static
17859          */
17860         lock: function() { this.locked = true; },
17861
17862         /**
17863          * Unlock all drag and drop functionality
17864          * @method unlock
17865          * @static
17866          */
17867         unlock: function() { this.locked = false; },
17868
17869         /**
17870          * Is drag and drop locked?
17871          * @method isLocked
17872          * @return {boolean} True if drag and drop is locked, false otherwise.
17873          * @static
17874          */
17875         isLocked: function() { return this.locked; },
17876
17877         /**
17878          * Location cache that is set for all drag drop objects when a drag is
17879          * initiated, cleared when the drag is finished.
17880          * @property locationCache
17881          * @private
17882          * @static
17883          */
17884         locationCache: {},
17885
17886         /**
17887          * Set useCache to false if you want to force object the lookup of each
17888          * drag and drop linked element constantly during a drag.
17889          * @property useCache
17890          * @type boolean
17891          * @static
17892          */
17893         useCache: true,
17894
17895         /**
17896          * The number of pixels that the mouse needs to move after the
17897          * mousedown before the drag is initiated.  Default=3;
17898          * @property clickPixelThresh
17899          * @type int
17900          * @static
17901          */
17902         clickPixelThresh: 3,
17903
17904         /**
17905          * The number of milliseconds after the mousedown event to initiate the
17906          * drag if we don't get a mouseup event. Default=1000
17907          * @property clickTimeThresh
17908          * @type int
17909          * @static
17910          */
17911         clickTimeThresh: 350,
17912
17913         /**
17914          * Flag that indicates that either the drag pixel threshold or the
17915          * mousdown time threshold has been met
17916          * @property dragThreshMet
17917          * @type boolean
17918          * @private
17919          * @static
17920          */
17921         dragThreshMet: false,
17922
17923         /**
17924          * Timeout used for the click time threshold
17925          * @property clickTimeout
17926          * @type Object
17927          * @private
17928          * @static
17929          */
17930         clickTimeout: null,
17931
17932         /**
17933          * The X position of the mousedown event stored for later use when a
17934          * drag threshold is met.
17935          * @property startX
17936          * @type int
17937          * @private
17938          * @static
17939          */
17940         startX: 0,
17941
17942         /**
17943          * The Y position of the mousedown event stored for later use when a
17944          * drag threshold is met.
17945          * @property startY
17946          * @type int
17947          * @private
17948          * @static
17949          */
17950         startY: 0,
17951
17952         /**
17953          * Each DragDrop instance must be registered with the DragDropMgr.
17954          * This is executed in DragDrop.init()
17955          * @method regDragDrop
17956          * @param {DragDrop} oDD the DragDrop object to register
17957          * @param {String} sGroup the name of the group this element belongs to
17958          * @static
17959          */
17960         regDragDrop: function(oDD, sGroup) {
17961             if (!this.initialized) { this.init(); }
17962
17963             if (!this.ids[sGroup]) {
17964                 this.ids[sGroup] = {};
17965             }
17966             this.ids[sGroup][oDD.id] = oDD;
17967         },
17968
17969         /**
17970          * Removes the supplied dd instance from the supplied group. Executed
17971          * by DragDrop.removeFromGroup, so don't call this function directly.
17972          * @method removeDDFromGroup
17973          * @private
17974          * @static
17975          */
17976         removeDDFromGroup: function(oDD, sGroup) {
17977             if (!this.ids[sGroup]) {
17978                 this.ids[sGroup] = {};
17979             }
17980
17981             var obj = this.ids[sGroup];
17982             if (obj && obj[oDD.id]) {
17983                 delete obj[oDD.id];
17984             }
17985         },
17986
17987         /**
17988          * Unregisters a drag and drop item.  This is executed in
17989          * DragDrop.unreg, use that method instead of calling this directly.
17990          * @method _remove
17991          * @private
17992          * @static
17993          */
17994         _remove: function(oDD) {
17995             for (var g in oDD.groups) {
17996                 if (g && this.ids[g][oDD.id]) {
17997                     delete this.ids[g][oDD.id];
17998                 }
17999             }
18000             delete this.handleIds[oDD.id];
18001         },
18002
18003         /**
18004          * Each DragDrop handle element must be registered.  This is done
18005          * automatically when executing DragDrop.setHandleElId()
18006          * @method regHandle
18007          * @param {String} sDDId the DragDrop id this element is a handle for
18008          * @param {String} sHandleId the id of the element that is the drag
18009          * handle
18010          * @static
18011          */
18012         regHandle: function(sDDId, sHandleId) {
18013             if (!this.handleIds[sDDId]) {
18014                 this.handleIds[sDDId] = {};
18015             }
18016             this.handleIds[sDDId][sHandleId] = sHandleId;
18017         },
18018
18019         /**
18020          * Utility function to determine if a given element has been
18021          * registered as a drag drop item.
18022          * @method isDragDrop
18023          * @param {String} id the element id to check
18024          * @return {boolean} true if this element is a DragDrop item,
18025          * false otherwise
18026          * @static
18027          */
18028         isDragDrop: function(id) {
18029             return ( this.getDDById(id) ) ? true : false;
18030         },
18031
18032         /**
18033          * Returns the drag and drop instances that are in all groups the
18034          * passed in instance belongs to.
18035          * @method getRelated
18036          * @param {DragDrop} p_oDD the obj to get related data for
18037          * @param {boolean} bTargetsOnly if true, only return targetable objs
18038          * @return {DragDrop[]} the related instances
18039          * @static
18040          */
18041         getRelated: function(p_oDD, bTargetsOnly) {
18042             var oDDs = [];
18043             for (var i in p_oDD.groups) {
18044                 for (j in this.ids[i]) {
18045                     var dd = this.ids[i][j];
18046                     if (! this.isTypeOfDD(dd)) {
18047                         continue;
18048                     }
18049                     if (!bTargetsOnly || dd.isTarget) {
18050                         oDDs[oDDs.length] = dd;
18051                     }
18052                 }
18053             }
18054
18055             return oDDs;
18056         },
18057
18058         /**
18059          * Returns true if the specified dd target is a legal target for
18060          * the specifice drag obj
18061          * @method isLegalTarget
18062          * @param {DragDrop} the drag obj
18063          * @param {DragDrop} the target
18064          * @return {boolean} true if the target is a legal target for the
18065          * dd obj
18066          * @static
18067          */
18068         isLegalTarget: function (oDD, oTargetDD) {
18069             var targets = this.getRelated(oDD, true);
18070             for (var i=0, len=targets.length;i<len;++i) {
18071                 if (targets[i].id == oTargetDD.id) {
18072                     return true;
18073                 }
18074             }
18075
18076             return false;
18077         },
18078
18079         /**
18080          * My goal is to be able to transparently determine if an object is
18081          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
18082          * returns "object", oDD.constructor.toString() always returns
18083          * "DragDrop" and not the name of the subclass.  So for now it just
18084          * evaluates a well-known variable in DragDrop.
18085          * @method isTypeOfDD
18086          * @param {Object} the object to evaluate
18087          * @return {boolean} true if typeof oDD = DragDrop
18088          * @static
18089          */
18090         isTypeOfDD: function (oDD) {
18091             return (oDD && oDD.__ygDragDrop);
18092         },
18093
18094         /**
18095          * Utility function to determine if a given element has been
18096          * registered as a drag drop handle for the given Drag Drop object.
18097          * @method isHandle
18098          * @param {String} id the element id to check
18099          * @return {boolean} true if this element is a DragDrop handle, false
18100          * otherwise
18101          * @static
18102          */
18103         isHandle: function(sDDId, sHandleId) {
18104             return ( this.handleIds[sDDId] &&
18105                             this.handleIds[sDDId][sHandleId] );
18106         },
18107
18108         /**
18109          * Returns the DragDrop instance for a given id
18110          * @method getDDById
18111          * @param {String} id the id of the DragDrop object
18112          * @return {DragDrop} the drag drop object, null if it is not found
18113          * @static
18114          */
18115         getDDById: function(id) {
18116             for (var i in this.ids) {
18117                 if (this.ids[i][id]) {
18118                     return this.ids[i][id];
18119                 }
18120             }
18121             return null;
18122         },
18123
18124         /**
18125          * Fired after a registered DragDrop object gets the mousedown event.
18126          * Sets up the events required to track the object being dragged
18127          * @method handleMouseDown
18128          * @param {Event} e the event
18129          * @param oDD the DragDrop object being dragged
18130          * @private
18131          * @static
18132          */
18133         handleMouseDown: function(e, oDD) {
18134             if(Roo.QuickTips){
18135                 Roo.QuickTips.disable();
18136             }
18137             this.currentTarget = e.getTarget();
18138
18139             this.dragCurrent = oDD;
18140
18141             var el = oDD.getEl();
18142
18143             // track start position
18144             this.startX = e.getPageX();
18145             this.startY = e.getPageY();
18146
18147             this.deltaX = this.startX - el.offsetLeft;
18148             this.deltaY = this.startY - el.offsetTop;
18149
18150             this.dragThreshMet = false;
18151
18152             this.clickTimeout = setTimeout(
18153                     function() {
18154                         var DDM = Roo.dd.DDM;
18155                         DDM.startDrag(DDM.startX, DDM.startY);
18156                     },
18157                     this.clickTimeThresh );
18158         },
18159
18160         /**
18161          * Fired when either the drag pixel threshol or the mousedown hold
18162          * time threshold has been met.
18163          * @method startDrag
18164          * @param x {int} the X position of the original mousedown
18165          * @param y {int} the Y position of the original mousedown
18166          * @static
18167          */
18168         startDrag: function(x, y) {
18169             clearTimeout(this.clickTimeout);
18170             if (this.dragCurrent) {
18171                 this.dragCurrent.b4StartDrag(x, y);
18172                 this.dragCurrent.startDrag(x, y);
18173             }
18174             this.dragThreshMet = true;
18175         },
18176
18177         /**
18178          * Internal function to handle the mouseup event.  Will be invoked
18179          * from the context of the document.
18180          * @method handleMouseUp
18181          * @param {Event} e the event
18182          * @private
18183          * @static
18184          */
18185         handleMouseUp: function(e) {
18186
18187             if(Roo.QuickTips){
18188                 Roo.QuickTips.enable();
18189             }
18190             if (! this.dragCurrent) {
18191                 return;
18192             }
18193
18194             clearTimeout(this.clickTimeout);
18195
18196             if (this.dragThreshMet) {
18197                 this.fireEvents(e, true);
18198             } else {
18199             }
18200
18201             this.stopDrag(e);
18202
18203             this.stopEvent(e);
18204         },
18205
18206         /**
18207          * Utility to stop event propagation and event default, if these
18208          * features are turned on.
18209          * @method stopEvent
18210          * @param {Event} e the event as returned by this.getEvent()
18211          * @static
18212          */
18213         stopEvent: function(e){
18214             if(this.stopPropagation) {
18215                 e.stopPropagation();
18216             }
18217
18218             if (this.preventDefault) {
18219                 e.preventDefault();
18220             }
18221         },
18222
18223         /**
18224          * Internal function to clean up event handlers after the drag
18225          * operation is complete
18226          * @method stopDrag
18227          * @param {Event} e the event
18228          * @private
18229          * @static
18230          */
18231         stopDrag: function(e) {
18232             // Fire the drag end event for the item that was dragged
18233             if (this.dragCurrent) {
18234                 if (this.dragThreshMet) {
18235                     this.dragCurrent.b4EndDrag(e);
18236                     this.dragCurrent.endDrag(e);
18237                 }
18238
18239                 this.dragCurrent.onMouseUp(e);
18240             }
18241
18242             this.dragCurrent = null;
18243             this.dragOvers = {};
18244         },
18245
18246         /**
18247          * Internal function to handle the mousemove event.  Will be invoked
18248          * from the context of the html element.
18249          *
18250          * @TODO figure out what we can do about mouse events lost when the
18251          * user drags objects beyond the window boundary.  Currently we can
18252          * detect this in internet explorer by verifying that the mouse is
18253          * down during the mousemove event.  Firefox doesn't give us the
18254          * button state on the mousemove event.
18255          * @method handleMouseMove
18256          * @param {Event} e the event
18257          * @private
18258          * @static
18259          */
18260         handleMouseMove: function(e) {
18261             if (! this.dragCurrent) {
18262                 return true;
18263             }
18264
18265             // var button = e.which || e.button;
18266
18267             // check for IE mouseup outside of page boundary
18268             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18269                 this.stopEvent(e);
18270                 return this.handleMouseUp(e);
18271             }
18272
18273             if (!this.dragThreshMet) {
18274                 var diffX = Math.abs(this.startX - e.getPageX());
18275                 var diffY = Math.abs(this.startY - e.getPageY());
18276                 if (diffX > this.clickPixelThresh ||
18277                             diffY > this.clickPixelThresh) {
18278                     this.startDrag(this.startX, this.startY);
18279                 }
18280             }
18281
18282             if (this.dragThreshMet) {
18283                 this.dragCurrent.b4Drag(e);
18284                 this.dragCurrent.onDrag(e);
18285                 if(!this.dragCurrent.moveOnly){
18286                     this.fireEvents(e, false);
18287                 }
18288             }
18289
18290             this.stopEvent(e);
18291
18292             return true;
18293         },
18294
18295         /**
18296          * Iterates over all of the DragDrop elements to find ones we are
18297          * hovering over or dropping on
18298          * @method fireEvents
18299          * @param {Event} e the event
18300          * @param {boolean} isDrop is this a drop op or a mouseover op?
18301          * @private
18302          * @static
18303          */
18304         fireEvents: function(e, isDrop) {
18305             var dc = this.dragCurrent;
18306
18307             // If the user did the mouse up outside of the window, we could
18308             // get here even though we have ended the drag.
18309             if (!dc || dc.isLocked()) {
18310                 return;
18311             }
18312
18313             var pt = e.getPoint();
18314
18315             // cache the previous dragOver array
18316             var oldOvers = [];
18317
18318             var outEvts   = [];
18319             var overEvts  = [];
18320             var dropEvts  = [];
18321             var enterEvts = [];
18322
18323             // Check to see if the object(s) we were hovering over is no longer
18324             // being hovered over so we can fire the onDragOut event
18325             for (var i in this.dragOvers) {
18326
18327                 var ddo = this.dragOvers[i];
18328
18329                 if (! this.isTypeOfDD(ddo)) {
18330                     continue;
18331                 }
18332
18333                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18334                     outEvts.push( ddo );
18335                 }
18336
18337                 oldOvers[i] = true;
18338                 delete this.dragOvers[i];
18339             }
18340
18341             for (var sGroup in dc.groups) {
18342
18343                 if ("string" != typeof sGroup) {
18344                     continue;
18345                 }
18346
18347                 for (i in this.ids[sGroup]) {
18348                     var oDD = this.ids[sGroup][i];
18349                     if (! this.isTypeOfDD(oDD)) {
18350                         continue;
18351                     }
18352
18353                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18354                         if (this.isOverTarget(pt, oDD, this.mode)) {
18355                             // look for drop interactions
18356                             if (isDrop) {
18357                                 dropEvts.push( oDD );
18358                             // look for drag enter and drag over interactions
18359                             } else {
18360
18361                                 // initial drag over: dragEnter fires
18362                                 if (!oldOvers[oDD.id]) {
18363                                     enterEvts.push( oDD );
18364                                 // subsequent drag overs: dragOver fires
18365                                 } else {
18366                                     overEvts.push( oDD );
18367                                 }
18368
18369                                 this.dragOvers[oDD.id] = oDD;
18370                             }
18371                         }
18372                     }
18373                 }
18374             }
18375
18376             if (this.mode) {
18377                 if (outEvts.length) {
18378                     dc.b4DragOut(e, outEvts);
18379                     dc.onDragOut(e, outEvts);
18380                 }
18381
18382                 if (enterEvts.length) {
18383                     dc.onDragEnter(e, enterEvts);
18384                 }
18385
18386                 if (overEvts.length) {
18387                     dc.b4DragOver(e, overEvts);
18388                     dc.onDragOver(e, overEvts);
18389                 }
18390
18391                 if (dropEvts.length) {
18392                     dc.b4DragDrop(e, dropEvts);
18393                     dc.onDragDrop(e, dropEvts);
18394                 }
18395
18396             } else {
18397                 // fire dragout events
18398                 var len = 0;
18399                 for (i=0, len=outEvts.length; i<len; ++i) {
18400                     dc.b4DragOut(e, outEvts[i].id);
18401                     dc.onDragOut(e, outEvts[i].id);
18402                 }
18403
18404                 // fire enter events
18405                 for (i=0,len=enterEvts.length; i<len; ++i) {
18406                     // dc.b4DragEnter(e, oDD.id);
18407                     dc.onDragEnter(e, enterEvts[i].id);
18408                 }
18409
18410                 // fire over events
18411                 for (i=0,len=overEvts.length; i<len; ++i) {
18412                     dc.b4DragOver(e, overEvts[i].id);
18413                     dc.onDragOver(e, overEvts[i].id);
18414                 }
18415
18416                 // fire drop events
18417                 for (i=0, len=dropEvts.length; i<len; ++i) {
18418                     dc.b4DragDrop(e, dropEvts[i].id);
18419                     dc.onDragDrop(e, dropEvts[i].id);
18420                 }
18421
18422             }
18423
18424             // notify about a drop that did not find a target
18425             if (isDrop && !dropEvts.length) {
18426                 dc.onInvalidDrop(e);
18427             }
18428
18429         },
18430
18431         /**
18432          * Helper function for getting the best match from the list of drag
18433          * and drop objects returned by the drag and drop events when we are
18434          * in INTERSECT mode.  It returns either the first object that the
18435          * cursor is over, or the object that has the greatest overlap with
18436          * the dragged element.
18437          * @method getBestMatch
18438          * @param  {DragDrop[]} dds The array of drag and drop objects
18439          * targeted
18440          * @return {DragDrop}       The best single match
18441          * @static
18442          */
18443         getBestMatch: function(dds) {
18444             var winner = null;
18445             // Return null if the input is not what we expect
18446             //if (!dds || !dds.length || dds.length == 0) {
18447                // winner = null;
18448             // If there is only one item, it wins
18449             //} else if (dds.length == 1) {
18450
18451             var len = dds.length;
18452
18453             if (len == 1) {
18454                 winner = dds[0];
18455             } else {
18456                 // Loop through the targeted items
18457                 for (var i=0; i<len; ++i) {
18458                     var dd = dds[i];
18459                     // If the cursor is over the object, it wins.  If the
18460                     // cursor is over multiple matches, the first one we come
18461                     // to wins.
18462                     if (dd.cursorIsOver) {
18463                         winner = dd;
18464                         break;
18465                     // Otherwise the object with the most overlap wins
18466                     } else {
18467                         if (!winner ||
18468                             winner.overlap.getArea() < dd.overlap.getArea()) {
18469                             winner = dd;
18470                         }
18471                     }
18472                 }
18473             }
18474
18475             return winner;
18476         },
18477
18478         /**
18479          * Refreshes the cache of the top-left and bottom-right points of the
18480          * drag and drop objects in the specified group(s).  This is in the
18481          * format that is stored in the drag and drop instance, so typical
18482          * usage is:
18483          * <code>
18484          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18485          * </code>
18486          * Alternatively:
18487          * <code>
18488          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18489          * </code>
18490          * @TODO this really should be an indexed array.  Alternatively this
18491          * method could accept both.
18492          * @method refreshCache
18493          * @param {Object} groups an associative array of groups to refresh
18494          * @static
18495          */
18496         refreshCache: function(groups) {
18497             for (var sGroup in groups) {
18498                 if ("string" != typeof sGroup) {
18499                     continue;
18500                 }
18501                 for (var i in this.ids[sGroup]) {
18502                     var oDD = this.ids[sGroup][i];
18503
18504                     if (this.isTypeOfDD(oDD)) {
18505                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18506                         var loc = this.getLocation(oDD);
18507                         if (loc) {
18508                             this.locationCache[oDD.id] = loc;
18509                         } else {
18510                             delete this.locationCache[oDD.id];
18511                             // this will unregister the drag and drop object if
18512                             // the element is not in a usable state
18513                             // oDD.unreg();
18514                         }
18515                     }
18516                 }
18517             }
18518         },
18519
18520         /**
18521          * This checks to make sure an element exists and is in the DOM.  The
18522          * main purpose is to handle cases where innerHTML is used to remove
18523          * drag and drop objects from the DOM.  IE provides an 'unspecified
18524          * error' when trying to access the offsetParent of such an element
18525          * @method verifyEl
18526          * @param {HTMLElement} el the element to check
18527          * @return {boolean} true if the element looks usable
18528          * @static
18529          */
18530         verifyEl: function(el) {
18531             if (el) {
18532                 var parent;
18533                 if(Roo.isIE){
18534                     try{
18535                         parent = el.offsetParent;
18536                     }catch(e){}
18537                 }else{
18538                     parent = el.offsetParent;
18539                 }
18540                 if (parent) {
18541                     return true;
18542                 }
18543             }
18544
18545             return false;
18546         },
18547
18548         /**
18549          * Returns a Region object containing the drag and drop element's position
18550          * and size, including the padding configured for it
18551          * @method getLocation
18552          * @param {DragDrop} oDD the drag and drop object to get the
18553          *                       location for
18554          * @return {Roo.lib.Region} a Region object representing the total area
18555          *                             the element occupies, including any padding
18556          *                             the instance is configured for.
18557          * @static
18558          */
18559         getLocation: function(oDD) {
18560             if (! this.isTypeOfDD(oDD)) {
18561                 return null;
18562             }
18563
18564             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18565
18566             try {
18567                 pos= Roo.lib.Dom.getXY(el);
18568             } catch (e) { }
18569
18570             if (!pos) {
18571                 return null;
18572             }
18573
18574             x1 = pos[0];
18575             x2 = x1 + el.offsetWidth;
18576             y1 = pos[1];
18577             y2 = y1 + el.offsetHeight;
18578
18579             t = y1 - oDD.padding[0];
18580             r = x2 + oDD.padding[1];
18581             b = y2 + oDD.padding[2];
18582             l = x1 - oDD.padding[3];
18583
18584             return new Roo.lib.Region( t, r, b, l );
18585         },
18586
18587         /**
18588          * Checks the cursor location to see if it over the target
18589          * @method isOverTarget
18590          * @param {Roo.lib.Point} pt The point to evaluate
18591          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18592          * @return {boolean} true if the mouse is over the target
18593          * @private
18594          * @static
18595          */
18596         isOverTarget: function(pt, oTarget, intersect) {
18597             // use cache if available
18598             var loc = this.locationCache[oTarget.id];
18599             if (!loc || !this.useCache) {
18600                 loc = this.getLocation(oTarget);
18601                 this.locationCache[oTarget.id] = loc;
18602
18603             }
18604
18605             if (!loc) {
18606                 return false;
18607             }
18608
18609             oTarget.cursorIsOver = loc.contains( pt );
18610
18611             // DragDrop is using this as a sanity check for the initial mousedown
18612             // in this case we are done.  In POINT mode, if the drag obj has no
18613             // contraints, we are also done. Otherwise we need to evaluate the
18614             // location of the target as related to the actual location of the
18615             // dragged element.
18616             var dc = this.dragCurrent;
18617             if (!dc || !dc.getTargetCoord ||
18618                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18619                 return oTarget.cursorIsOver;
18620             }
18621
18622             oTarget.overlap = null;
18623
18624             // Get the current location of the drag element, this is the
18625             // location of the mouse event less the delta that represents
18626             // where the original mousedown happened on the element.  We
18627             // need to consider constraints and ticks as well.
18628             var pos = dc.getTargetCoord(pt.x, pt.y);
18629
18630             var el = dc.getDragEl();
18631             var curRegion = new Roo.lib.Region( pos.y,
18632                                                    pos.x + el.offsetWidth,
18633                                                    pos.y + el.offsetHeight,
18634                                                    pos.x );
18635
18636             var overlap = curRegion.intersect(loc);
18637
18638             if (overlap) {
18639                 oTarget.overlap = overlap;
18640                 return (intersect) ? true : oTarget.cursorIsOver;
18641             } else {
18642                 return false;
18643             }
18644         },
18645
18646         /**
18647          * unload event handler
18648          * @method _onUnload
18649          * @private
18650          * @static
18651          */
18652         _onUnload: function(e, me) {
18653             Roo.dd.DragDropMgr.unregAll();
18654         },
18655
18656         /**
18657          * Cleans up the drag and drop events and objects.
18658          * @method unregAll
18659          * @private
18660          * @static
18661          */
18662         unregAll: function() {
18663
18664             if (this.dragCurrent) {
18665                 this.stopDrag();
18666                 this.dragCurrent = null;
18667             }
18668
18669             this._execOnAll("unreg", []);
18670
18671             for (i in this.elementCache) {
18672                 delete this.elementCache[i];
18673             }
18674
18675             this.elementCache = {};
18676             this.ids = {};
18677         },
18678
18679         /**
18680          * A cache of DOM elements
18681          * @property elementCache
18682          * @private
18683          * @static
18684          */
18685         elementCache: {},
18686
18687         /**
18688          * Get the wrapper for the DOM element specified
18689          * @method getElWrapper
18690          * @param {String} id the id of the element to get
18691          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18692          * @private
18693          * @deprecated This wrapper isn't that useful
18694          * @static
18695          */
18696         getElWrapper: function(id) {
18697             var oWrapper = this.elementCache[id];
18698             if (!oWrapper || !oWrapper.el) {
18699                 oWrapper = this.elementCache[id] =
18700                     new this.ElementWrapper(Roo.getDom(id));
18701             }
18702             return oWrapper;
18703         },
18704
18705         /**
18706          * Returns the actual DOM element
18707          * @method getElement
18708          * @param {String} id the id of the elment to get
18709          * @return {Object} The element
18710          * @deprecated use Roo.getDom instead
18711          * @static
18712          */
18713         getElement: function(id) {
18714             return Roo.getDom(id);
18715         },
18716
18717         /**
18718          * Returns the style property for the DOM element (i.e.,
18719          * document.getElById(id).style)
18720          * @method getCss
18721          * @param {String} id the id of the elment to get
18722          * @return {Object} The style property of the element
18723          * @deprecated use Roo.getDom instead
18724          * @static
18725          */
18726         getCss: function(id) {
18727             var el = Roo.getDom(id);
18728             return (el) ? el.style : null;
18729         },
18730
18731         /**
18732          * Inner class for cached elements
18733          * @class DragDropMgr.ElementWrapper
18734          * @for DragDropMgr
18735          * @private
18736          * @deprecated
18737          */
18738         ElementWrapper: function(el) {
18739                 /**
18740                  * The element
18741                  * @property el
18742                  */
18743                 this.el = el || null;
18744                 /**
18745                  * The element id
18746                  * @property id
18747                  */
18748                 this.id = this.el && el.id;
18749                 /**
18750                  * A reference to the style property
18751                  * @property css
18752                  */
18753                 this.css = this.el && el.style;
18754             },
18755
18756         /**
18757          * Returns the X position of an html element
18758          * @method getPosX
18759          * @param el the element for which to get the position
18760          * @return {int} the X coordinate
18761          * @for DragDropMgr
18762          * @deprecated use Roo.lib.Dom.getX instead
18763          * @static
18764          */
18765         getPosX: function(el) {
18766             return Roo.lib.Dom.getX(el);
18767         },
18768
18769         /**
18770          * Returns the Y position of an html element
18771          * @method getPosY
18772          * @param el the element for which to get the position
18773          * @return {int} the Y coordinate
18774          * @deprecated use Roo.lib.Dom.getY instead
18775          * @static
18776          */
18777         getPosY: function(el) {
18778             return Roo.lib.Dom.getY(el);
18779         },
18780
18781         /**
18782          * Swap two nodes.  In IE, we use the native method, for others we
18783          * emulate the IE behavior
18784          * @method swapNode
18785          * @param n1 the first node to swap
18786          * @param n2 the other node to swap
18787          * @static
18788          */
18789         swapNode: function(n1, n2) {
18790             if (n1.swapNode) {
18791                 n1.swapNode(n2);
18792             } else {
18793                 var p = n2.parentNode;
18794                 var s = n2.nextSibling;
18795
18796                 if (s == n1) {
18797                     p.insertBefore(n1, n2);
18798                 } else if (n2 == n1.nextSibling) {
18799                     p.insertBefore(n2, n1);
18800                 } else {
18801                     n1.parentNode.replaceChild(n2, n1);
18802                     p.insertBefore(n1, s);
18803                 }
18804             }
18805         },
18806
18807         /**
18808          * Returns the current scroll position
18809          * @method getScroll
18810          * @private
18811          * @static
18812          */
18813         getScroll: function () {
18814             var t, l, dde=document.documentElement, db=document.body;
18815             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18816                 t = dde.scrollTop;
18817                 l = dde.scrollLeft;
18818             } else if (db) {
18819                 t = db.scrollTop;
18820                 l = db.scrollLeft;
18821             } else {
18822
18823             }
18824             return { top: t, left: l };
18825         },
18826
18827         /**
18828          * Returns the specified element style property
18829          * @method getStyle
18830          * @param {HTMLElement} el          the element
18831          * @param {string}      styleProp   the style property
18832          * @return {string} The value of the style property
18833          * @deprecated use Roo.lib.Dom.getStyle
18834          * @static
18835          */
18836         getStyle: function(el, styleProp) {
18837             return Roo.fly(el).getStyle(styleProp);
18838         },
18839
18840         /**
18841          * Gets the scrollTop
18842          * @method getScrollTop
18843          * @return {int} the document's scrollTop
18844          * @static
18845          */
18846         getScrollTop: function () { return this.getScroll().top; },
18847
18848         /**
18849          * Gets the scrollLeft
18850          * @method getScrollLeft
18851          * @return {int} the document's scrollTop
18852          * @static
18853          */
18854         getScrollLeft: function () { return this.getScroll().left; },
18855
18856         /**
18857          * Sets the x/y position of an element to the location of the
18858          * target element.
18859          * @method moveToEl
18860          * @param {HTMLElement} moveEl      The element to move
18861          * @param {HTMLElement} targetEl    The position reference element
18862          * @static
18863          */
18864         moveToEl: function (moveEl, targetEl) {
18865             var aCoord = Roo.lib.Dom.getXY(targetEl);
18866             Roo.lib.Dom.setXY(moveEl, aCoord);
18867         },
18868
18869         /**
18870          * Numeric array sort function
18871          * @method numericSort
18872          * @static
18873          */
18874         numericSort: function(a, b) { return (a - b); },
18875
18876         /**
18877          * Internal counter
18878          * @property _timeoutCount
18879          * @private
18880          * @static
18881          */
18882         _timeoutCount: 0,
18883
18884         /**
18885          * Trying to make the load order less important.  Without this we get
18886          * an error if this file is loaded before the Event Utility.
18887          * @method _addListeners
18888          * @private
18889          * @static
18890          */
18891         _addListeners: function() {
18892             var DDM = Roo.dd.DDM;
18893             if ( Roo.lib.Event && document ) {
18894                 DDM._onLoad();
18895             } else {
18896                 if (DDM._timeoutCount > 2000) {
18897                 } else {
18898                     setTimeout(DDM._addListeners, 10);
18899                     if (document && document.body) {
18900                         DDM._timeoutCount += 1;
18901                     }
18902                 }
18903             }
18904         },
18905
18906         /**
18907          * Recursively searches the immediate parent and all child nodes for
18908          * the handle element in order to determine wheter or not it was
18909          * clicked.
18910          * @method handleWasClicked
18911          * @param node the html element to inspect
18912          * @static
18913          */
18914         handleWasClicked: function(node, id) {
18915             if (this.isHandle(id, node.id)) {
18916                 return true;
18917             } else {
18918                 // check to see if this is a text node child of the one we want
18919                 var p = node.parentNode;
18920
18921                 while (p) {
18922                     if (this.isHandle(id, p.id)) {
18923                         return true;
18924                     } else {
18925                         p = p.parentNode;
18926                     }
18927                 }
18928             }
18929
18930             return false;
18931         }
18932
18933     };
18934
18935 }();
18936
18937 // shorter alias, save a few bytes
18938 Roo.dd.DDM = Roo.dd.DragDropMgr;
18939 Roo.dd.DDM._addListeners();
18940
18941 }/*
18942  * Based on:
18943  * Ext JS Library 1.1.1
18944  * Copyright(c) 2006-2007, Ext JS, LLC.
18945  *
18946  * Originally Released Under LGPL - original licence link has changed is not relivant.
18947  *
18948  * Fork - LGPL
18949  * <script type="text/javascript">
18950  */
18951
18952 /**
18953  * @class Roo.dd.DD
18954  * A DragDrop implementation where the linked element follows the
18955  * mouse cursor during a drag.
18956  * @extends Roo.dd.DragDrop
18957  * @constructor
18958  * @param {String} id the id of the linked element
18959  * @param {String} sGroup the group of related DragDrop items
18960  * @param {object} config an object containing configurable attributes
18961  *                Valid properties for DD:
18962  *                    scroll
18963  */
18964 Roo.dd.DD = function(id, sGroup, config) {
18965     if (id) {
18966         this.init(id, sGroup, config);
18967     }
18968 };
18969
18970 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18971
18972     /**
18973      * When set to true, the utility automatically tries to scroll the browser
18974      * window wehn a drag and drop element is dragged near the viewport boundary.
18975      * Defaults to true.
18976      * @property scroll
18977      * @type boolean
18978      */
18979     scroll: true,
18980
18981     /**
18982      * Sets the pointer offset to the distance between the linked element's top
18983      * left corner and the location the element was clicked
18984      * @method autoOffset
18985      * @param {int} iPageX the X coordinate of the click
18986      * @param {int} iPageY the Y coordinate of the click
18987      */
18988     autoOffset: function(iPageX, iPageY) {
18989         var x = iPageX - this.startPageX;
18990         var y = iPageY - this.startPageY;
18991         this.setDelta(x, y);
18992     },
18993
18994     /**
18995      * Sets the pointer offset.  You can call this directly to force the
18996      * offset to be in a particular location (e.g., pass in 0,0 to set it
18997      * to the center of the object)
18998      * @method setDelta
18999      * @param {int} iDeltaX the distance from the left
19000      * @param {int} iDeltaY the distance from the top
19001      */
19002     setDelta: function(iDeltaX, iDeltaY) {
19003         this.deltaX = iDeltaX;
19004         this.deltaY = iDeltaY;
19005     },
19006
19007     /**
19008      * Sets the drag element to the location of the mousedown or click event,
19009      * maintaining the cursor location relative to the location on the element
19010      * that was clicked.  Override this if you want to place the element in a
19011      * location other than where the cursor is.
19012      * @method setDragElPos
19013      * @param {int} iPageX the X coordinate of the mousedown or drag event
19014      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19015      */
19016     setDragElPos: function(iPageX, iPageY) {
19017         // the first time we do this, we are going to check to make sure
19018         // the element has css positioning
19019
19020         var el = this.getDragEl();
19021         this.alignElWithMouse(el, iPageX, iPageY);
19022     },
19023
19024     /**
19025      * Sets the element to the location of the mousedown or click event,
19026      * maintaining the cursor location relative to the location on the element
19027      * that was clicked.  Override this if you want to place the element in a
19028      * location other than where the cursor is.
19029      * @method alignElWithMouse
19030      * @param {HTMLElement} el the element to move
19031      * @param {int} iPageX the X coordinate of the mousedown or drag event
19032      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19033      */
19034     alignElWithMouse: function(el, iPageX, iPageY) {
19035         var oCoord = this.getTargetCoord(iPageX, iPageY);
19036         var fly = el.dom ? el : Roo.fly(el);
19037         if (!this.deltaSetXY) {
19038             var aCoord = [oCoord.x, oCoord.y];
19039             fly.setXY(aCoord);
19040             var newLeft = fly.getLeft(true);
19041             var newTop  = fly.getTop(true);
19042             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
19043         } else {
19044             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
19045         }
19046
19047         this.cachePosition(oCoord.x, oCoord.y);
19048         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
19049         return oCoord;
19050     },
19051
19052     /**
19053      * Saves the most recent position so that we can reset the constraints and
19054      * tick marks on-demand.  We need to know this so that we can calculate the
19055      * number of pixels the element is offset from its original position.
19056      * @method cachePosition
19057      * @param iPageX the current x position (optional, this just makes it so we
19058      * don't have to look it up again)
19059      * @param iPageY the current y position (optional, this just makes it so we
19060      * don't have to look it up again)
19061      */
19062     cachePosition: function(iPageX, iPageY) {
19063         if (iPageX) {
19064             this.lastPageX = iPageX;
19065             this.lastPageY = iPageY;
19066         } else {
19067             var aCoord = Roo.lib.Dom.getXY(this.getEl());
19068             this.lastPageX = aCoord[0];
19069             this.lastPageY = aCoord[1];
19070         }
19071     },
19072
19073     /**
19074      * Auto-scroll the window if the dragged object has been moved beyond the
19075      * visible window boundary.
19076      * @method autoScroll
19077      * @param {int} x the drag element's x position
19078      * @param {int} y the drag element's y position
19079      * @param {int} h the height of the drag element
19080      * @param {int} w the width of the drag element
19081      * @private
19082      */
19083     autoScroll: function(x, y, h, w) {
19084
19085         if (this.scroll) {
19086             // The client height
19087             var clientH = Roo.lib.Dom.getViewWidth();
19088
19089             // The client width
19090             var clientW = Roo.lib.Dom.getViewHeight();
19091
19092             // The amt scrolled down
19093             var st = this.DDM.getScrollTop();
19094
19095             // The amt scrolled right
19096             var sl = this.DDM.getScrollLeft();
19097
19098             // Location of the bottom of the element
19099             var bot = h + y;
19100
19101             // Location of the right of the element
19102             var right = w + x;
19103
19104             // The distance from the cursor to the bottom of the visible area,
19105             // adjusted so that we don't scroll if the cursor is beyond the
19106             // element drag constraints
19107             var toBot = (clientH + st - y - this.deltaY);
19108
19109             // The distance from the cursor to the right of the visible area
19110             var toRight = (clientW + sl - x - this.deltaX);
19111
19112
19113             // How close to the edge the cursor must be before we scroll
19114             // var thresh = (document.all) ? 100 : 40;
19115             var thresh = 40;
19116
19117             // How many pixels to scroll per autoscroll op.  This helps to reduce
19118             // clunky scrolling. IE is more sensitive about this ... it needs this
19119             // value to be higher.
19120             var scrAmt = (document.all) ? 80 : 30;
19121
19122             // Scroll down if we are near the bottom of the visible page and the
19123             // obj extends below the crease
19124             if ( bot > clientH && toBot < thresh ) {
19125                 window.scrollTo(sl, st + scrAmt);
19126             }
19127
19128             // Scroll up if the window is scrolled down and the top of the object
19129             // goes above the top border
19130             if ( y < st && st > 0 && y - st < thresh ) {
19131                 window.scrollTo(sl, st - scrAmt);
19132             }
19133
19134             // Scroll right if the obj is beyond the right border and the cursor is
19135             // near the border.
19136             if ( right > clientW && toRight < thresh ) {
19137                 window.scrollTo(sl + scrAmt, st);
19138             }
19139
19140             // Scroll left if the window has been scrolled to the right and the obj
19141             // extends past the left border
19142             if ( x < sl && sl > 0 && x - sl < thresh ) {
19143                 window.scrollTo(sl - scrAmt, st);
19144             }
19145         }
19146     },
19147
19148     /**
19149      * Finds the location the element should be placed if we want to move
19150      * it to where the mouse location less the click offset would place us.
19151      * @method getTargetCoord
19152      * @param {int} iPageX the X coordinate of the click
19153      * @param {int} iPageY the Y coordinate of the click
19154      * @return an object that contains the coordinates (Object.x and Object.y)
19155      * @private
19156      */
19157     getTargetCoord: function(iPageX, iPageY) {
19158
19159
19160         var x = iPageX - this.deltaX;
19161         var y = iPageY - this.deltaY;
19162
19163         if (this.constrainX) {
19164             if (x < this.minX) { x = this.minX; }
19165             if (x > this.maxX) { x = this.maxX; }
19166         }
19167
19168         if (this.constrainY) {
19169             if (y < this.minY) { y = this.minY; }
19170             if (y > this.maxY) { y = this.maxY; }
19171         }
19172
19173         x = this.getTick(x, this.xTicks);
19174         y = this.getTick(y, this.yTicks);
19175
19176
19177         return {x:x, y:y};
19178     },
19179
19180     /*
19181      * Sets up config options specific to this class. Overrides
19182      * Roo.dd.DragDrop, but all versions of this method through the
19183      * inheritance chain are called
19184      */
19185     applyConfig: function() {
19186         Roo.dd.DD.superclass.applyConfig.call(this);
19187         this.scroll = (this.config.scroll !== false);
19188     },
19189
19190     /*
19191      * Event that fires prior to the onMouseDown event.  Overrides
19192      * Roo.dd.DragDrop.
19193      */
19194     b4MouseDown: function(e) {
19195         // this.resetConstraints();
19196         this.autoOffset(e.getPageX(),
19197                             e.getPageY());
19198     },
19199
19200     /*
19201      * Event that fires prior to the onDrag event.  Overrides
19202      * Roo.dd.DragDrop.
19203      */
19204     b4Drag: function(e) {
19205         this.setDragElPos(e.getPageX(),
19206                             e.getPageY());
19207     },
19208
19209     toString: function() {
19210         return ("DD " + this.id);
19211     }
19212
19213     //////////////////////////////////////////////////////////////////////////
19214     // Debugging ygDragDrop events that can be overridden
19215     //////////////////////////////////////////////////////////////////////////
19216     /*
19217     startDrag: function(x, y) {
19218     },
19219
19220     onDrag: function(e) {
19221     },
19222
19223     onDragEnter: function(e, id) {
19224     },
19225
19226     onDragOver: function(e, id) {
19227     },
19228
19229     onDragOut: function(e, id) {
19230     },
19231
19232     onDragDrop: function(e, id) {
19233     },
19234
19235     endDrag: function(e) {
19236     }
19237
19238     */
19239
19240 });/*
19241  * Based on:
19242  * Ext JS Library 1.1.1
19243  * Copyright(c) 2006-2007, Ext JS, LLC.
19244  *
19245  * Originally Released Under LGPL - original licence link has changed is not relivant.
19246  *
19247  * Fork - LGPL
19248  * <script type="text/javascript">
19249  */
19250
19251 /**
19252  * @class Roo.dd.DDProxy
19253  * A DragDrop implementation that inserts an empty, bordered div into
19254  * the document that follows the cursor during drag operations.  At the time of
19255  * the click, the frame div is resized to the dimensions of the linked html
19256  * element, and moved to the exact location of the linked element.
19257  *
19258  * References to the "frame" element refer to the single proxy element that
19259  * was created to be dragged in place of all DDProxy elements on the
19260  * page.
19261  *
19262  * @extends Roo.dd.DD
19263  * @constructor
19264  * @param {String} id the id of the linked html element
19265  * @param {String} sGroup the group of related DragDrop objects
19266  * @param {object} config an object containing configurable attributes
19267  *                Valid properties for DDProxy in addition to those in DragDrop:
19268  *                   resizeFrame, centerFrame, dragElId
19269  */
19270 Roo.dd.DDProxy = function(id, sGroup, config) {
19271     if (id) {
19272         this.init(id, sGroup, config);
19273         this.initFrame();
19274     }
19275 };
19276
19277 /**
19278  * The default drag frame div id
19279  * @property Roo.dd.DDProxy.dragElId
19280  * @type String
19281  * @static
19282  */
19283 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19284
19285 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19286
19287     /**
19288      * By default we resize the drag frame to be the same size as the element
19289      * we want to drag (this is to get the frame effect).  We can turn it off
19290      * if we want a different behavior.
19291      * @property resizeFrame
19292      * @type boolean
19293      */
19294     resizeFrame: true,
19295
19296     /**
19297      * By default the frame is positioned exactly where the drag element is, so
19298      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19299      * you do not have constraints on the obj is to have the drag frame centered
19300      * around the cursor.  Set centerFrame to true for this effect.
19301      * @property centerFrame
19302      * @type boolean
19303      */
19304     centerFrame: false,
19305
19306     /**
19307      * Creates the proxy element if it does not yet exist
19308      * @method createFrame
19309      */
19310     createFrame: function() {
19311         var self = this;
19312         var body = document.body;
19313
19314         if (!body || !body.firstChild) {
19315             setTimeout( function() { self.createFrame(); }, 50 );
19316             return;
19317         }
19318
19319         var div = this.getDragEl();
19320
19321         if (!div) {
19322             div    = document.createElement("div");
19323             div.id = this.dragElId;
19324             var s  = div.style;
19325
19326             s.position   = "absolute";
19327             s.visibility = "hidden";
19328             s.cursor     = "move";
19329             s.border     = "2px solid #aaa";
19330             s.zIndex     = 999;
19331
19332             // appendChild can blow up IE if invoked prior to the window load event
19333             // while rendering a table.  It is possible there are other scenarios
19334             // that would cause this to happen as well.
19335             body.insertBefore(div, body.firstChild);
19336         }
19337     },
19338
19339     /**
19340      * Initialization for the drag frame element.  Must be called in the
19341      * constructor of all subclasses
19342      * @method initFrame
19343      */
19344     initFrame: function() {
19345         this.createFrame();
19346     },
19347
19348     applyConfig: function() {
19349         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19350
19351         this.resizeFrame = (this.config.resizeFrame !== false);
19352         this.centerFrame = (this.config.centerFrame);
19353         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19354     },
19355
19356     /**
19357      * Resizes the drag frame to the dimensions of the clicked object, positions
19358      * it over the object, and finally displays it
19359      * @method showFrame
19360      * @param {int} iPageX X click position
19361      * @param {int} iPageY Y click position
19362      * @private
19363      */
19364     showFrame: function(iPageX, iPageY) {
19365         var el = this.getEl();
19366         var dragEl = this.getDragEl();
19367         var s = dragEl.style;
19368
19369         this._resizeProxy();
19370
19371         if (this.centerFrame) {
19372             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19373                            Math.round(parseInt(s.height, 10)/2) );
19374         }
19375
19376         this.setDragElPos(iPageX, iPageY);
19377
19378         Roo.fly(dragEl).show();
19379     },
19380
19381     /**
19382      * The proxy is automatically resized to the dimensions of the linked
19383      * element when a drag is initiated, unless resizeFrame is set to false
19384      * @method _resizeProxy
19385      * @private
19386      */
19387     _resizeProxy: function() {
19388         if (this.resizeFrame) {
19389             var el = this.getEl();
19390             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19391         }
19392     },
19393
19394     // overrides Roo.dd.DragDrop
19395     b4MouseDown: function(e) {
19396         var x = e.getPageX();
19397         var y = e.getPageY();
19398         this.autoOffset(x, y);
19399         this.setDragElPos(x, y);
19400     },
19401
19402     // overrides Roo.dd.DragDrop
19403     b4StartDrag: function(x, y) {
19404         // show the drag frame
19405         this.showFrame(x, y);
19406     },
19407
19408     // overrides Roo.dd.DragDrop
19409     b4EndDrag: function(e) {
19410         Roo.fly(this.getDragEl()).hide();
19411     },
19412
19413     // overrides Roo.dd.DragDrop
19414     // By default we try to move the element to the last location of the frame.
19415     // This is so that the default behavior mirrors that of Roo.dd.DD.
19416     endDrag: function(e) {
19417
19418         var lel = this.getEl();
19419         var del = this.getDragEl();
19420
19421         // Show the drag frame briefly so we can get its position
19422         del.style.visibility = "";
19423
19424         this.beforeMove();
19425         // Hide the linked element before the move to get around a Safari
19426         // rendering bug.
19427         lel.style.visibility = "hidden";
19428         Roo.dd.DDM.moveToEl(lel, del);
19429         del.style.visibility = "hidden";
19430         lel.style.visibility = "";
19431
19432         this.afterDrag();
19433     },
19434
19435     beforeMove : function(){
19436
19437     },
19438
19439     afterDrag : function(){
19440
19441     },
19442
19443     toString: function() {
19444         return ("DDProxy " + this.id);
19445     }
19446
19447 });
19448 /*
19449  * Based on:
19450  * Ext JS Library 1.1.1
19451  * Copyright(c) 2006-2007, Ext JS, LLC.
19452  *
19453  * Originally Released Under LGPL - original licence link has changed is not relivant.
19454  *
19455  * Fork - LGPL
19456  * <script type="text/javascript">
19457  */
19458
19459  /**
19460  * @class Roo.dd.DDTarget
19461  * A DragDrop implementation that does not move, but can be a drop
19462  * target.  You would get the same result by simply omitting implementation
19463  * for the event callbacks, but this way we reduce the processing cost of the
19464  * event listener and the callbacks.
19465  * @extends Roo.dd.DragDrop
19466  * @constructor
19467  * @param {String} id the id of the element that is a drop target
19468  * @param {String} sGroup the group of related DragDrop objects
19469  * @param {object} config an object containing configurable attributes
19470  *                 Valid properties for DDTarget in addition to those in
19471  *                 DragDrop:
19472  *                    none
19473  */
19474 Roo.dd.DDTarget = function(id, sGroup, config) {
19475     if (id) {
19476         this.initTarget(id, sGroup, config);
19477     }
19478     if (config.listeners || config.events) { 
19479        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19480             listeners : config.listeners || {}, 
19481             events : config.events || {} 
19482         });    
19483     }
19484 };
19485
19486 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19487 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19488     toString: function() {
19489         return ("DDTarget " + this.id);
19490     }
19491 });
19492 /*
19493  * Based on:
19494  * Ext JS Library 1.1.1
19495  * Copyright(c) 2006-2007, Ext JS, LLC.
19496  *
19497  * Originally Released Under LGPL - original licence link has changed is not relivant.
19498  *
19499  * Fork - LGPL
19500  * <script type="text/javascript">
19501  */
19502  
19503
19504 /**
19505  * @class Roo.dd.ScrollManager
19506  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19507  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19508  * @singleton
19509  */
19510 Roo.dd.ScrollManager = function(){
19511     var ddm = Roo.dd.DragDropMgr;
19512     var els = {};
19513     var dragEl = null;
19514     var proc = {};
19515     
19516     
19517     
19518     var onStop = function(e){
19519         dragEl = null;
19520         clearProc();
19521     };
19522     
19523     var triggerRefresh = function(){
19524         if(ddm.dragCurrent){
19525              ddm.refreshCache(ddm.dragCurrent.groups);
19526         }
19527     };
19528     
19529     var doScroll = function(){
19530         if(ddm.dragCurrent){
19531             var dds = Roo.dd.ScrollManager;
19532             if(!dds.animate){
19533                 if(proc.el.scroll(proc.dir, dds.increment)){
19534                     triggerRefresh();
19535                 }
19536             }else{
19537                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19538             }
19539         }
19540     };
19541     
19542     var clearProc = function(){
19543         if(proc.id){
19544             clearInterval(proc.id);
19545         }
19546         proc.id = 0;
19547         proc.el = null;
19548         proc.dir = "";
19549     };
19550     
19551     var startProc = function(el, dir){
19552          Roo.log('scroll startproc');
19553         clearProc();
19554         proc.el = el;
19555         proc.dir = dir;
19556         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19557     };
19558     
19559     var onFire = function(e, isDrop){
19560        
19561         if(isDrop || !ddm.dragCurrent){ return; }
19562         var dds = Roo.dd.ScrollManager;
19563         if(!dragEl || dragEl != ddm.dragCurrent){
19564             dragEl = ddm.dragCurrent;
19565             // refresh regions on drag start
19566             dds.refreshCache();
19567         }
19568         
19569         var xy = Roo.lib.Event.getXY(e);
19570         var pt = new Roo.lib.Point(xy[0], xy[1]);
19571         for(var id in els){
19572             var el = els[id], r = el._region;
19573             if(r && r.contains(pt) && el.isScrollable()){
19574                 if(r.bottom - pt.y <= dds.thresh){
19575                     if(proc.el != el){
19576                         startProc(el, "down");
19577                     }
19578                     return;
19579                 }else if(r.right - pt.x <= dds.thresh){
19580                     if(proc.el != el){
19581                         startProc(el, "left");
19582                     }
19583                     return;
19584                 }else if(pt.y - r.top <= dds.thresh){
19585                     if(proc.el != el){
19586                         startProc(el, "up");
19587                     }
19588                     return;
19589                 }else if(pt.x - r.left <= dds.thresh){
19590                     if(proc.el != el){
19591                         startProc(el, "right");
19592                     }
19593                     return;
19594                 }
19595             }
19596         }
19597         clearProc();
19598     };
19599     
19600     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19601     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19602     
19603     return {
19604         /**
19605          * Registers new overflow element(s) to auto scroll
19606          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19607          */
19608         register : function(el){
19609             if(el instanceof Array){
19610                 for(var i = 0, len = el.length; i < len; i++) {
19611                         this.register(el[i]);
19612                 }
19613             }else{
19614                 el = Roo.get(el);
19615                 els[el.id] = el;
19616             }
19617             Roo.dd.ScrollManager.els = els;
19618         },
19619         
19620         /**
19621          * Unregisters overflow element(s) so they are no longer scrolled
19622          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19623          */
19624         unregister : function(el){
19625             if(el instanceof Array){
19626                 for(var i = 0, len = el.length; i < len; i++) {
19627                         this.unregister(el[i]);
19628                 }
19629             }else{
19630                 el = Roo.get(el);
19631                 delete els[el.id];
19632             }
19633         },
19634         
19635         /**
19636          * The number of pixels from the edge of a container the pointer needs to be to 
19637          * trigger scrolling (defaults to 25)
19638          * @type Number
19639          */
19640         thresh : 25,
19641         
19642         /**
19643          * The number of pixels to scroll in each scroll increment (defaults to 50)
19644          * @type Number
19645          */
19646         increment : 100,
19647         
19648         /**
19649          * The frequency of scrolls in milliseconds (defaults to 500)
19650          * @type Number
19651          */
19652         frequency : 500,
19653         
19654         /**
19655          * True to animate the scroll (defaults to true)
19656          * @type Boolean
19657          */
19658         animate: true,
19659         
19660         /**
19661          * The animation duration in seconds - 
19662          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19663          * @type Number
19664          */
19665         animDuration: .4,
19666         
19667         /**
19668          * Manually trigger a cache refresh.
19669          */
19670         refreshCache : function(){
19671             for(var id in els){
19672                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19673                     els[id]._region = els[id].getRegion();
19674                 }
19675             }
19676         }
19677     };
19678 }();/*
19679  * Based on:
19680  * Ext JS Library 1.1.1
19681  * Copyright(c) 2006-2007, Ext JS, LLC.
19682  *
19683  * Originally Released Under LGPL - original licence link has changed is not relivant.
19684  *
19685  * Fork - LGPL
19686  * <script type="text/javascript">
19687  */
19688  
19689
19690 /**
19691  * @class Roo.dd.Registry
19692  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19693  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19694  * @singleton
19695  */
19696 Roo.dd.Registry = function(){
19697     var elements = {}; 
19698     var handles = {}; 
19699     var autoIdSeed = 0;
19700
19701     var getId = function(el, autogen){
19702         if(typeof el == "string"){
19703             return el;
19704         }
19705         var id = el.id;
19706         if(!id && autogen !== false){
19707             id = "roodd-" + (++autoIdSeed);
19708             el.id = id;
19709         }
19710         return id;
19711     };
19712     
19713     return {
19714     /**
19715      * Register a drag drop element
19716      * @param {String|HTMLElement} element The id or DOM node to register
19717      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19718      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19719      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19720      * populated in the data object (if applicable):
19721      * <pre>
19722 Value      Description<br />
19723 ---------  ------------------------------------------<br />
19724 handles    Array of DOM nodes that trigger dragging<br />
19725            for the element being registered<br />
19726 isHandle   True if the element passed in triggers<br />
19727            dragging itself, else false
19728 </pre>
19729      */
19730         register : function(el, data){
19731             data = data || {};
19732             if(typeof el == "string"){
19733                 el = document.getElementById(el);
19734             }
19735             data.ddel = el;
19736             elements[getId(el)] = data;
19737             if(data.isHandle !== false){
19738                 handles[data.ddel.id] = data;
19739             }
19740             if(data.handles){
19741                 var hs = data.handles;
19742                 for(var i = 0, len = hs.length; i < len; i++){
19743                         handles[getId(hs[i])] = data;
19744                 }
19745             }
19746         },
19747
19748     /**
19749      * Unregister a drag drop element
19750      * @param {String|HTMLElement}  element The id or DOM node to unregister
19751      */
19752         unregister : function(el){
19753             var id = getId(el, false);
19754             var data = elements[id];
19755             if(data){
19756                 delete elements[id];
19757                 if(data.handles){
19758                     var hs = data.handles;
19759                     for(var i = 0, len = hs.length; i < len; i++){
19760                         delete handles[getId(hs[i], false)];
19761                     }
19762                 }
19763             }
19764         },
19765
19766     /**
19767      * Returns the handle registered for a DOM Node by id
19768      * @param {String|HTMLElement} id The DOM node or id to look up
19769      * @return {Object} handle The custom handle data
19770      */
19771         getHandle : function(id){
19772             if(typeof id != "string"){ // must be element?
19773                 id = id.id;
19774             }
19775             return handles[id];
19776         },
19777
19778     /**
19779      * Returns the handle that is registered for the DOM node that is the target of the event
19780      * @param {Event} e The event
19781      * @return {Object} handle The custom handle data
19782      */
19783         getHandleFromEvent : function(e){
19784             var t = Roo.lib.Event.getTarget(e);
19785             return t ? handles[t.id] : null;
19786         },
19787
19788     /**
19789      * Returns a custom data object that is registered for a DOM node by id
19790      * @param {String|HTMLElement} id The DOM node or id to look up
19791      * @return {Object} data The custom data
19792      */
19793         getTarget : function(id){
19794             if(typeof id != "string"){ // must be element?
19795                 id = id.id;
19796             }
19797             return elements[id];
19798         },
19799
19800     /**
19801      * Returns a custom data object that is registered for the DOM node that is the target of the event
19802      * @param {Event} e The event
19803      * @return {Object} data The custom data
19804      */
19805         getTargetFromEvent : function(e){
19806             var t = Roo.lib.Event.getTarget(e);
19807             return t ? elements[t.id] || handles[t.id] : null;
19808         }
19809     };
19810 }();/*
19811  * Based on:
19812  * Ext JS Library 1.1.1
19813  * Copyright(c) 2006-2007, Ext JS, LLC.
19814  *
19815  * Originally Released Under LGPL - original licence link has changed is not relivant.
19816  *
19817  * Fork - LGPL
19818  * <script type="text/javascript">
19819  */
19820  
19821
19822 /**
19823  * @class Roo.dd.StatusProxy
19824  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19825  * default drag proxy used by all Roo.dd components.
19826  * @constructor
19827  * @param {Object} config
19828  */
19829 Roo.dd.StatusProxy = function(config){
19830     Roo.apply(this, config);
19831     this.id = this.id || Roo.id();
19832     this.el = new Roo.Layer({
19833         dh: {
19834             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19835                 {tag: "div", cls: "x-dd-drop-icon"},
19836                 {tag: "div", cls: "x-dd-drag-ghost"}
19837             ]
19838         }, 
19839         shadow: !config || config.shadow !== false
19840     });
19841     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19842     this.dropStatus = this.dropNotAllowed;
19843 };
19844
19845 Roo.dd.StatusProxy.prototype = {
19846     /**
19847      * @cfg {String} dropAllowed
19848      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19849      */
19850     dropAllowed : "x-dd-drop-ok",
19851     /**
19852      * @cfg {String} dropNotAllowed
19853      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19854      */
19855     dropNotAllowed : "x-dd-drop-nodrop",
19856
19857     /**
19858      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19859      * over the current target element.
19860      * @param {String} cssClass The css class for the new drop status indicator image
19861      */
19862     setStatus : function(cssClass){
19863         cssClass = cssClass || this.dropNotAllowed;
19864         if(this.dropStatus != cssClass){
19865             this.el.replaceClass(this.dropStatus, cssClass);
19866             this.dropStatus = cssClass;
19867         }
19868     },
19869
19870     /**
19871      * Resets the status indicator to the default dropNotAllowed value
19872      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19873      */
19874     reset : function(clearGhost){
19875         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19876         this.dropStatus = this.dropNotAllowed;
19877         if(clearGhost){
19878             this.ghost.update("");
19879         }
19880     },
19881
19882     /**
19883      * Updates the contents of the ghost element
19884      * @param {String} html The html that will replace the current innerHTML of the ghost element
19885      */
19886     update : function(html){
19887         if(typeof html == "string"){
19888             this.ghost.update(html);
19889         }else{
19890             this.ghost.update("");
19891             html.style.margin = "0";
19892             this.ghost.dom.appendChild(html);
19893         }
19894         // ensure float = none set?? cant remember why though.
19895         var el = this.ghost.dom.firstChild;
19896                 if(el){
19897                         Roo.fly(el).setStyle('float', 'none');
19898                 }
19899     },
19900     
19901     /**
19902      * Returns the underlying proxy {@link Roo.Layer}
19903      * @return {Roo.Layer} el
19904     */
19905     getEl : function(){
19906         return this.el;
19907     },
19908
19909     /**
19910      * Returns the ghost element
19911      * @return {Roo.Element} el
19912      */
19913     getGhost : function(){
19914         return this.ghost;
19915     },
19916
19917     /**
19918      * Hides the proxy
19919      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19920      */
19921     hide : function(clear){
19922         this.el.hide();
19923         if(clear){
19924             this.reset(true);
19925         }
19926     },
19927
19928     /**
19929      * Stops the repair animation if it's currently running
19930      */
19931     stop : function(){
19932         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19933             this.anim.stop();
19934         }
19935     },
19936
19937     /**
19938      * Displays this proxy
19939      */
19940     show : function(){
19941         this.el.show();
19942     },
19943
19944     /**
19945      * Force the Layer to sync its shadow and shim positions to the element
19946      */
19947     sync : function(){
19948         this.el.sync();
19949     },
19950
19951     /**
19952      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19953      * invalid drop operation by the item being dragged.
19954      * @param {Array} xy The XY position of the element ([x, y])
19955      * @param {Function} callback The function to call after the repair is complete
19956      * @param {Object} scope The scope in which to execute the callback
19957      */
19958     repair : function(xy, callback, scope){
19959         this.callback = callback;
19960         this.scope = scope;
19961         if(xy && this.animRepair !== false){
19962             this.el.addClass("x-dd-drag-repair");
19963             this.el.hideUnders(true);
19964             this.anim = this.el.shift({
19965                 duration: this.repairDuration || .5,
19966                 easing: 'easeOut',
19967                 xy: xy,
19968                 stopFx: true,
19969                 callback: this.afterRepair,
19970                 scope: this
19971             });
19972         }else{
19973             this.afterRepair();
19974         }
19975     },
19976
19977     // private
19978     afterRepair : function(){
19979         this.hide(true);
19980         if(typeof this.callback == "function"){
19981             this.callback.call(this.scope || this);
19982         }
19983         this.callback = null;
19984         this.scope = null;
19985     }
19986 };/*
19987  * Based on:
19988  * Ext JS Library 1.1.1
19989  * Copyright(c) 2006-2007, Ext JS, LLC.
19990  *
19991  * Originally Released Under LGPL - original licence link has changed is not relivant.
19992  *
19993  * Fork - LGPL
19994  * <script type="text/javascript">
19995  */
19996
19997 /**
19998  * @class Roo.dd.DragSource
19999  * @extends Roo.dd.DDProxy
20000  * A simple class that provides the basic implementation needed to make any element draggable.
20001  * @constructor
20002  * @param {String/HTMLElement/Element} el The container element
20003  * @param {Object} config
20004  */
20005 Roo.dd.DragSource = function(el, config){
20006     this.el = Roo.get(el);
20007     this.dragData = {};
20008     
20009     Roo.apply(this, config);
20010     
20011     if(!this.proxy){
20012         this.proxy = new Roo.dd.StatusProxy();
20013     }
20014
20015     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
20016           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
20017     
20018     this.dragging = false;
20019 };
20020
20021 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
20022     /**
20023      * @cfg {String} dropAllowed
20024      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20025      */
20026     dropAllowed : "x-dd-drop-ok",
20027     /**
20028      * @cfg {String} dropNotAllowed
20029      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20030      */
20031     dropNotAllowed : "x-dd-drop-nodrop",
20032
20033     /**
20034      * Returns the data object associated with this drag source
20035      * @return {Object} data An object containing arbitrary data
20036      */
20037     getDragData : function(e){
20038         return this.dragData;
20039     },
20040
20041     // private
20042     onDragEnter : function(e, id){
20043         var target = Roo.dd.DragDropMgr.getDDById(id);
20044         this.cachedTarget = target;
20045         if(this.beforeDragEnter(target, e, id) !== false){
20046             if(target.isNotifyTarget){
20047                 var status = target.notifyEnter(this, e, this.dragData);
20048                 this.proxy.setStatus(status);
20049             }else{
20050                 this.proxy.setStatus(this.dropAllowed);
20051             }
20052             
20053             if(this.afterDragEnter){
20054                 /**
20055                  * An empty function by default, but provided so that you can perform a custom action
20056                  * when the dragged item enters the drop target by providing an implementation.
20057                  * @param {Roo.dd.DragDrop} target The drop target
20058                  * @param {Event} e The event object
20059                  * @param {String} id The id of the dragged element
20060                  * @method afterDragEnter
20061                  */
20062                 this.afterDragEnter(target, e, id);
20063             }
20064         }
20065     },
20066
20067     /**
20068      * An empty function by default, but provided so that you can perform a custom action
20069      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
20070      * @param {Roo.dd.DragDrop} target The drop target
20071      * @param {Event} e The event object
20072      * @param {String} id The id of the dragged element
20073      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20074      */
20075     beforeDragEnter : function(target, e, id){
20076         return true;
20077     },
20078
20079     // private
20080     alignElWithMouse: function() {
20081         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
20082         this.proxy.sync();
20083     },
20084
20085     // private
20086     onDragOver : function(e, id){
20087         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20088         if(this.beforeDragOver(target, e, id) !== false){
20089             if(target.isNotifyTarget){
20090                 var status = target.notifyOver(this, e, this.dragData);
20091                 this.proxy.setStatus(status);
20092             }
20093
20094             if(this.afterDragOver){
20095                 /**
20096                  * An empty function by default, but provided so that you can perform a custom action
20097                  * while the dragged item is over the drop target by providing an implementation.
20098                  * @param {Roo.dd.DragDrop} target The drop target
20099                  * @param {Event} e The event object
20100                  * @param {String} id The id of the dragged element
20101                  * @method afterDragOver
20102                  */
20103                 this.afterDragOver(target, e, id);
20104             }
20105         }
20106     },
20107
20108     /**
20109      * An empty function by default, but provided so that you can perform a custom action
20110      * while the dragged item is over the drop target and optionally cancel the onDragOver.
20111      * @param {Roo.dd.DragDrop} target The drop target
20112      * @param {Event} e The event object
20113      * @param {String} id The id of the dragged element
20114      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20115      */
20116     beforeDragOver : function(target, e, id){
20117         return true;
20118     },
20119
20120     // private
20121     onDragOut : function(e, id){
20122         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20123         if(this.beforeDragOut(target, e, id) !== false){
20124             if(target.isNotifyTarget){
20125                 target.notifyOut(this, e, this.dragData);
20126             }
20127             this.proxy.reset();
20128             if(this.afterDragOut){
20129                 /**
20130                  * An empty function by default, but provided so that you can perform a custom action
20131                  * after the dragged item is dragged out of the target without dropping.
20132                  * @param {Roo.dd.DragDrop} target The drop target
20133                  * @param {Event} e The event object
20134                  * @param {String} id The id of the dragged element
20135                  * @method afterDragOut
20136                  */
20137                 this.afterDragOut(target, e, id);
20138             }
20139         }
20140         this.cachedTarget = null;
20141     },
20142
20143     /**
20144      * An empty function by default, but provided so that you can perform a custom action before the dragged
20145      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20146      * @param {Roo.dd.DragDrop} target The drop target
20147      * @param {Event} e The event object
20148      * @param {String} id The id of the dragged element
20149      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20150      */
20151     beforeDragOut : function(target, e, id){
20152         return true;
20153     },
20154     
20155     // private
20156     onDragDrop : function(e, id){
20157         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20158         if(this.beforeDragDrop(target, e, id) !== false){
20159             if(target.isNotifyTarget){
20160                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20161                     this.onValidDrop(target, e, id);
20162                 }else{
20163                     this.onInvalidDrop(target, e, id);
20164                 }
20165             }else{
20166                 this.onValidDrop(target, e, id);
20167             }
20168             
20169             if(this.afterDragDrop){
20170                 /**
20171                  * An empty function by default, but provided so that you can perform a custom action
20172                  * after a valid drag drop has occurred by providing an implementation.
20173                  * @param {Roo.dd.DragDrop} target The drop target
20174                  * @param {Event} e The event object
20175                  * @param {String} id The id of the dropped element
20176                  * @method afterDragDrop
20177                  */
20178                 this.afterDragDrop(target, e, id);
20179             }
20180         }
20181         delete this.cachedTarget;
20182     },
20183
20184     /**
20185      * An empty function by default, but provided so that you can perform a custom action before the dragged
20186      * item is dropped onto the target and optionally cancel the onDragDrop.
20187      * @param {Roo.dd.DragDrop} target The drop target
20188      * @param {Event} e The event object
20189      * @param {String} id The id of the dragged element
20190      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20191      */
20192     beforeDragDrop : function(target, e, id){
20193         return true;
20194     },
20195
20196     // private
20197     onValidDrop : function(target, e, id){
20198         this.hideProxy();
20199         if(this.afterValidDrop){
20200             /**
20201              * An empty function by default, but provided so that you can perform a custom action
20202              * after a valid drop has occurred by providing an implementation.
20203              * @param {Object} target The target DD 
20204              * @param {Event} e The event object
20205              * @param {String} id The id of the dropped element
20206              * @method afterInvalidDrop
20207              */
20208             this.afterValidDrop(target, e, id);
20209         }
20210     },
20211
20212     // private
20213     getRepairXY : function(e, data){
20214         return this.el.getXY();  
20215     },
20216
20217     // private
20218     onInvalidDrop : function(target, e, id){
20219         this.beforeInvalidDrop(target, e, id);
20220         if(this.cachedTarget){
20221             if(this.cachedTarget.isNotifyTarget){
20222                 this.cachedTarget.notifyOut(this, e, this.dragData);
20223             }
20224             this.cacheTarget = null;
20225         }
20226         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20227
20228         if(this.afterInvalidDrop){
20229             /**
20230              * An empty function by default, but provided so that you can perform a custom action
20231              * after an invalid drop has occurred by providing an implementation.
20232              * @param {Event} e The event object
20233              * @param {String} id The id of the dropped element
20234              * @method afterInvalidDrop
20235              */
20236             this.afterInvalidDrop(e, id);
20237         }
20238     },
20239
20240     // private
20241     afterRepair : function(){
20242         if(Roo.enableFx){
20243             this.el.highlight(this.hlColor || "c3daf9");
20244         }
20245         this.dragging = false;
20246     },
20247
20248     /**
20249      * An empty function by default, but provided so that you can perform a custom action after an invalid
20250      * drop has occurred.
20251      * @param {Roo.dd.DragDrop} target The drop target
20252      * @param {Event} e The event object
20253      * @param {String} id The id of the dragged element
20254      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20255      */
20256     beforeInvalidDrop : function(target, e, id){
20257         return true;
20258     },
20259
20260     // private
20261     handleMouseDown : function(e){
20262         if(this.dragging) {
20263             return;
20264         }
20265         var data = this.getDragData(e);
20266         if(data && this.onBeforeDrag(data, e) !== false){
20267             this.dragData = data;
20268             this.proxy.stop();
20269             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20270         } 
20271     },
20272
20273     /**
20274      * An empty function by default, but provided so that you can perform a custom action before the initial
20275      * drag event begins and optionally cancel it.
20276      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20277      * @param {Event} e The event object
20278      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20279      */
20280     onBeforeDrag : function(data, e){
20281         return true;
20282     },
20283
20284     /**
20285      * An empty function by default, but provided so that you can perform a custom action once the initial
20286      * drag event has begun.  The drag cannot be canceled from this function.
20287      * @param {Number} x The x position of the click on the dragged object
20288      * @param {Number} y The y position of the click on the dragged object
20289      */
20290     onStartDrag : Roo.emptyFn,
20291
20292     // private - YUI override
20293     startDrag : function(x, y){
20294         this.proxy.reset();
20295         this.dragging = true;
20296         this.proxy.update("");
20297         this.onInitDrag(x, y);
20298         this.proxy.show();
20299     },
20300
20301     // private
20302     onInitDrag : function(x, y){
20303         var clone = this.el.dom.cloneNode(true);
20304         clone.id = Roo.id(); // prevent duplicate ids
20305         this.proxy.update(clone);
20306         this.onStartDrag(x, y);
20307         return true;
20308     },
20309
20310     /**
20311      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20312      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20313      */
20314     getProxy : function(){
20315         return this.proxy;  
20316     },
20317
20318     /**
20319      * Hides the drag source's {@link Roo.dd.StatusProxy}
20320      */
20321     hideProxy : function(){
20322         this.proxy.hide();  
20323         this.proxy.reset(true);
20324         this.dragging = false;
20325     },
20326
20327     // private
20328     triggerCacheRefresh : function(){
20329         Roo.dd.DDM.refreshCache(this.groups);
20330     },
20331
20332     // private - override to prevent hiding
20333     b4EndDrag: function(e) {
20334     },
20335
20336     // private - override to prevent moving
20337     endDrag : function(e){
20338         this.onEndDrag(this.dragData, e);
20339     },
20340
20341     // private
20342     onEndDrag : function(data, e){
20343     },
20344     
20345     // private - pin to cursor
20346     autoOffset : function(x, y) {
20347         this.setDelta(-12, -20);
20348     }    
20349 });/*
20350  * Based on:
20351  * Ext JS Library 1.1.1
20352  * Copyright(c) 2006-2007, Ext JS, LLC.
20353  *
20354  * Originally Released Under LGPL - original licence link has changed is not relivant.
20355  *
20356  * Fork - LGPL
20357  * <script type="text/javascript">
20358  */
20359
20360
20361 /**
20362  * @class Roo.dd.DropTarget
20363  * @extends Roo.dd.DDTarget
20364  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20365  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20366  * @constructor
20367  * @param {String/HTMLElement/Element} el The container element
20368  * @param {Object} config
20369  */
20370 Roo.dd.DropTarget = function(el, config){
20371     this.el = Roo.get(el);
20372     
20373     var listeners = false; ;
20374     if (config && config.listeners) {
20375         listeners= config.listeners;
20376         delete config.listeners;
20377     }
20378     Roo.apply(this, config);
20379     
20380     if(this.containerScroll){
20381         Roo.dd.ScrollManager.register(this.el);
20382     }
20383     this.addEvents( {
20384          /**
20385          * @scope Roo.dd.DropTarget
20386          */
20387          
20388          /**
20389          * @event enter
20390          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20391          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20392          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20393          * 
20394          * IMPORTANT : it should set this.overClass and this.dropAllowed
20395          * 
20396          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20397          * @param {Event} e The event
20398          * @param {Object} data An object containing arbitrary data supplied by the drag source
20399          */
20400         "enter" : true,
20401         
20402          /**
20403          * @event over
20404          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20405          * This method will be called on every mouse movement while the drag source is over the drop target.
20406          * This default implementation simply returns the dropAllowed config value.
20407          * 
20408          * IMPORTANT : it should set this.dropAllowed
20409          * 
20410          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20411          * @param {Event} e The event
20412          * @param {Object} data An object containing arbitrary data supplied by the drag source
20413          
20414          */
20415         "over" : true,
20416         /**
20417          * @event out
20418          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20419          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20420          * overClass (if any) from the drop element.
20421          * 
20422          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20423          * @param {Event} e The event
20424          * @param {Object} data An object containing arbitrary data supplied by the drag source
20425          */
20426          "out" : true,
20427          
20428         /**
20429          * @event drop
20430          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20431          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20432          * implementation that does something to process the drop event and returns true so that the drag source's
20433          * repair action does not run.
20434          * 
20435          * IMPORTANT : it should set this.success
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          "drop" : true
20442     });
20443             
20444      
20445     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20446         this.el.dom, 
20447         this.ddGroup || this.group,
20448         {
20449             isTarget: true,
20450             listeners : listeners || {} 
20451            
20452         
20453         }
20454     );
20455
20456 };
20457
20458 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20459     /**
20460      * @cfg {String} overClass
20461      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20462      */
20463      /**
20464      * @cfg {String} ddGroup
20465      * The drag drop group to handle drop events for
20466      */
20467      
20468     /**
20469      * @cfg {String} dropAllowed
20470      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20471      */
20472     dropAllowed : "x-dd-drop-ok",
20473     /**
20474      * @cfg {String} dropNotAllowed
20475      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20476      */
20477     dropNotAllowed : "x-dd-drop-nodrop",
20478     /**
20479      * @cfg {boolean} success
20480      * set this after drop listener.. 
20481      */
20482     success : false,
20483     /**
20484      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20485      * if the drop point is valid for over/enter..
20486      */
20487     valid : false,
20488     // private
20489     isTarget : true,
20490
20491     // private
20492     isNotifyTarget : true,
20493     
20494     /**
20495      * @hide
20496      */
20497     notifyEnter : function(dd, e, data)
20498     {
20499         this.valid = true;
20500         this.fireEvent('enter', dd, e, data);
20501         if(this.overClass){
20502             this.el.addClass(this.overClass);
20503         }
20504         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20505             this.valid ? this.dropAllowed : this.dropNotAllowed
20506         );
20507     },
20508
20509     /**
20510      * @hide
20511      */
20512     notifyOver : function(dd, e, data)
20513     {
20514         this.valid = true;
20515         this.fireEvent('over', dd, e, data);
20516         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20517             this.valid ? this.dropAllowed : this.dropNotAllowed
20518         );
20519     },
20520
20521     /**
20522      * @hide
20523      */
20524     notifyOut : function(dd, e, data)
20525     {
20526         this.fireEvent('out', dd, e, data);
20527         if(this.overClass){
20528             this.el.removeClass(this.overClass);
20529         }
20530     },
20531
20532     /**
20533      * @hide
20534      */
20535     notifyDrop : function(dd, e, data)
20536     {
20537         this.success = false;
20538         this.fireEvent('drop', dd, e, data);
20539         return this.success;
20540     }
20541 });/*
20542  * Based on:
20543  * Ext JS Library 1.1.1
20544  * Copyright(c) 2006-2007, Ext JS, LLC.
20545  *
20546  * Originally Released Under LGPL - original licence link has changed is not relivant.
20547  *
20548  * Fork - LGPL
20549  * <script type="text/javascript">
20550  */
20551
20552
20553 /**
20554  * @class Roo.dd.DragZone
20555  * @extends Roo.dd.DragSource
20556  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20557  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20558  * @constructor
20559  * @param {String/HTMLElement/Element} el The container element
20560  * @param {Object} config
20561  */
20562 Roo.dd.DragZone = function(el, config){
20563     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20564     if(this.containerScroll){
20565         Roo.dd.ScrollManager.register(this.el);
20566     }
20567 };
20568
20569 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20570     /**
20571      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20572      * for auto scrolling during drag operations.
20573      */
20574     /**
20575      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20576      * method after a failed drop (defaults to "c3daf9" - light blue)
20577      */
20578
20579     /**
20580      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20581      * for a valid target to drag based on the mouse down. Override this method
20582      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20583      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20584      * @param {EventObject} e The mouse down event
20585      * @return {Object} The dragData
20586      */
20587     getDragData : function(e){
20588         return Roo.dd.Registry.getHandleFromEvent(e);
20589     },
20590     
20591     /**
20592      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20593      * this.dragData.ddel
20594      * @param {Number} x The x position of the click on the dragged object
20595      * @param {Number} y The y position of the click on the dragged object
20596      * @return {Boolean} true to continue the drag, false to cancel
20597      */
20598     onInitDrag : function(x, y){
20599         this.proxy.update(this.dragData.ddel.cloneNode(true));
20600         this.onStartDrag(x, y);
20601         return true;
20602     },
20603     
20604     /**
20605      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20606      */
20607     afterRepair : function(){
20608         if(Roo.enableFx){
20609             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20610         }
20611         this.dragging = false;
20612     },
20613
20614     /**
20615      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20616      * the XY of this.dragData.ddel
20617      * @param {EventObject} e The mouse up event
20618      * @return {Array} The xy location (e.g. [100, 200])
20619      */
20620     getRepairXY : function(e){
20621         return Roo.Element.fly(this.dragData.ddel).getXY();  
20622     }
20623 });/*
20624  * Based on:
20625  * Ext JS Library 1.1.1
20626  * Copyright(c) 2006-2007, Ext JS, LLC.
20627  *
20628  * Originally Released Under LGPL - original licence link has changed is not relivant.
20629  *
20630  * Fork - LGPL
20631  * <script type="text/javascript">
20632  */
20633 /**
20634  * @class Roo.dd.DropZone
20635  * @extends Roo.dd.DropTarget
20636  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20637  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20638  * @constructor
20639  * @param {String/HTMLElement/Element} el The container element
20640  * @param {Object} config
20641  */
20642 Roo.dd.DropZone = function(el, config){
20643     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20644 };
20645
20646 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20647     /**
20648      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20649      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20650      * provide your own custom lookup.
20651      * @param {Event} e The event
20652      * @return {Object} data The custom data
20653      */
20654     getTargetFromEvent : function(e){
20655         return Roo.dd.Registry.getTargetFromEvent(e);
20656     },
20657
20658     /**
20659      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20660      * that it has registered.  This method has no default implementation and should be overridden to provide
20661      * node-specific processing if necessary.
20662      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20663      * {@link #getTargetFromEvent} for this node)
20664      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20665      * @param {Event} e The event
20666      * @param {Object} data An object containing arbitrary data supplied by the drag source
20667      */
20668     onNodeEnter : function(n, dd, e, data){
20669         
20670     },
20671
20672     /**
20673      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20674      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20675      * overridden to provide the proper feedback.
20676      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20677      * {@link #getTargetFromEvent} for this node)
20678      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20679      * @param {Event} e The event
20680      * @param {Object} data An object containing arbitrary data supplied by the drag source
20681      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20682      * underlying {@link Roo.dd.StatusProxy} can be updated
20683      */
20684     onNodeOver : function(n, dd, e, data){
20685         return this.dropAllowed;
20686     },
20687
20688     /**
20689      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20690      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20691      * node-specific processing if necessary.
20692      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20693      * {@link #getTargetFromEvent} for this node)
20694      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20695      * @param {Event} e The event
20696      * @param {Object} data An object containing arbitrary data supplied by the drag source
20697      */
20698     onNodeOut : function(n, dd, e, data){
20699         
20700     },
20701
20702     /**
20703      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20704      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20705      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20706      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20707      * {@link #getTargetFromEvent} for this node)
20708      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20709      * @param {Event} e The event
20710      * @param {Object} data An object containing arbitrary data supplied by the drag source
20711      * @return {Boolean} True if the drop was valid, else false
20712      */
20713     onNodeDrop : function(n, dd, e, data){
20714         return false;
20715     },
20716
20717     /**
20718      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20719      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20720      * it should be overridden to provide the proper feedback if necessary.
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      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20725      * underlying {@link Roo.dd.StatusProxy} can be updated
20726      */
20727     onContainerOver : function(dd, e, data){
20728         return this.dropNotAllowed;
20729     },
20730
20731     /**
20732      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20733      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20734      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20735      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20736      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20737      * @param {Event} e The event
20738      * @param {Object} data An object containing arbitrary data supplied by the drag source
20739      * @return {Boolean} True if the drop was valid, else false
20740      */
20741     onContainerDrop : function(dd, e, data){
20742         return false;
20743     },
20744
20745     /**
20746      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20747      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20748      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20749      * you should override this method and provide a custom implementation.
20750      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20751      * @param {Event} e The event
20752      * @param {Object} data An object containing arbitrary data supplied by the drag source
20753      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20754      * underlying {@link Roo.dd.StatusProxy} can be updated
20755      */
20756     notifyEnter : function(dd, e, data){
20757         return this.dropNotAllowed;
20758     },
20759
20760     /**
20761      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20762      * This method will be called on every mouse movement while the drag source is over the drop zone.
20763      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20764      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20765      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20766      * registered node, it will call {@link #onContainerOver}.
20767      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20768      * @param {Event} e The event
20769      * @param {Object} data An object containing arbitrary data supplied by the drag source
20770      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20771      * underlying {@link Roo.dd.StatusProxy} can be updated
20772      */
20773     notifyOver : function(dd, e, data){
20774         var n = this.getTargetFromEvent(e);
20775         if(!n){ // not over valid drop target
20776             if(this.lastOverNode){
20777                 this.onNodeOut(this.lastOverNode, dd, e, data);
20778                 this.lastOverNode = null;
20779             }
20780             return this.onContainerOver(dd, e, data);
20781         }
20782         if(this.lastOverNode != n){
20783             if(this.lastOverNode){
20784                 this.onNodeOut(this.lastOverNode, dd, e, data);
20785             }
20786             this.onNodeEnter(n, dd, e, data);
20787             this.lastOverNode = n;
20788         }
20789         return this.onNodeOver(n, dd, e, data);
20790     },
20791
20792     /**
20793      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20794      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20795      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20796      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20797      * @param {Event} e The event
20798      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20799      */
20800     notifyOut : function(dd, e, data){
20801         if(this.lastOverNode){
20802             this.onNodeOut(this.lastOverNode, dd, e, data);
20803             this.lastOverNode = null;
20804         }
20805     },
20806
20807     /**
20808      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20809      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20810      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20811      * otherwise it will call {@link #onContainerDrop}.
20812      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20813      * @param {Event} e The event
20814      * @param {Object} data An object containing arbitrary data supplied by the drag source
20815      * @return {Boolean} True if the drop was valid, else false
20816      */
20817     notifyDrop : function(dd, e, data){
20818         if(this.lastOverNode){
20819             this.onNodeOut(this.lastOverNode, dd, e, data);
20820             this.lastOverNode = null;
20821         }
20822         var n = this.getTargetFromEvent(e);
20823         return n ?
20824             this.onNodeDrop(n, dd, e, data) :
20825             this.onContainerDrop(dd, e, data);
20826     },
20827
20828     // private
20829     triggerCacheRefresh : function(){
20830         Roo.dd.DDM.refreshCache(this.groups);
20831     }  
20832 });/*
20833  * Based on:
20834  * Ext JS Library 1.1.1
20835  * Copyright(c) 2006-2007, Ext JS, LLC.
20836  *
20837  * Originally Released Under LGPL - original licence link has changed is not relivant.
20838  *
20839  * Fork - LGPL
20840  * <script type="text/javascript">
20841  */
20842
20843
20844 /**
20845  * @class Roo.data.SortTypes
20846  * @singleton
20847  * Defines the default sorting (casting?) comparison functions used when sorting data.
20848  */
20849 Roo.data.SortTypes = {
20850     /**
20851      * Default sort that does nothing
20852      * @param {Mixed} s The value being converted
20853      * @return {Mixed} The comparison value
20854      */
20855     none : function(s){
20856         return s;
20857     },
20858     
20859     /**
20860      * The regular expression used to strip tags
20861      * @type {RegExp}
20862      * @property
20863      */
20864     stripTagsRE : /<\/?[^>]+>/gi,
20865     
20866     /**
20867      * Strips all HTML tags to sort on text only
20868      * @param {Mixed} s The value being converted
20869      * @return {String} The comparison value
20870      */
20871     asText : function(s){
20872         return String(s).replace(this.stripTagsRE, "");
20873     },
20874     
20875     /**
20876      * Strips all HTML tags to sort on text only - Case insensitive
20877      * @param {Mixed} s The value being converted
20878      * @return {String} The comparison value
20879      */
20880     asUCText : function(s){
20881         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20882     },
20883     
20884     /**
20885      * Case insensitive string
20886      * @param {Mixed} s The value being converted
20887      * @return {String} The comparison value
20888      */
20889     asUCString : function(s) {
20890         return String(s).toUpperCase();
20891     },
20892     
20893     /**
20894      * Date sorting
20895      * @param {Mixed} s The value being converted
20896      * @return {Number} The comparison value
20897      */
20898     asDate : function(s) {
20899         if(!s){
20900             return 0;
20901         }
20902         if(s instanceof Date){
20903             return s.getTime();
20904         }
20905         return Date.parse(String(s));
20906     },
20907     
20908     /**
20909      * Float sorting
20910      * @param {Mixed} s The value being converted
20911      * @return {Float} The comparison value
20912      */
20913     asFloat : function(s) {
20914         var val = parseFloat(String(s).replace(/,/g, ""));
20915         if(isNaN(val)) val = 0;
20916         return val;
20917     },
20918     
20919     /**
20920      * Integer sorting
20921      * @param {Mixed} s The value being converted
20922      * @return {Number} The comparison value
20923      */
20924     asInt : function(s) {
20925         var val = parseInt(String(s).replace(/,/g, ""));
20926         if(isNaN(val)) val = 0;
20927         return val;
20928     }
20929 };/*
20930  * Based on:
20931  * Ext JS Library 1.1.1
20932  * Copyright(c) 2006-2007, Ext JS, LLC.
20933  *
20934  * Originally Released Under LGPL - original licence link has changed is not relivant.
20935  *
20936  * Fork - LGPL
20937  * <script type="text/javascript">
20938  */
20939
20940 /**
20941 * @class Roo.data.Record
20942  * Instances of this class encapsulate both record <em>definition</em> information, and record
20943  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20944  * to access Records cached in an {@link Roo.data.Store} object.<br>
20945  * <p>
20946  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20947  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20948  * objects.<br>
20949  * <p>
20950  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20951  * @constructor
20952  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20953  * {@link #create}. The parameters are the same.
20954  * @param {Array} data An associative Array of data values keyed by the field name.
20955  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20956  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20957  * not specified an integer id is generated.
20958  */
20959 Roo.data.Record = function(data, id){
20960     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20961     this.data = data;
20962 };
20963
20964 /**
20965  * Generate a constructor for a specific record layout.
20966  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20967  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20968  * Each field definition object may contain the following properties: <ul>
20969  * <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,
20970  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20971  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20972  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20973  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20974  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20975  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20976  * this may be omitted.</p></li>
20977  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20978  * <ul><li>auto (Default, implies no conversion)</li>
20979  * <li>string</li>
20980  * <li>int</li>
20981  * <li>float</li>
20982  * <li>boolean</li>
20983  * <li>date</li></ul></p></li>
20984  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20985  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20986  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20987  * by the Reader into an object that will be stored in the Record. It is passed the
20988  * following parameters:<ul>
20989  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20990  * </ul></p></li>
20991  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20992  * </ul>
20993  * <br>usage:<br><pre><code>
20994 var TopicRecord = Roo.data.Record.create(
20995     {name: 'title', mapping: 'topic_title'},
20996     {name: 'author', mapping: 'username'},
20997     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
20998     {name: 'lastPost', mapping: 'post_time', type: 'date'},
20999     {name: 'lastPoster', mapping: 'user2'},
21000     {name: 'excerpt', mapping: 'post_text'}
21001 );
21002
21003 var myNewRecord = new TopicRecord({
21004     title: 'Do my job please',
21005     author: 'noobie',
21006     totalPosts: 1,
21007     lastPost: new Date(),
21008     lastPoster: 'Animal',
21009     excerpt: 'No way dude!'
21010 });
21011 myStore.add(myNewRecord);
21012 </code></pre>
21013  * @method create
21014  * @static
21015  */
21016 Roo.data.Record.create = function(o){
21017     var f = function(){
21018         f.superclass.constructor.apply(this, arguments);
21019     };
21020     Roo.extend(f, Roo.data.Record);
21021     var p = f.prototype;
21022     p.fields = new Roo.util.MixedCollection(false, function(field){
21023         return field.name;
21024     });
21025     for(var i = 0, len = o.length; i < len; i++){
21026         p.fields.add(new Roo.data.Field(o[i]));
21027     }
21028     f.getField = function(name){
21029         return p.fields.get(name);  
21030     };
21031     return f;
21032 };
21033
21034 Roo.data.Record.AUTO_ID = 1000;
21035 Roo.data.Record.EDIT = 'edit';
21036 Roo.data.Record.REJECT = 'reject';
21037 Roo.data.Record.COMMIT = 'commit';
21038
21039 Roo.data.Record.prototype = {
21040     /**
21041      * Readonly flag - true if this record has been modified.
21042      * @type Boolean
21043      */
21044     dirty : false,
21045     editing : false,
21046     error: null,
21047     modified: null,
21048
21049     // private
21050     join : function(store){
21051         this.store = store;
21052     },
21053
21054     /**
21055      * Set the named field to the specified value.
21056      * @param {String} name The name of the field to set.
21057      * @param {Object} value The value to set the field to.
21058      */
21059     set : function(name, value){
21060         if(this.data[name] == value){
21061             return;
21062         }
21063         this.dirty = true;
21064         if(!this.modified){
21065             this.modified = {};
21066         }
21067         if(typeof this.modified[name] == 'undefined'){
21068             this.modified[name] = this.data[name];
21069         }
21070         this.data[name] = value;
21071         if(!this.editing && this.store){
21072             this.store.afterEdit(this);
21073         }       
21074     },
21075
21076     /**
21077      * Get the value of the named field.
21078      * @param {String} name The name of the field to get the value of.
21079      * @return {Object} The value of the field.
21080      */
21081     get : function(name){
21082         return this.data[name]; 
21083     },
21084
21085     // private
21086     beginEdit : function(){
21087         this.editing = true;
21088         this.modified = {}; 
21089     },
21090
21091     // private
21092     cancelEdit : function(){
21093         this.editing = false;
21094         delete this.modified;
21095     },
21096
21097     // private
21098     endEdit : function(){
21099         this.editing = false;
21100         if(this.dirty && this.store){
21101             this.store.afterEdit(this);
21102         }
21103     },
21104
21105     /**
21106      * Usually called by the {@link Roo.data.Store} which owns the Record.
21107      * Rejects all changes made to the Record since either creation, or the last commit operation.
21108      * Modified fields are reverted to their original values.
21109      * <p>
21110      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21111      * of reject operations.
21112      */
21113     reject : function(){
21114         var m = this.modified;
21115         for(var n in m){
21116             if(typeof m[n] != "function"){
21117                 this.data[n] = m[n];
21118             }
21119         }
21120         this.dirty = false;
21121         delete this.modified;
21122         this.editing = false;
21123         if(this.store){
21124             this.store.afterReject(this);
21125         }
21126     },
21127
21128     /**
21129      * Usually called by the {@link Roo.data.Store} which owns the Record.
21130      * Commits all changes made to the Record since either creation, or the last commit operation.
21131      * <p>
21132      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21133      * of commit operations.
21134      */
21135     commit : function(){
21136         this.dirty = false;
21137         delete this.modified;
21138         this.editing = false;
21139         if(this.store){
21140             this.store.afterCommit(this);
21141         }
21142     },
21143
21144     // private
21145     hasError : function(){
21146         return this.error != null;
21147     },
21148
21149     // private
21150     clearError : function(){
21151         this.error = null;
21152     },
21153
21154     /**
21155      * Creates a copy of this record.
21156      * @param {String} id (optional) A new record id if you don't want to use this record's id
21157      * @return {Record}
21158      */
21159     copy : function(newId) {
21160         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21161     }
21162 };/*
21163  * Based on:
21164  * Ext JS Library 1.1.1
21165  * Copyright(c) 2006-2007, Ext JS, LLC.
21166  *
21167  * Originally Released Under LGPL - original licence link has changed is not relivant.
21168  *
21169  * Fork - LGPL
21170  * <script type="text/javascript">
21171  */
21172
21173
21174
21175 /**
21176  * @class Roo.data.Store
21177  * @extends Roo.util.Observable
21178  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21179  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21180  * <p>
21181  * 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
21182  * has no knowledge of the format of the data returned by the Proxy.<br>
21183  * <p>
21184  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21185  * instances from the data object. These records are cached and made available through accessor functions.
21186  * @constructor
21187  * Creates a new Store.
21188  * @param {Object} config A config object containing the objects needed for the Store to access data,
21189  * and read the data into Records.
21190  */
21191 Roo.data.Store = function(config){
21192     this.data = new Roo.util.MixedCollection(false);
21193     this.data.getKey = function(o){
21194         return o.id;
21195     };
21196     this.baseParams = {};
21197     // private
21198     this.paramNames = {
21199         "start" : "start",
21200         "limit" : "limit",
21201         "sort" : "sort",
21202         "dir" : "dir",
21203         "multisort" : "_multisort"
21204     };
21205
21206     if(config && config.data){
21207         this.inlineData = config.data;
21208         delete config.data;
21209     }
21210
21211     Roo.apply(this, config);
21212     
21213     if(this.reader){ // reader passed
21214         this.reader = Roo.factory(this.reader, Roo.data);
21215         this.reader.xmodule = this.xmodule || false;
21216         if(!this.recordType){
21217             this.recordType = this.reader.recordType;
21218         }
21219         if(this.reader.onMetaChange){
21220             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21221         }
21222     }
21223
21224     if(this.recordType){
21225         this.fields = this.recordType.prototype.fields;
21226     }
21227     this.modified = [];
21228
21229     this.addEvents({
21230         /**
21231          * @event datachanged
21232          * Fires when the data cache has changed, and a widget which is using this Store
21233          * as a Record cache should refresh its view.
21234          * @param {Store} this
21235          */
21236         datachanged : true,
21237         /**
21238          * @event metachange
21239          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21240          * @param {Store} this
21241          * @param {Object} meta The JSON metadata
21242          */
21243         metachange : true,
21244         /**
21245          * @event add
21246          * Fires when Records have been added to the Store
21247          * @param {Store} this
21248          * @param {Roo.data.Record[]} records The array of Records added
21249          * @param {Number} index The index at which the record(s) were added
21250          */
21251         add : true,
21252         /**
21253          * @event remove
21254          * Fires when a Record has been removed from the Store
21255          * @param {Store} this
21256          * @param {Roo.data.Record} record The Record that was removed
21257          * @param {Number} index The index at which the record was removed
21258          */
21259         remove : true,
21260         /**
21261          * @event update
21262          * Fires when a Record has been updated
21263          * @param {Store} this
21264          * @param {Roo.data.Record} record The Record that was updated
21265          * @param {String} operation The update operation being performed.  Value may be one of:
21266          * <pre><code>
21267  Roo.data.Record.EDIT
21268  Roo.data.Record.REJECT
21269  Roo.data.Record.COMMIT
21270          * </code></pre>
21271          */
21272         update : true,
21273         /**
21274          * @event clear
21275          * Fires when the data cache has been cleared.
21276          * @param {Store} this
21277          */
21278         clear : true,
21279         /**
21280          * @event beforeload
21281          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21282          * the load action will be canceled.
21283          * @param {Store} this
21284          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21285          */
21286         beforeload : true,
21287         /**
21288          * @event beforeloadadd
21289          * Fires after a new set of Records has been loaded.
21290          * @param {Store} this
21291          * @param {Roo.data.Record[]} records The Records that were loaded
21292          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21293          */
21294         beforeloadadd : true,
21295         /**
21296          * @event load
21297          * Fires after a new set of Records has been loaded, before they are added to the store.
21298          * @param {Store} this
21299          * @param {Roo.data.Record[]} records The Records that were loaded
21300          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21301          * @params {Object} return from reader
21302          */
21303         load : true,
21304         /**
21305          * @event loadexception
21306          * Fires if an exception occurs in the Proxy during loading.
21307          * Called with the signature of the Proxy's "loadexception" event.
21308          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21309          * 
21310          * @param {Proxy} 
21311          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21312          * @param {Object} load options 
21313          * @param {Object} jsonData from your request (normally this contains the Exception)
21314          */
21315         loadexception : true
21316     });
21317     
21318     if(this.proxy){
21319         this.proxy = Roo.factory(this.proxy, Roo.data);
21320         this.proxy.xmodule = this.xmodule || false;
21321         this.relayEvents(this.proxy,  ["loadexception"]);
21322     }
21323     this.sortToggle = {};
21324     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21325
21326     Roo.data.Store.superclass.constructor.call(this);
21327
21328     if(this.inlineData){
21329         this.loadData(this.inlineData);
21330         delete this.inlineData;
21331     }
21332 };
21333
21334 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21335      /**
21336     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21337     * without a remote query - used by combo/forms at present.
21338     */
21339     
21340     /**
21341     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21342     */
21343     /**
21344     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21345     */
21346     /**
21347     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21348     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21349     */
21350     /**
21351     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21352     * on any HTTP request
21353     */
21354     /**
21355     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21356     */
21357     /**
21358     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21359     */
21360     multiSort: false,
21361     /**
21362     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21363     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21364     */
21365     remoteSort : false,
21366
21367     /**
21368     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21369      * loaded or when a record is removed. (defaults to false).
21370     */
21371     pruneModifiedRecords : false,
21372
21373     // private
21374     lastOptions : null,
21375
21376     /**
21377      * Add Records to the Store and fires the add event.
21378      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21379      */
21380     add : function(records){
21381         records = [].concat(records);
21382         for(var i = 0, len = records.length; i < len; i++){
21383             records[i].join(this);
21384         }
21385         var index = this.data.length;
21386         this.data.addAll(records);
21387         this.fireEvent("add", this, records, index);
21388     },
21389
21390     /**
21391      * Remove a Record from the Store and fires the remove event.
21392      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21393      */
21394     remove : function(record){
21395         var index = this.data.indexOf(record);
21396         this.data.removeAt(index);
21397         if(this.pruneModifiedRecords){
21398             this.modified.remove(record);
21399         }
21400         this.fireEvent("remove", this, record, index);
21401     },
21402
21403     /**
21404      * Remove all Records from the Store and fires the clear event.
21405      */
21406     removeAll : function(){
21407         this.data.clear();
21408         if(this.pruneModifiedRecords){
21409             this.modified = [];
21410         }
21411         this.fireEvent("clear", this);
21412     },
21413
21414     /**
21415      * Inserts Records to the Store at the given index and fires the add event.
21416      * @param {Number} index The start index at which to insert the passed Records.
21417      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21418      */
21419     insert : function(index, records){
21420         records = [].concat(records);
21421         for(var i = 0, len = records.length; i < len; i++){
21422             this.data.insert(index, records[i]);
21423             records[i].join(this);
21424         }
21425         this.fireEvent("add", this, records, index);
21426     },
21427
21428     /**
21429      * Get the index within the cache of the passed Record.
21430      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21431      * @return {Number} The index of the passed Record. Returns -1 if not found.
21432      */
21433     indexOf : function(record){
21434         return this.data.indexOf(record);
21435     },
21436
21437     /**
21438      * Get the index within the cache of the Record with the passed id.
21439      * @param {String} id The id of the Record to find.
21440      * @return {Number} The index of the Record. Returns -1 if not found.
21441      */
21442     indexOfId : function(id){
21443         return this.data.indexOfKey(id);
21444     },
21445
21446     /**
21447      * Get the Record with the specified id.
21448      * @param {String} id The id of the Record to find.
21449      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21450      */
21451     getById : function(id){
21452         return this.data.key(id);
21453     },
21454
21455     /**
21456      * Get the Record at the specified index.
21457      * @param {Number} index The index of the Record to find.
21458      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21459      */
21460     getAt : function(index){
21461         return this.data.itemAt(index);
21462     },
21463
21464     /**
21465      * Returns a range of Records between specified indices.
21466      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21467      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21468      * @return {Roo.data.Record[]} An array of Records
21469      */
21470     getRange : function(start, end){
21471         return this.data.getRange(start, end);
21472     },
21473
21474     // private
21475     storeOptions : function(o){
21476         o = Roo.apply({}, o);
21477         delete o.callback;
21478         delete o.scope;
21479         this.lastOptions = o;
21480     },
21481
21482     /**
21483      * Loads the Record cache from the configured Proxy using the configured Reader.
21484      * <p>
21485      * If using remote paging, then the first load call must specify the <em>start</em>
21486      * and <em>limit</em> properties in the options.params property to establish the initial
21487      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21488      * <p>
21489      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21490      * and this call will return before the new data has been loaded. Perform any post-processing
21491      * in a callback function, or in a "load" event handler.</strong>
21492      * <p>
21493      * @param {Object} options An object containing properties which control loading options:<ul>
21494      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21495      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21496      * passed the following arguments:<ul>
21497      * <li>r : Roo.data.Record[]</li>
21498      * <li>options: Options object from the load call</li>
21499      * <li>success: Boolean success indicator</li></ul></li>
21500      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21501      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21502      * </ul>
21503      */
21504     load : function(options){
21505         options = options || {};
21506         if(this.fireEvent("beforeload", this, options) !== false){
21507             this.storeOptions(options);
21508             var p = Roo.apply(options.params || {}, this.baseParams);
21509             // if meta was not loaded from remote source.. try requesting it.
21510             if (!this.reader.metaFromRemote) {
21511                 p._requestMeta = 1;
21512             }
21513             if(this.sortInfo && this.remoteSort){
21514                 var pn = this.paramNames;
21515                 p[pn["sort"]] = this.sortInfo.field;
21516                 p[pn["dir"]] = this.sortInfo.direction;
21517             }
21518             if (this.multiSort) {
21519                 var pn = this.paramNames;
21520                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21521             }
21522             
21523             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21524         }
21525     },
21526
21527     /**
21528      * Reloads the Record cache from the configured Proxy using the configured Reader and
21529      * the options from the last load operation performed.
21530      * @param {Object} options (optional) An object containing properties which may override the options
21531      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21532      * the most recently used options are reused).
21533      */
21534     reload : function(options){
21535         this.load(Roo.applyIf(options||{}, this.lastOptions));
21536     },
21537
21538     // private
21539     // Called as a callback by the Reader during a load operation.
21540     loadRecords : function(o, options, success){
21541         if(!o || success === false){
21542             if(success !== false){
21543                 this.fireEvent("load", this, [], options, o);
21544             }
21545             if(options.callback){
21546                 options.callback.call(options.scope || this, [], options, false);
21547             }
21548             return;
21549         }
21550         // if data returned failure - throw an exception.
21551         if (o.success === false) {
21552             // show a message if no listener is registered.
21553             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21554                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21555             }
21556             // loadmask wil be hooked into this..
21557             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21558             return;
21559         }
21560         var r = o.records, t = o.totalRecords || r.length;
21561         
21562         this.fireEvent("beforeloadadd", this, r, options, o);
21563         
21564         if(!options || options.add !== true){
21565             if(this.pruneModifiedRecords){
21566                 this.modified = [];
21567             }
21568             for(var i = 0, len = r.length; i < len; i++){
21569                 r[i].join(this);
21570             }
21571             if(this.snapshot){
21572                 this.data = this.snapshot;
21573                 delete this.snapshot;
21574             }
21575             this.data.clear();
21576             this.data.addAll(r);
21577             this.totalLength = t;
21578             this.applySort();
21579             this.fireEvent("datachanged", this);
21580         }else{
21581             this.totalLength = Math.max(t, this.data.length+r.length);
21582             this.add(r);
21583         }
21584         this.fireEvent("load", this, r, options, o);
21585         if(options.callback){
21586             options.callback.call(options.scope || this, r, options, true);
21587         }
21588     },
21589
21590
21591     /**
21592      * Loads data from a passed data block. A Reader which understands the format of the data
21593      * must have been configured in the constructor.
21594      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21595      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21596      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21597      */
21598     loadData : function(o, append){
21599         var r = this.reader.readRecords(o);
21600         this.loadRecords(r, {add: append}, true);
21601     },
21602
21603     /**
21604      * Gets the number of cached records.
21605      * <p>
21606      * <em>If using paging, this may not be the total size of the dataset. If the data object
21607      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21608      * the data set size</em>
21609      */
21610     getCount : function(){
21611         return this.data.length || 0;
21612     },
21613
21614     /**
21615      * Gets the total number of records in the dataset as returned by the server.
21616      * <p>
21617      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21618      * the dataset size</em>
21619      */
21620     getTotalCount : function(){
21621         return this.totalLength || 0;
21622     },
21623
21624     /**
21625      * Returns the sort state of the Store as an object with two properties:
21626      * <pre><code>
21627  field {String} The name of the field by which the Records are sorted
21628  direction {String} The sort order, "ASC" or "DESC"
21629      * </code></pre>
21630      */
21631     getSortState : function(){
21632         return this.sortInfo;
21633     },
21634
21635     // private
21636     applySort : function(){
21637         if(this.sortInfo && !this.remoteSort){
21638             var s = this.sortInfo, f = s.field;
21639             var st = this.fields.get(f).sortType;
21640             var fn = function(r1, r2){
21641                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21642                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21643             };
21644             this.data.sort(s.direction, fn);
21645             if(this.snapshot && this.snapshot != this.data){
21646                 this.snapshot.sort(s.direction, fn);
21647             }
21648         }
21649     },
21650
21651     /**
21652      * Sets the default sort column and order to be used by the next load operation.
21653      * @param {String} fieldName The name of the field to sort by.
21654      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21655      */
21656     setDefaultSort : function(field, dir){
21657         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21658     },
21659
21660     /**
21661      * Sort the Records.
21662      * If remote sorting is used, the sort is performed on the server, and the cache is
21663      * reloaded. If local sorting is used, the cache is sorted internally.
21664      * @param {String} fieldName The name of the field to sort by.
21665      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21666      */
21667     sort : function(fieldName, dir){
21668         var f = this.fields.get(fieldName);
21669         if(!dir){
21670             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21671             
21672             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21673                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21674             }else{
21675                 dir = f.sortDir;
21676             }
21677         }
21678         this.sortToggle[f.name] = dir;
21679         this.sortInfo = {field: f.name, direction: dir};
21680         if(!this.remoteSort){
21681             this.applySort();
21682             this.fireEvent("datachanged", this);
21683         }else{
21684             this.load(this.lastOptions);
21685         }
21686     },
21687
21688     /**
21689      * Calls the specified function for each of the Records in the cache.
21690      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21691      * Returning <em>false</em> aborts and exits the iteration.
21692      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21693      */
21694     each : function(fn, scope){
21695         this.data.each(fn, scope);
21696     },
21697
21698     /**
21699      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21700      * (e.g., during paging).
21701      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21702      */
21703     getModifiedRecords : function(){
21704         return this.modified;
21705     },
21706
21707     // private
21708     createFilterFn : function(property, value, anyMatch){
21709         if(!value.exec){ // not a regex
21710             value = String(value);
21711             if(value.length == 0){
21712                 return false;
21713             }
21714             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21715         }
21716         return function(r){
21717             return value.test(r.data[property]);
21718         };
21719     },
21720
21721     /**
21722      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21723      * @param {String} property A field on your records
21724      * @param {Number} start The record index to start at (defaults to 0)
21725      * @param {Number} end The last record index to include (defaults to length - 1)
21726      * @return {Number} The sum
21727      */
21728     sum : function(property, start, end){
21729         var rs = this.data.items, v = 0;
21730         start = start || 0;
21731         end = (end || end === 0) ? end : rs.length-1;
21732
21733         for(var i = start; i <= end; i++){
21734             v += (rs[i].data[property] || 0);
21735         }
21736         return v;
21737     },
21738
21739     /**
21740      * Filter the records by a specified property.
21741      * @param {String} field A field on your records
21742      * @param {String/RegExp} value Either a string that the field
21743      * should start with or a RegExp to test against the field
21744      * @param {Boolean} anyMatch True to match any part not just the beginning
21745      */
21746     filter : function(property, value, anyMatch){
21747         var fn = this.createFilterFn(property, value, anyMatch);
21748         return fn ? this.filterBy(fn) : this.clearFilter();
21749     },
21750
21751     /**
21752      * Filter by a function. The specified function will be called with each
21753      * record in this data source. If the function returns true the record is included,
21754      * otherwise it is filtered.
21755      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21756      * @param {Object} scope (optional) The scope of the function (defaults to this)
21757      */
21758     filterBy : function(fn, scope){
21759         this.snapshot = this.snapshot || this.data;
21760         this.data = this.queryBy(fn, scope||this);
21761         this.fireEvent("datachanged", this);
21762     },
21763
21764     /**
21765      * Query the records by a specified property.
21766      * @param {String} field A field on your records
21767      * @param {String/RegExp} value Either a string that the field
21768      * should start with or a RegExp to test against the field
21769      * @param {Boolean} anyMatch True to match any part not just the beginning
21770      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21771      */
21772     query : function(property, value, anyMatch){
21773         var fn = this.createFilterFn(property, value, anyMatch);
21774         return fn ? this.queryBy(fn) : this.data.clone();
21775     },
21776
21777     /**
21778      * Query by a function. The specified function will be called with each
21779      * record in this data source. If the function returns true the record is included
21780      * in the results.
21781      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21782      * @param {Object} scope (optional) The scope of the function (defaults to this)
21783       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21784      **/
21785     queryBy : function(fn, scope){
21786         var data = this.snapshot || this.data;
21787         return data.filterBy(fn, scope||this);
21788     },
21789
21790     /**
21791      * Collects unique values for a particular dataIndex from this store.
21792      * @param {String} dataIndex The property to collect
21793      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21794      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21795      * @return {Array} An array of the unique values
21796      **/
21797     collect : function(dataIndex, allowNull, bypassFilter){
21798         var d = (bypassFilter === true && this.snapshot) ?
21799                 this.snapshot.items : this.data.items;
21800         var v, sv, r = [], l = {};
21801         for(var i = 0, len = d.length; i < len; i++){
21802             v = d[i].data[dataIndex];
21803             sv = String(v);
21804             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21805                 l[sv] = true;
21806                 r[r.length] = v;
21807             }
21808         }
21809         return r;
21810     },
21811
21812     /**
21813      * Revert to a view of the Record cache with no filtering applied.
21814      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21815      */
21816     clearFilter : function(suppressEvent){
21817         if(this.snapshot && this.snapshot != this.data){
21818             this.data = this.snapshot;
21819             delete this.snapshot;
21820             if(suppressEvent !== true){
21821                 this.fireEvent("datachanged", this);
21822             }
21823         }
21824     },
21825
21826     // private
21827     afterEdit : function(record){
21828         if(this.modified.indexOf(record) == -1){
21829             this.modified.push(record);
21830         }
21831         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21832     },
21833     
21834     // private
21835     afterReject : function(record){
21836         this.modified.remove(record);
21837         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21838     },
21839
21840     // private
21841     afterCommit : function(record){
21842         this.modified.remove(record);
21843         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21844     },
21845
21846     /**
21847      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21848      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21849      */
21850     commitChanges : function(){
21851         var m = this.modified.slice(0);
21852         this.modified = [];
21853         for(var i = 0, len = m.length; i < len; i++){
21854             m[i].commit();
21855         }
21856     },
21857
21858     /**
21859      * Cancel outstanding changes on all changed records.
21860      */
21861     rejectChanges : function(){
21862         var m = this.modified.slice(0);
21863         this.modified = [];
21864         for(var i = 0, len = m.length; i < len; i++){
21865             m[i].reject();
21866         }
21867     },
21868
21869     onMetaChange : function(meta, rtype, o){
21870         this.recordType = rtype;
21871         this.fields = rtype.prototype.fields;
21872         delete this.snapshot;
21873         this.sortInfo = meta.sortInfo || this.sortInfo;
21874         this.modified = [];
21875         this.fireEvent('metachange', this, this.reader.meta);
21876     },
21877     
21878     moveIndex : function(data, type)
21879     {
21880         var index = this.indexOf(data);
21881         
21882         var newIndex = index + type;
21883         
21884         this.remove(data);
21885         
21886         this.insert(newIndex, data);
21887         
21888     }
21889 });/*
21890  * Based on:
21891  * Ext JS Library 1.1.1
21892  * Copyright(c) 2006-2007, Ext JS, LLC.
21893  *
21894  * Originally Released Under LGPL - original licence link has changed is not relivant.
21895  *
21896  * Fork - LGPL
21897  * <script type="text/javascript">
21898  */
21899
21900 /**
21901  * @class Roo.data.SimpleStore
21902  * @extends Roo.data.Store
21903  * Small helper class to make creating Stores from Array data easier.
21904  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21905  * @cfg {Array} fields An array of field definition objects, or field name strings.
21906  * @cfg {Array} data The multi-dimensional array of data
21907  * @constructor
21908  * @param {Object} config
21909  */
21910 Roo.data.SimpleStore = function(config){
21911     Roo.data.SimpleStore.superclass.constructor.call(this, {
21912         isLocal : true,
21913         reader: new Roo.data.ArrayReader({
21914                 id: config.id
21915             },
21916             Roo.data.Record.create(config.fields)
21917         ),
21918         proxy : new Roo.data.MemoryProxy(config.data)
21919     });
21920     this.load();
21921 };
21922 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21923  * Based on:
21924  * Ext JS Library 1.1.1
21925  * Copyright(c) 2006-2007, Ext JS, LLC.
21926  *
21927  * Originally Released Under LGPL - original licence link has changed is not relivant.
21928  *
21929  * Fork - LGPL
21930  * <script type="text/javascript">
21931  */
21932
21933 /**
21934 /**
21935  * @extends Roo.data.Store
21936  * @class Roo.data.JsonStore
21937  * Small helper class to make creating Stores for JSON data easier. <br/>
21938 <pre><code>
21939 var store = new Roo.data.JsonStore({
21940     url: 'get-images.php',
21941     root: 'images',
21942     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21943 });
21944 </code></pre>
21945  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21946  * JsonReader and HttpProxy (unless inline data is provided).</b>
21947  * @cfg {Array} fields An array of field definition objects, or field name strings.
21948  * @constructor
21949  * @param {Object} config
21950  */
21951 Roo.data.JsonStore = function(c){
21952     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21953         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21954         reader: new Roo.data.JsonReader(c, c.fields)
21955     }));
21956 };
21957 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21958  * Based on:
21959  * Ext JS Library 1.1.1
21960  * Copyright(c) 2006-2007, Ext JS, LLC.
21961  *
21962  * Originally Released Under LGPL - original licence link has changed is not relivant.
21963  *
21964  * Fork - LGPL
21965  * <script type="text/javascript">
21966  */
21967
21968  
21969 Roo.data.Field = function(config){
21970     if(typeof config == "string"){
21971         config = {name: config};
21972     }
21973     Roo.apply(this, config);
21974     
21975     if(!this.type){
21976         this.type = "auto";
21977     }
21978     
21979     var st = Roo.data.SortTypes;
21980     // named sortTypes are supported, here we look them up
21981     if(typeof this.sortType == "string"){
21982         this.sortType = st[this.sortType];
21983     }
21984     
21985     // set default sortType for strings and dates
21986     if(!this.sortType){
21987         switch(this.type){
21988             case "string":
21989                 this.sortType = st.asUCString;
21990                 break;
21991             case "date":
21992                 this.sortType = st.asDate;
21993                 break;
21994             default:
21995                 this.sortType = st.none;
21996         }
21997     }
21998
21999     // define once
22000     var stripRe = /[\$,%]/g;
22001
22002     // prebuilt conversion function for this field, instead of
22003     // switching every time we're reading a value
22004     if(!this.convert){
22005         var cv, dateFormat = this.dateFormat;
22006         switch(this.type){
22007             case "":
22008             case "auto":
22009             case undefined:
22010                 cv = function(v){ return v; };
22011                 break;
22012             case "string":
22013                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
22014                 break;
22015             case "int":
22016                 cv = function(v){
22017                     return v !== undefined && v !== null && v !== '' ?
22018                            parseInt(String(v).replace(stripRe, ""), 10) : '';
22019                     };
22020                 break;
22021             case "float":
22022                 cv = function(v){
22023                     return v !== undefined && v !== null && v !== '' ?
22024                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
22025                     };
22026                 break;
22027             case "bool":
22028             case "boolean":
22029                 cv = function(v){ return v === true || v === "true" || v == 1; };
22030                 break;
22031             case "date":
22032                 cv = function(v){
22033                     if(!v){
22034                         return '';
22035                     }
22036                     if(v instanceof Date){
22037                         return v;
22038                     }
22039                     if(dateFormat){
22040                         if(dateFormat == "timestamp"){
22041                             return new Date(v*1000);
22042                         }
22043                         return Date.parseDate(v, dateFormat);
22044                     }
22045                     var parsed = Date.parse(v);
22046                     return parsed ? new Date(parsed) : null;
22047                 };
22048              break;
22049             
22050         }
22051         this.convert = cv;
22052     }
22053 };
22054
22055 Roo.data.Field.prototype = {
22056     dateFormat: null,
22057     defaultValue: "",
22058     mapping: null,
22059     sortType : null,
22060     sortDir : "ASC"
22061 };/*
22062  * Based on:
22063  * Ext JS Library 1.1.1
22064  * Copyright(c) 2006-2007, Ext JS, LLC.
22065  *
22066  * Originally Released Under LGPL - original licence link has changed is not relivant.
22067  *
22068  * Fork - LGPL
22069  * <script type="text/javascript">
22070  */
22071  
22072 // Base class for reading structured data from a data source.  This class is intended to be
22073 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
22074
22075 /**
22076  * @class Roo.data.DataReader
22077  * Base class for reading structured data from a data source.  This class is intended to be
22078  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
22079  */
22080
22081 Roo.data.DataReader = function(meta, recordType){
22082     
22083     this.meta = meta;
22084     
22085     this.recordType = recordType instanceof Array ? 
22086         Roo.data.Record.create(recordType) : recordType;
22087 };
22088
22089 Roo.data.DataReader.prototype = {
22090      /**
22091      * Create an empty record
22092      * @param {Object} data (optional) - overlay some values
22093      * @return {Roo.data.Record} record created.
22094      */
22095     newRow :  function(d) {
22096         var da =  {};
22097         this.recordType.prototype.fields.each(function(c) {
22098             switch( c.type) {
22099                 case 'int' : da[c.name] = 0; break;
22100                 case 'date' : da[c.name] = new Date(); break;
22101                 case 'float' : da[c.name] = 0.0; break;
22102                 case 'boolean' : da[c.name] = false; break;
22103                 default : da[c.name] = ""; break;
22104             }
22105             
22106         });
22107         return new this.recordType(Roo.apply(da, d));
22108     }
22109     
22110 };/*
22111  * Based on:
22112  * Ext JS Library 1.1.1
22113  * Copyright(c) 2006-2007, Ext JS, LLC.
22114  *
22115  * Originally Released Under LGPL - original licence link has changed is not relivant.
22116  *
22117  * Fork - LGPL
22118  * <script type="text/javascript">
22119  */
22120
22121 /**
22122  * @class Roo.data.DataProxy
22123  * @extends Roo.data.Observable
22124  * This class is an abstract base class for implementations which provide retrieval of
22125  * unformatted data objects.<br>
22126  * <p>
22127  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
22128  * (of the appropriate type which knows how to parse the data object) to provide a block of
22129  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
22130  * <p>
22131  * Custom implementations must implement the load method as described in
22132  * {@link Roo.data.HttpProxy#load}.
22133  */
22134 Roo.data.DataProxy = function(){
22135     this.addEvents({
22136         /**
22137          * @event beforeload
22138          * Fires before a network request is made to retrieve a data object.
22139          * @param {Object} This DataProxy object.
22140          * @param {Object} params The params parameter to the load function.
22141          */
22142         beforeload : true,
22143         /**
22144          * @event load
22145          * Fires before the load method's callback is called.
22146          * @param {Object} This DataProxy object.
22147          * @param {Object} o The data object.
22148          * @param {Object} arg The callback argument object passed to the load function.
22149          */
22150         load : true,
22151         /**
22152          * @event loadexception
22153          * Fires if an Exception occurs during data retrieval.
22154          * @param {Object} This DataProxy object.
22155          * @param {Object} o The data object.
22156          * @param {Object} arg The callback argument object passed to the load function.
22157          * @param {Object} e The Exception.
22158          */
22159         loadexception : true
22160     });
22161     Roo.data.DataProxy.superclass.constructor.call(this);
22162 };
22163
22164 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22165
22166     /**
22167      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22168      */
22169 /*
22170  * Based on:
22171  * Ext JS Library 1.1.1
22172  * Copyright(c) 2006-2007, Ext JS, LLC.
22173  *
22174  * Originally Released Under LGPL - original licence link has changed is not relivant.
22175  *
22176  * Fork - LGPL
22177  * <script type="text/javascript">
22178  */
22179 /**
22180  * @class Roo.data.MemoryProxy
22181  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22182  * to the Reader when its load method is called.
22183  * @constructor
22184  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22185  */
22186 Roo.data.MemoryProxy = function(data){
22187     if (data.data) {
22188         data = data.data;
22189     }
22190     Roo.data.MemoryProxy.superclass.constructor.call(this);
22191     this.data = data;
22192 };
22193
22194 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22195     /**
22196      * Load data from the requested source (in this case an in-memory
22197      * data object passed to the constructor), read the data object into
22198      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22199      * process that block using the passed callback.
22200      * @param {Object} params This parameter is not used by the MemoryProxy class.
22201      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22202      * object into a block of Roo.data.Records.
22203      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22204      * The function must be passed <ul>
22205      * <li>The Record block object</li>
22206      * <li>The "arg" argument from the load function</li>
22207      * <li>A boolean success indicator</li>
22208      * </ul>
22209      * @param {Object} scope The scope in which to call the callback
22210      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22211      */
22212     load : function(params, reader, callback, scope, arg){
22213         params = params || {};
22214         var result;
22215         try {
22216             result = reader.readRecords(this.data);
22217         }catch(e){
22218             this.fireEvent("loadexception", this, arg, null, e);
22219             callback.call(scope, null, arg, false);
22220             return;
22221         }
22222         callback.call(scope, result, arg, true);
22223     },
22224     
22225     // private
22226     update : function(params, records){
22227         
22228     }
22229 });/*
22230  * Based on:
22231  * Ext JS Library 1.1.1
22232  * Copyright(c) 2006-2007, Ext JS, LLC.
22233  *
22234  * Originally Released Under LGPL - original licence link has changed is not relivant.
22235  *
22236  * Fork - LGPL
22237  * <script type="text/javascript">
22238  */
22239 /**
22240  * @class Roo.data.HttpProxy
22241  * @extends Roo.data.DataProxy
22242  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22243  * configured to reference a certain URL.<br><br>
22244  * <p>
22245  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22246  * from which the running page was served.<br><br>
22247  * <p>
22248  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22249  * <p>
22250  * Be aware that to enable the browser to parse an XML document, the server must set
22251  * the Content-Type header in the HTTP response to "text/xml".
22252  * @constructor
22253  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22254  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22255  * will be used to make the request.
22256  */
22257 Roo.data.HttpProxy = function(conn){
22258     Roo.data.HttpProxy.superclass.constructor.call(this);
22259     // is conn a conn config or a real conn?
22260     this.conn = conn;
22261     this.useAjax = !conn || !conn.events;
22262   
22263 };
22264
22265 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22266     // thse are take from connection...
22267     
22268     /**
22269      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22270      */
22271     /**
22272      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22273      * extra parameters to each request made by this object. (defaults to undefined)
22274      */
22275     /**
22276      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22277      *  to each request made by this object. (defaults to undefined)
22278      */
22279     /**
22280      * @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)
22281      */
22282     /**
22283      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22284      */
22285      /**
22286      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22287      * @type Boolean
22288      */
22289   
22290
22291     /**
22292      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22293      * @type Boolean
22294      */
22295     /**
22296      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22297      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22298      * a finer-grained basis than the DataProxy events.
22299      */
22300     getConnection : function(){
22301         return this.useAjax ? Roo.Ajax : this.conn;
22302     },
22303
22304     /**
22305      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22306      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22307      * process that block using the passed callback.
22308      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22309      * for the request to the remote server.
22310      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22311      * object into a block of Roo.data.Records.
22312      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22313      * The function must be passed <ul>
22314      * <li>The Record block object</li>
22315      * <li>The "arg" argument from the load function</li>
22316      * <li>A boolean success indicator</li>
22317      * </ul>
22318      * @param {Object} scope The scope in which to call the callback
22319      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22320      */
22321     load : function(params, reader, callback, scope, arg){
22322         if(this.fireEvent("beforeload", this, params) !== false){
22323             var  o = {
22324                 params : params || {},
22325                 request: {
22326                     callback : callback,
22327                     scope : scope,
22328                     arg : arg
22329                 },
22330                 reader: reader,
22331                 callback : this.loadResponse,
22332                 scope: this
22333             };
22334             if(this.useAjax){
22335                 Roo.applyIf(o, this.conn);
22336                 if(this.activeRequest){
22337                     Roo.Ajax.abort(this.activeRequest);
22338                 }
22339                 this.activeRequest = Roo.Ajax.request(o);
22340             }else{
22341                 this.conn.request(o);
22342             }
22343         }else{
22344             callback.call(scope||this, null, arg, false);
22345         }
22346     },
22347
22348     // private
22349     loadResponse : function(o, success, response){
22350         delete this.activeRequest;
22351         if(!success){
22352             this.fireEvent("loadexception", this, o, response);
22353             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22354             return;
22355         }
22356         var result;
22357         try {
22358             result = o.reader.read(response);
22359         }catch(e){
22360             this.fireEvent("loadexception", this, o, response, e);
22361             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22362             return;
22363         }
22364         
22365         this.fireEvent("load", this, o, o.request.arg);
22366         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22367     },
22368
22369     // private
22370     update : function(dataSet){
22371
22372     },
22373
22374     // private
22375     updateResponse : function(dataSet){
22376
22377     }
22378 });/*
22379  * Based on:
22380  * Ext JS Library 1.1.1
22381  * Copyright(c) 2006-2007, Ext JS, LLC.
22382  *
22383  * Originally Released Under LGPL - original licence link has changed is not relivant.
22384  *
22385  * Fork - LGPL
22386  * <script type="text/javascript">
22387  */
22388
22389 /**
22390  * @class Roo.data.ScriptTagProxy
22391  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22392  * other than the originating domain of the running page.<br><br>
22393  * <p>
22394  * <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
22395  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22396  * <p>
22397  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22398  * source code that is used as the source inside a &lt;script> tag.<br><br>
22399  * <p>
22400  * In order for the browser to process the returned data, the server must wrap the data object
22401  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22402  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22403  * depending on whether the callback name was passed:
22404  * <p>
22405  * <pre><code>
22406 boolean scriptTag = false;
22407 String cb = request.getParameter("callback");
22408 if (cb != null) {
22409     scriptTag = true;
22410     response.setContentType("text/javascript");
22411 } else {
22412     response.setContentType("application/x-json");
22413 }
22414 Writer out = response.getWriter();
22415 if (scriptTag) {
22416     out.write(cb + "(");
22417 }
22418 out.print(dataBlock.toJsonString());
22419 if (scriptTag) {
22420     out.write(");");
22421 }
22422 </pre></code>
22423  *
22424  * @constructor
22425  * @param {Object} config A configuration object.
22426  */
22427 Roo.data.ScriptTagProxy = function(config){
22428     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22429     Roo.apply(this, config);
22430     this.head = document.getElementsByTagName("head")[0];
22431 };
22432
22433 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22434
22435 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22436     /**
22437      * @cfg {String} url The URL from which to request the data object.
22438      */
22439     /**
22440      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22441      */
22442     timeout : 30000,
22443     /**
22444      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22445      * the server the name of the callback function set up by the load call to process the returned data object.
22446      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22447      * javascript output which calls this named function passing the data object as its only parameter.
22448      */
22449     callbackParam : "callback",
22450     /**
22451      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22452      * name to the request.
22453      */
22454     nocache : true,
22455
22456     /**
22457      * Load data from the configured URL, read the data object into
22458      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22459      * process that block using the passed callback.
22460      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22461      * for the request to the remote server.
22462      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22463      * object into a block of Roo.data.Records.
22464      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22465      * The function must be passed <ul>
22466      * <li>The Record block object</li>
22467      * <li>The "arg" argument from the load function</li>
22468      * <li>A boolean success indicator</li>
22469      * </ul>
22470      * @param {Object} scope The scope in which to call the callback
22471      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22472      */
22473     load : function(params, reader, callback, scope, arg){
22474         if(this.fireEvent("beforeload", this, params) !== false){
22475
22476             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22477
22478             var url = this.url;
22479             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22480             if(this.nocache){
22481                 url += "&_dc=" + (new Date().getTime());
22482             }
22483             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22484             var trans = {
22485                 id : transId,
22486                 cb : "stcCallback"+transId,
22487                 scriptId : "stcScript"+transId,
22488                 params : params,
22489                 arg : arg,
22490                 url : url,
22491                 callback : callback,
22492                 scope : scope,
22493                 reader : reader
22494             };
22495             var conn = this;
22496
22497             window[trans.cb] = function(o){
22498                 conn.handleResponse(o, trans);
22499             };
22500
22501             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22502
22503             if(this.autoAbort !== false){
22504                 this.abort();
22505             }
22506
22507             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22508
22509             var script = document.createElement("script");
22510             script.setAttribute("src", url);
22511             script.setAttribute("type", "text/javascript");
22512             script.setAttribute("id", trans.scriptId);
22513             this.head.appendChild(script);
22514
22515             this.trans = trans;
22516         }else{
22517             callback.call(scope||this, null, arg, false);
22518         }
22519     },
22520
22521     // private
22522     isLoading : function(){
22523         return this.trans ? true : false;
22524     },
22525
22526     /**
22527      * Abort the current server request.
22528      */
22529     abort : function(){
22530         if(this.isLoading()){
22531             this.destroyTrans(this.trans);
22532         }
22533     },
22534
22535     // private
22536     destroyTrans : function(trans, isLoaded){
22537         this.head.removeChild(document.getElementById(trans.scriptId));
22538         clearTimeout(trans.timeoutId);
22539         if(isLoaded){
22540             window[trans.cb] = undefined;
22541             try{
22542                 delete window[trans.cb];
22543             }catch(e){}
22544         }else{
22545             // if hasn't been loaded, wait for load to remove it to prevent script error
22546             window[trans.cb] = function(){
22547                 window[trans.cb] = undefined;
22548                 try{
22549                     delete window[trans.cb];
22550                 }catch(e){}
22551             };
22552         }
22553     },
22554
22555     // private
22556     handleResponse : function(o, trans){
22557         this.trans = false;
22558         this.destroyTrans(trans, true);
22559         var result;
22560         try {
22561             result = trans.reader.readRecords(o);
22562         }catch(e){
22563             this.fireEvent("loadexception", this, o, trans.arg, e);
22564             trans.callback.call(trans.scope||window, null, trans.arg, false);
22565             return;
22566         }
22567         this.fireEvent("load", this, o, trans.arg);
22568         trans.callback.call(trans.scope||window, result, trans.arg, true);
22569     },
22570
22571     // private
22572     handleFailure : function(trans){
22573         this.trans = false;
22574         this.destroyTrans(trans, false);
22575         this.fireEvent("loadexception", this, null, trans.arg);
22576         trans.callback.call(trans.scope||window, null, trans.arg, false);
22577     }
22578 });/*
22579  * Based on:
22580  * Ext JS Library 1.1.1
22581  * Copyright(c) 2006-2007, Ext JS, LLC.
22582  *
22583  * Originally Released Under LGPL - original licence link has changed is not relivant.
22584  *
22585  * Fork - LGPL
22586  * <script type="text/javascript">
22587  */
22588
22589 /**
22590  * @class Roo.data.JsonReader
22591  * @extends Roo.data.DataReader
22592  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22593  * based on mappings in a provided Roo.data.Record constructor.
22594  * 
22595  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22596  * in the reply previously. 
22597  * 
22598  * <p>
22599  * Example code:
22600  * <pre><code>
22601 var RecordDef = Roo.data.Record.create([
22602     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22603     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22604 ]);
22605 var myReader = new Roo.data.JsonReader({
22606     totalProperty: "results",    // The property which contains the total dataset size (optional)
22607     root: "rows",                // The property which contains an Array of row objects
22608     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22609 }, RecordDef);
22610 </code></pre>
22611  * <p>
22612  * This would consume a JSON file like this:
22613  * <pre><code>
22614 { 'results': 2, 'rows': [
22615     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22616     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22617 }
22618 </code></pre>
22619  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22620  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22621  * paged from the remote server.
22622  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22623  * @cfg {String} root name of the property which contains the Array of row objects.
22624  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22625  * @cfg {Array} fields Array of field definition objects
22626  * @constructor
22627  * Create a new JsonReader
22628  * @param {Object} meta Metadata configuration options
22629  * @param {Object} recordType Either an Array of field definition objects,
22630  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22631  */
22632 Roo.data.JsonReader = function(meta, recordType){
22633     
22634     meta = meta || {};
22635     // set some defaults:
22636     Roo.applyIf(meta, {
22637         totalProperty: 'total',
22638         successProperty : 'success',
22639         root : 'data',
22640         id : 'id'
22641     });
22642     
22643     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22644 };
22645 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22646     
22647     /**
22648      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22649      * Used by Store query builder to append _requestMeta to params.
22650      * 
22651      */
22652     metaFromRemote : false,
22653     /**
22654      * This method is only used by a DataProxy which has retrieved data from a remote server.
22655      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22656      * @return {Object} data A data block which is used by an Roo.data.Store object as
22657      * a cache of Roo.data.Records.
22658      */
22659     read : function(response){
22660         var json = response.responseText;
22661        
22662         var o = /* eval:var:o */ eval("("+json+")");
22663         if(!o) {
22664             throw {message: "JsonReader.read: Json object not found"};
22665         }
22666         
22667         if(o.metaData){
22668             
22669             delete this.ef;
22670             this.metaFromRemote = true;
22671             this.meta = o.metaData;
22672             this.recordType = Roo.data.Record.create(o.metaData.fields);
22673             this.onMetaChange(this.meta, this.recordType, o);
22674         }
22675         return this.readRecords(o);
22676     },
22677
22678     // private function a store will implement
22679     onMetaChange : function(meta, recordType, o){
22680
22681     },
22682
22683     /**
22684          * @ignore
22685          */
22686     simpleAccess: function(obj, subsc) {
22687         return obj[subsc];
22688     },
22689
22690         /**
22691          * @ignore
22692          */
22693     getJsonAccessor: function(){
22694         var re = /[\[\.]/;
22695         return function(expr) {
22696             try {
22697                 return(re.test(expr))
22698                     ? new Function("obj", "return obj." + expr)
22699                     : function(obj){
22700                         return obj[expr];
22701                     };
22702             } catch(e){}
22703             return Roo.emptyFn;
22704         };
22705     }(),
22706
22707     /**
22708      * Create a data block containing Roo.data.Records from an XML document.
22709      * @param {Object} o An object which contains an Array of row objects in the property specified
22710      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22711      * which contains the total size of the dataset.
22712      * @return {Object} data A data block which is used by an Roo.data.Store object as
22713      * a cache of Roo.data.Records.
22714      */
22715     readRecords : function(o){
22716         /**
22717          * After any data loads, the raw JSON data is available for further custom processing.
22718          * @type Object
22719          */
22720         this.o = o;
22721         var s = this.meta, Record = this.recordType,
22722             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
22723
22724 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22725         if (!this.ef) {
22726             if(s.totalProperty) {
22727                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22728                 }
22729                 if(s.successProperty) {
22730                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22731                 }
22732                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22733                 if (s.id) {
22734                         var g = this.getJsonAccessor(s.id);
22735                         this.getId = function(rec) {
22736                                 var r = g(rec);  
22737                                 return (r === undefined || r === "") ? null : r;
22738                         };
22739                 } else {
22740                         this.getId = function(){return null;};
22741                 }
22742             this.ef = [];
22743             for(var jj = 0; jj < fl; jj++){
22744                 f = fi[jj];
22745                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22746                 this.ef[jj] = this.getJsonAccessor(map);
22747             }
22748         }
22749
22750         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22751         if(s.totalProperty){
22752             var vt = parseInt(this.getTotal(o), 10);
22753             if(!isNaN(vt)){
22754                 totalRecords = vt;
22755             }
22756         }
22757         if(s.successProperty){
22758             var vs = this.getSuccess(o);
22759             if(vs === false || vs === 'false'){
22760                 success = false;
22761             }
22762         }
22763         var records = [];
22764         for(var i = 0; i < c; i++){
22765                 var n = root[i];
22766             var values = {};
22767             var id = this.getId(n);
22768             for(var j = 0; j < fl; j++){
22769                 f = fi[j];
22770             var v = this.ef[j](n);
22771             if (!f.convert) {
22772                 Roo.log('missing convert for ' + f.name);
22773                 Roo.log(f);
22774                 continue;
22775             }
22776             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22777             }
22778             var record = new Record(values, id);
22779             record.json = n;
22780             records[i] = record;
22781         }
22782         return {
22783             raw : o,
22784             success : success,
22785             records : records,
22786             totalRecords : totalRecords
22787         };
22788     }
22789 });/*
22790  * Based on:
22791  * Ext JS Library 1.1.1
22792  * Copyright(c) 2006-2007, Ext JS, LLC.
22793  *
22794  * Originally Released Under LGPL - original licence link has changed is not relivant.
22795  *
22796  * Fork - LGPL
22797  * <script type="text/javascript">
22798  */
22799
22800 /**
22801  * @class Roo.data.XmlReader
22802  * @extends Roo.data.DataReader
22803  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22804  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22805  * <p>
22806  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22807  * header in the HTTP response must be set to "text/xml".</em>
22808  * <p>
22809  * Example code:
22810  * <pre><code>
22811 var RecordDef = Roo.data.Record.create([
22812    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22813    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22814 ]);
22815 var myReader = new Roo.data.XmlReader({
22816    totalRecords: "results", // The element which contains the total dataset size (optional)
22817    record: "row",           // The repeated element which contains row information
22818    id: "id"                 // The element within the row that provides an ID for the record (optional)
22819 }, RecordDef);
22820 </code></pre>
22821  * <p>
22822  * This would consume an XML file like this:
22823  * <pre><code>
22824 &lt;?xml?>
22825 &lt;dataset>
22826  &lt;results>2&lt;/results>
22827  &lt;row>
22828    &lt;id>1&lt;/id>
22829    &lt;name>Bill&lt;/name>
22830    &lt;occupation>Gardener&lt;/occupation>
22831  &lt;/row>
22832  &lt;row>
22833    &lt;id>2&lt;/id>
22834    &lt;name>Ben&lt;/name>
22835    &lt;occupation>Horticulturalist&lt;/occupation>
22836  &lt;/row>
22837 &lt;/dataset>
22838 </code></pre>
22839  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22840  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22841  * paged from the remote server.
22842  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22843  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22844  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22845  * a record identifier value.
22846  * @constructor
22847  * Create a new XmlReader
22848  * @param {Object} meta Metadata configuration options
22849  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22850  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22851  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22852  */
22853 Roo.data.XmlReader = function(meta, recordType){
22854     meta = meta || {};
22855     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22856 };
22857 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22858     /**
22859      * This method is only used by a DataProxy which has retrieved data from a remote server.
22860          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22861          * to contain a method called 'responseXML' that returns an XML document object.
22862      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22863      * a cache of Roo.data.Records.
22864      */
22865     read : function(response){
22866         var doc = response.responseXML;
22867         if(!doc) {
22868             throw {message: "XmlReader.read: XML Document not available"};
22869         }
22870         return this.readRecords(doc);
22871     },
22872
22873     /**
22874      * Create a data block containing Roo.data.Records from an XML document.
22875          * @param {Object} doc A parsed XML document.
22876      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22877      * a cache of Roo.data.Records.
22878      */
22879     readRecords : function(doc){
22880         /**
22881          * After any data loads/reads, the raw XML Document is available for further custom processing.
22882          * @type XMLDocument
22883          */
22884         this.xmlData = doc;
22885         var root = doc.documentElement || doc;
22886         var q = Roo.DomQuery;
22887         var recordType = this.recordType, fields = recordType.prototype.fields;
22888         var sid = this.meta.id;
22889         var totalRecords = 0, success = true;
22890         if(this.meta.totalRecords){
22891             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22892         }
22893         
22894         if(this.meta.success){
22895             var sv = q.selectValue(this.meta.success, root, true);
22896             success = sv !== false && sv !== 'false';
22897         }
22898         var records = [];
22899         var ns = q.select(this.meta.record, root);
22900         for(var i = 0, len = ns.length; i < len; i++) {
22901                 var n = ns[i];
22902                 var values = {};
22903                 var id = sid ? q.selectValue(sid, n) : undefined;
22904                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22905                     var f = fields.items[j];
22906                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22907                     v = f.convert(v);
22908                     values[f.name] = v;
22909                 }
22910                 var record = new recordType(values, id);
22911                 record.node = n;
22912                 records[records.length] = record;
22913             }
22914
22915             return {
22916                 success : success,
22917                 records : records,
22918                 totalRecords : totalRecords || records.length
22919             };
22920     }
22921 });/*
22922  * Based on:
22923  * Ext JS Library 1.1.1
22924  * Copyright(c) 2006-2007, Ext JS, LLC.
22925  *
22926  * Originally Released Under LGPL - original licence link has changed is not relivant.
22927  *
22928  * Fork - LGPL
22929  * <script type="text/javascript">
22930  */
22931
22932 /**
22933  * @class Roo.data.ArrayReader
22934  * @extends Roo.data.DataReader
22935  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22936  * Each element of that Array represents a row of data fields. The
22937  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22938  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22939  * <p>
22940  * Example code:.
22941  * <pre><code>
22942 var RecordDef = Roo.data.Record.create([
22943     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22944     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22945 ]);
22946 var myReader = new Roo.data.ArrayReader({
22947     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22948 }, RecordDef);
22949 </code></pre>
22950  * <p>
22951  * This would consume an Array like this:
22952  * <pre><code>
22953 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22954   </code></pre>
22955  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22956  * @constructor
22957  * Create a new JsonReader
22958  * @param {Object} meta Metadata configuration options.
22959  * @param {Object} recordType Either an Array of field definition objects
22960  * as specified to {@link Roo.data.Record#create},
22961  * or an {@link Roo.data.Record} object
22962  * created using {@link Roo.data.Record#create}.
22963  */
22964 Roo.data.ArrayReader = function(meta, recordType){
22965     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22966 };
22967
22968 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22969     /**
22970      * Create a data block containing Roo.data.Records from an XML document.
22971      * @param {Object} o An Array of row objects which represents the dataset.
22972      * @return {Object} data A data block which is used by an Roo.data.Store object as
22973      * a cache of Roo.data.Records.
22974      */
22975     readRecords : function(o){
22976         var sid = this.meta ? this.meta.id : null;
22977         var recordType = this.recordType, fields = recordType.prototype.fields;
22978         var records = [];
22979         var root = o;
22980             for(var i = 0; i < root.length; i++){
22981                     var n = root[i];
22982                 var values = {};
22983                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22984                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22985                 var f = fields.items[j];
22986                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22987                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22988                 v = f.convert(v);
22989                 values[f.name] = v;
22990             }
22991                 var record = new recordType(values, id);
22992                 record.json = n;
22993                 records[records.length] = record;
22994             }
22995             return {
22996                 records : records,
22997                 totalRecords : records.length
22998             };
22999     }
23000 });/*
23001  * Based on:
23002  * Ext JS Library 1.1.1
23003  * Copyright(c) 2006-2007, Ext JS, LLC.
23004  *
23005  * Originally Released Under LGPL - original licence link has changed is not relivant.
23006  *
23007  * Fork - LGPL
23008  * <script type="text/javascript">
23009  */
23010
23011
23012 /**
23013  * @class Roo.data.Tree
23014  * @extends Roo.util.Observable
23015  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
23016  * in the tree have most standard DOM functionality.
23017  * @constructor
23018  * @param {Node} root (optional) The root node
23019  */
23020 Roo.data.Tree = function(root){
23021    this.nodeHash = {};
23022    /**
23023     * The root node for this tree
23024     * @type Node
23025     */
23026    this.root = null;
23027    if(root){
23028        this.setRootNode(root);
23029    }
23030    this.addEvents({
23031        /**
23032         * @event append
23033         * Fires when a new child node is appended to a node in this tree.
23034         * @param {Tree} tree The owner tree
23035         * @param {Node} parent The parent node
23036         * @param {Node} node The newly appended node
23037         * @param {Number} index The index of the newly appended node
23038         */
23039        "append" : true,
23040        /**
23041         * @event remove
23042         * Fires when a child node is removed from a node in this tree.
23043         * @param {Tree} tree The owner tree
23044         * @param {Node} parent The parent node
23045         * @param {Node} node The child node removed
23046         */
23047        "remove" : true,
23048        /**
23049         * @event move
23050         * Fires when a node is moved to a new location in the tree
23051         * @param {Tree} tree The owner tree
23052         * @param {Node} node The node moved
23053         * @param {Node} oldParent The old parent of this node
23054         * @param {Node} newParent The new parent of this node
23055         * @param {Number} index The index it was moved to
23056         */
23057        "move" : true,
23058        /**
23059         * @event insert
23060         * Fires when a new child node is inserted in a node in this tree.
23061         * @param {Tree} tree The owner tree
23062         * @param {Node} parent The parent node
23063         * @param {Node} node The child node inserted
23064         * @param {Node} refNode The child node the node was inserted before
23065         */
23066        "insert" : true,
23067        /**
23068         * @event beforeappend
23069         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
23070         * @param {Tree} tree The owner tree
23071         * @param {Node} parent The parent node
23072         * @param {Node} node The child node to be appended
23073         */
23074        "beforeappend" : true,
23075        /**
23076         * @event beforeremove
23077         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
23078         * @param {Tree} tree The owner tree
23079         * @param {Node} parent The parent node
23080         * @param {Node} node The child node to be removed
23081         */
23082        "beforeremove" : true,
23083        /**
23084         * @event beforemove
23085         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
23086         * @param {Tree} tree The owner tree
23087         * @param {Node} node The node being moved
23088         * @param {Node} oldParent The parent of the node
23089         * @param {Node} newParent The new parent the node is moving to
23090         * @param {Number} index The index it is being moved to
23091         */
23092        "beforemove" : true,
23093        /**
23094         * @event beforeinsert
23095         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
23096         * @param {Tree} tree The owner tree
23097         * @param {Node} parent The parent node
23098         * @param {Node} node The child node to be inserted
23099         * @param {Node} refNode The child node the node is being inserted before
23100         */
23101        "beforeinsert" : true
23102    });
23103
23104     Roo.data.Tree.superclass.constructor.call(this);
23105 };
23106
23107 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
23108     pathSeparator: "/",
23109
23110     proxyNodeEvent : function(){
23111         return this.fireEvent.apply(this, arguments);
23112     },
23113
23114     /**
23115      * Returns the root node for this tree.
23116      * @return {Node}
23117      */
23118     getRootNode : function(){
23119         return this.root;
23120     },
23121
23122     /**
23123      * Sets the root node for this tree.
23124      * @param {Node} node
23125      * @return {Node}
23126      */
23127     setRootNode : function(node){
23128         this.root = node;
23129         node.ownerTree = this;
23130         node.isRoot = true;
23131         this.registerNode(node);
23132         return node;
23133     },
23134
23135     /**
23136      * Gets a node in this tree by its id.
23137      * @param {String} id
23138      * @return {Node}
23139      */
23140     getNodeById : function(id){
23141         return this.nodeHash[id];
23142     },
23143
23144     registerNode : function(node){
23145         this.nodeHash[node.id] = node;
23146     },
23147
23148     unregisterNode : function(node){
23149         delete this.nodeHash[node.id];
23150     },
23151
23152     toString : function(){
23153         return "[Tree"+(this.id?" "+this.id:"")+"]";
23154     }
23155 });
23156
23157 /**
23158  * @class Roo.data.Node
23159  * @extends Roo.util.Observable
23160  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23161  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23162  * @constructor
23163  * @param {Object} attributes The attributes/config for the node
23164  */
23165 Roo.data.Node = function(attributes){
23166     /**
23167      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23168      * @type {Object}
23169      */
23170     this.attributes = attributes || {};
23171     this.leaf = this.attributes.leaf;
23172     /**
23173      * The node id. @type String
23174      */
23175     this.id = this.attributes.id;
23176     if(!this.id){
23177         this.id = Roo.id(null, "ynode-");
23178         this.attributes.id = this.id;
23179     }
23180      
23181     
23182     /**
23183      * All child nodes of this node. @type Array
23184      */
23185     this.childNodes = [];
23186     if(!this.childNodes.indexOf){ // indexOf is a must
23187         this.childNodes.indexOf = function(o){
23188             for(var i = 0, len = this.length; i < len; i++){
23189                 if(this[i] == o) {
23190                     return i;
23191                 }
23192             }
23193             return -1;
23194         };
23195     }
23196     /**
23197      * The parent node for this node. @type Node
23198      */
23199     this.parentNode = null;
23200     /**
23201      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23202      */
23203     this.firstChild = null;
23204     /**
23205      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23206      */
23207     this.lastChild = null;
23208     /**
23209      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23210      */
23211     this.previousSibling = null;
23212     /**
23213      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23214      */
23215     this.nextSibling = null;
23216
23217     this.addEvents({
23218        /**
23219         * @event append
23220         * Fires when a new child node is appended
23221         * @param {Tree} tree The owner tree
23222         * @param {Node} this This node
23223         * @param {Node} node The newly appended node
23224         * @param {Number} index The index of the newly appended node
23225         */
23226        "append" : true,
23227        /**
23228         * @event remove
23229         * Fires when a child node is removed
23230         * @param {Tree} tree The owner tree
23231         * @param {Node} this This node
23232         * @param {Node} node The removed node
23233         */
23234        "remove" : true,
23235        /**
23236         * @event move
23237         * Fires when this node is moved to a new location in the tree
23238         * @param {Tree} tree The owner tree
23239         * @param {Node} this This node
23240         * @param {Node} oldParent The old parent of this node
23241         * @param {Node} newParent The new parent of this node
23242         * @param {Number} index The index it was moved to
23243         */
23244        "move" : true,
23245        /**
23246         * @event insert
23247         * Fires when a new child node is inserted.
23248         * @param {Tree} tree The owner tree
23249         * @param {Node} this This node
23250         * @param {Node} node The child node inserted
23251         * @param {Node} refNode The child node the node was inserted before
23252         */
23253        "insert" : true,
23254        /**
23255         * @event beforeappend
23256         * Fires before a new child is appended, return false to cancel the append.
23257         * @param {Tree} tree The owner tree
23258         * @param {Node} this This node
23259         * @param {Node} node The child node to be appended
23260         */
23261        "beforeappend" : true,
23262        /**
23263         * @event beforeremove
23264         * Fires before a child is removed, return false to cancel the remove.
23265         * @param {Tree} tree The owner tree
23266         * @param {Node} this This node
23267         * @param {Node} node The child node to be removed
23268         */
23269        "beforeremove" : true,
23270        /**
23271         * @event beforemove
23272         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23273         * @param {Tree} tree The owner tree
23274         * @param {Node} this This node
23275         * @param {Node} oldParent The parent of this node
23276         * @param {Node} newParent The new parent this node is moving to
23277         * @param {Number} index The index it is being moved to
23278         */
23279        "beforemove" : true,
23280        /**
23281         * @event beforeinsert
23282         * Fires before a new child is inserted, return false to cancel the insert.
23283         * @param {Tree} tree The owner tree
23284         * @param {Node} this This node
23285         * @param {Node} node The child node to be inserted
23286         * @param {Node} refNode The child node the node is being inserted before
23287         */
23288        "beforeinsert" : true
23289    });
23290     this.listeners = this.attributes.listeners;
23291     Roo.data.Node.superclass.constructor.call(this);
23292 };
23293
23294 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23295     fireEvent : function(evtName){
23296         // first do standard event for this node
23297         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23298             return false;
23299         }
23300         // then bubble it up to the tree if the event wasn't cancelled
23301         var ot = this.getOwnerTree();
23302         if(ot){
23303             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23304                 return false;
23305             }
23306         }
23307         return true;
23308     },
23309
23310     /**
23311      * Returns true if this node is a leaf
23312      * @return {Boolean}
23313      */
23314     isLeaf : function(){
23315         return this.leaf === true;
23316     },
23317
23318     // private
23319     setFirstChild : function(node){
23320         this.firstChild = node;
23321     },
23322
23323     //private
23324     setLastChild : function(node){
23325         this.lastChild = node;
23326     },
23327
23328
23329     /**
23330      * Returns true if this node is the last child of its parent
23331      * @return {Boolean}
23332      */
23333     isLast : function(){
23334        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23335     },
23336
23337     /**
23338      * Returns true if this node is the first child of its parent
23339      * @return {Boolean}
23340      */
23341     isFirst : function(){
23342        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23343     },
23344
23345     hasChildNodes : function(){
23346         return !this.isLeaf() && this.childNodes.length > 0;
23347     },
23348
23349     /**
23350      * Insert node(s) as the last child node of this node.
23351      * @param {Node/Array} node The node or Array of nodes to append
23352      * @return {Node} The appended node if single append, or null if an array was passed
23353      */
23354     appendChild : function(node){
23355         var multi = false;
23356         if(node instanceof Array){
23357             multi = node;
23358         }else if(arguments.length > 1){
23359             multi = arguments;
23360         }
23361         // if passed an array or multiple args do them one by one
23362         if(multi){
23363             for(var i = 0, len = multi.length; i < len; i++) {
23364                 this.appendChild(multi[i]);
23365             }
23366         }else{
23367             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23368                 return false;
23369             }
23370             var index = this.childNodes.length;
23371             var oldParent = node.parentNode;
23372             // it's a move, make sure we move it cleanly
23373             if(oldParent){
23374                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23375                     return false;
23376                 }
23377                 oldParent.removeChild(node);
23378             }
23379             index = this.childNodes.length;
23380             if(index == 0){
23381                 this.setFirstChild(node);
23382             }
23383             this.childNodes.push(node);
23384             node.parentNode = this;
23385             var ps = this.childNodes[index-1];
23386             if(ps){
23387                 node.previousSibling = ps;
23388                 ps.nextSibling = node;
23389             }else{
23390                 node.previousSibling = null;
23391             }
23392             node.nextSibling = null;
23393             this.setLastChild(node);
23394             node.setOwnerTree(this.getOwnerTree());
23395             this.fireEvent("append", this.ownerTree, this, node, index);
23396             if(oldParent){
23397                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23398             }
23399             return node;
23400         }
23401     },
23402
23403     /**
23404      * Removes a child node from this node.
23405      * @param {Node} node The node to remove
23406      * @return {Node} The removed node
23407      */
23408     removeChild : function(node){
23409         var index = this.childNodes.indexOf(node);
23410         if(index == -1){
23411             return false;
23412         }
23413         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23414             return false;
23415         }
23416
23417         // remove it from childNodes collection
23418         this.childNodes.splice(index, 1);
23419
23420         // update siblings
23421         if(node.previousSibling){
23422             node.previousSibling.nextSibling = node.nextSibling;
23423         }
23424         if(node.nextSibling){
23425             node.nextSibling.previousSibling = node.previousSibling;
23426         }
23427
23428         // update child refs
23429         if(this.firstChild == node){
23430             this.setFirstChild(node.nextSibling);
23431         }
23432         if(this.lastChild == node){
23433             this.setLastChild(node.previousSibling);
23434         }
23435
23436         node.setOwnerTree(null);
23437         // clear any references from the node
23438         node.parentNode = null;
23439         node.previousSibling = null;
23440         node.nextSibling = null;
23441         this.fireEvent("remove", this.ownerTree, this, node);
23442         return node;
23443     },
23444
23445     /**
23446      * Inserts the first node before the second node in this nodes childNodes collection.
23447      * @param {Node} node The node to insert
23448      * @param {Node} refNode The node to insert before (if null the node is appended)
23449      * @return {Node} The inserted node
23450      */
23451     insertBefore : function(node, refNode){
23452         if(!refNode){ // like standard Dom, refNode can be null for append
23453             return this.appendChild(node);
23454         }
23455         // nothing to do
23456         if(node == refNode){
23457             return false;
23458         }
23459
23460         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23461             return false;
23462         }
23463         var index = this.childNodes.indexOf(refNode);
23464         var oldParent = node.parentNode;
23465         var refIndex = index;
23466
23467         // when moving internally, indexes will change after remove
23468         if(oldParent == this && this.childNodes.indexOf(node) < index){
23469             refIndex--;
23470         }
23471
23472         // it's a move, make sure we move it cleanly
23473         if(oldParent){
23474             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23475                 return false;
23476             }
23477             oldParent.removeChild(node);
23478         }
23479         if(refIndex == 0){
23480             this.setFirstChild(node);
23481         }
23482         this.childNodes.splice(refIndex, 0, node);
23483         node.parentNode = this;
23484         var ps = this.childNodes[refIndex-1];
23485         if(ps){
23486             node.previousSibling = ps;
23487             ps.nextSibling = node;
23488         }else{
23489             node.previousSibling = null;
23490         }
23491         node.nextSibling = refNode;
23492         refNode.previousSibling = node;
23493         node.setOwnerTree(this.getOwnerTree());
23494         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23495         if(oldParent){
23496             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23497         }
23498         return node;
23499     },
23500
23501     /**
23502      * Returns the child node at the specified index.
23503      * @param {Number} index
23504      * @return {Node}
23505      */
23506     item : function(index){
23507         return this.childNodes[index];
23508     },
23509
23510     /**
23511      * Replaces one child node in this node with another.
23512      * @param {Node} newChild The replacement node
23513      * @param {Node} oldChild The node to replace
23514      * @return {Node} The replaced node
23515      */
23516     replaceChild : function(newChild, oldChild){
23517         this.insertBefore(newChild, oldChild);
23518         this.removeChild(oldChild);
23519         return oldChild;
23520     },
23521
23522     /**
23523      * Returns the index of a child node
23524      * @param {Node} node
23525      * @return {Number} The index of the node or -1 if it was not found
23526      */
23527     indexOf : function(child){
23528         return this.childNodes.indexOf(child);
23529     },
23530
23531     /**
23532      * Returns the tree this node is in.
23533      * @return {Tree}
23534      */
23535     getOwnerTree : function(){
23536         // if it doesn't have one, look for one
23537         if(!this.ownerTree){
23538             var p = this;
23539             while(p){
23540                 if(p.ownerTree){
23541                     this.ownerTree = p.ownerTree;
23542                     break;
23543                 }
23544                 p = p.parentNode;
23545             }
23546         }
23547         return this.ownerTree;
23548     },
23549
23550     /**
23551      * Returns depth of this node (the root node has a depth of 0)
23552      * @return {Number}
23553      */
23554     getDepth : function(){
23555         var depth = 0;
23556         var p = this;
23557         while(p.parentNode){
23558             ++depth;
23559             p = p.parentNode;
23560         }
23561         return depth;
23562     },
23563
23564     // private
23565     setOwnerTree : function(tree){
23566         // if it's move, we need to update everyone
23567         if(tree != this.ownerTree){
23568             if(this.ownerTree){
23569                 this.ownerTree.unregisterNode(this);
23570             }
23571             this.ownerTree = tree;
23572             var cs = this.childNodes;
23573             for(var i = 0, len = cs.length; i < len; i++) {
23574                 cs[i].setOwnerTree(tree);
23575             }
23576             if(tree){
23577                 tree.registerNode(this);
23578             }
23579         }
23580     },
23581
23582     /**
23583      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23584      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23585      * @return {String} The path
23586      */
23587     getPath : function(attr){
23588         attr = attr || "id";
23589         var p = this.parentNode;
23590         var b = [this.attributes[attr]];
23591         while(p){
23592             b.unshift(p.attributes[attr]);
23593             p = p.parentNode;
23594         }
23595         var sep = this.getOwnerTree().pathSeparator;
23596         return sep + b.join(sep);
23597     },
23598
23599     /**
23600      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23601      * function call will be the scope provided or the current node. The arguments to the function
23602      * will be the args provided or the current node. If the function returns false at any point,
23603      * the bubble is stopped.
23604      * @param {Function} fn The function to call
23605      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23606      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23607      */
23608     bubble : function(fn, scope, args){
23609         var p = this;
23610         while(p){
23611             if(fn.call(scope || p, args || p) === false){
23612                 break;
23613             }
23614             p = p.parentNode;
23615         }
23616     },
23617
23618     /**
23619      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23620      * function call will be the scope provided or the current node. The arguments to the function
23621      * will be the args provided or the current node. If the function returns false at any point,
23622      * the cascade is stopped on that branch.
23623      * @param {Function} fn The function to call
23624      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23625      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23626      */
23627     cascade : function(fn, scope, args){
23628         if(fn.call(scope || this, args || this) !== false){
23629             var cs = this.childNodes;
23630             for(var i = 0, len = cs.length; i < len; i++) {
23631                 cs[i].cascade(fn, scope, args);
23632             }
23633         }
23634     },
23635
23636     /**
23637      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23638      * function call will be the scope provided or the current node. The arguments to the function
23639      * will be the args provided or the current node. If the function returns false at any point,
23640      * the iteration stops.
23641      * @param {Function} fn The function to call
23642      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23643      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23644      */
23645     eachChild : function(fn, scope, args){
23646         var cs = this.childNodes;
23647         for(var i = 0, len = cs.length; i < len; i++) {
23648                 if(fn.call(scope || this, args || cs[i]) === false){
23649                     break;
23650                 }
23651         }
23652     },
23653
23654     /**
23655      * Finds the first child that has the attribute with the specified value.
23656      * @param {String} attribute The attribute name
23657      * @param {Mixed} value The value to search for
23658      * @return {Node} The found child or null if none was found
23659      */
23660     findChild : function(attribute, value){
23661         var cs = this.childNodes;
23662         for(var i = 0, len = cs.length; i < len; i++) {
23663                 if(cs[i].attributes[attribute] == value){
23664                     return cs[i];
23665                 }
23666         }
23667         return null;
23668     },
23669
23670     /**
23671      * Finds the first child by a custom function. The child matches if the function passed
23672      * returns true.
23673      * @param {Function} fn
23674      * @param {Object} scope (optional)
23675      * @return {Node} The found child or null if none was found
23676      */
23677     findChildBy : function(fn, scope){
23678         var cs = this.childNodes;
23679         for(var i = 0, len = cs.length; i < len; i++) {
23680                 if(fn.call(scope||cs[i], cs[i]) === true){
23681                     return cs[i];
23682                 }
23683         }
23684         return null;
23685     },
23686
23687     /**
23688      * Sorts this nodes children using the supplied sort function
23689      * @param {Function} fn
23690      * @param {Object} scope (optional)
23691      */
23692     sort : function(fn, scope){
23693         var cs = this.childNodes;
23694         var len = cs.length;
23695         if(len > 0){
23696             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23697             cs.sort(sortFn);
23698             for(var i = 0; i < len; i++){
23699                 var n = cs[i];
23700                 n.previousSibling = cs[i-1];
23701                 n.nextSibling = cs[i+1];
23702                 if(i == 0){
23703                     this.setFirstChild(n);
23704                 }
23705                 if(i == len-1){
23706                     this.setLastChild(n);
23707                 }
23708             }
23709         }
23710     },
23711
23712     /**
23713      * Returns true if this node is an ancestor (at any point) of the passed node.
23714      * @param {Node} node
23715      * @return {Boolean}
23716      */
23717     contains : function(node){
23718         return node.isAncestor(this);
23719     },
23720
23721     /**
23722      * Returns true if the passed node is an ancestor (at any point) of this node.
23723      * @param {Node} node
23724      * @return {Boolean}
23725      */
23726     isAncestor : function(node){
23727         var p = this.parentNode;
23728         while(p){
23729             if(p == node){
23730                 return true;
23731             }
23732             p = p.parentNode;
23733         }
23734         return false;
23735     },
23736
23737     toString : function(){
23738         return "[Node"+(this.id?" "+this.id:"")+"]";
23739     }
23740 });/*
23741  * Based on:
23742  * Ext JS Library 1.1.1
23743  * Copyright(c) 2006-2007, Ext JS, LLC.
23744  *
23745  * Originally Released Under LGPL - original licence link has changed is not relivant.
23746  *
23747  * Fork - LGPL
23748  * <script type="text/javascript">
23749  */
23750  (function(){ 
23751 /**
23752  * @class Roo.Layer
23753  * @extends Roo.Element
23754  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23755  * automatic maintaining of shadow/shim positions.
23756  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23757  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23758  * you can pass a string with a CSS class name. False turns off the shadow.
23759  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23760  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23761  * @cfg {String} cls CSS class to add to the element
23762  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23763  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23764  * @constructor
23765  * @param {Object} config An object with config options.
23766  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23767  */
23768
23769 Roo.Layer = function(config, existingEl){
23770     config = config || {};
23771     var dh = Roo.DomHelper;
23772     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23773     if(existingEl){
23774         this.dom = Roo.getDom(existingEl);
23775     }
23776     if(!this.dom){
23777         var o = config.dh || {tag: "div", cls: "x-layer"};
23778         this.dom = dh.append(pel, o);
23779     }
23780     if(config.cls){
23781         this.addClass(config.cls);
23782     }
23783     this.constrain = config.constrain !== false;
23784     this.visibilityMode = Roo.Element.VISIBILITY;
23785     if(config.id){
23786         this.id = this.dom.id = config.id;
23787     }else{
23788         this.id = Roo.id(this.dom);
23789     }
23790     this.zindex = config.zindex || this.getZIndex();
23791     this.position("absolute", this.zindex);
23792     if(config.shadow){
23793         this.shadowOffset = config.shadowOffset || 4;
23794         this.shadow = new Roo.Shadow({
23795             offset : this.shadowOffset,
23796             mode : config.shadow
23797         });
23798     }else{
23799         this.shadowOffset = 0;
23800     }
23801     this.useShim = config.shim !== false && Roo.useShims;
23802     this.useDisplay = config.useDisplay;
23803     this.hide();
23804 };
23805
23806 var supr = Roo.Element.prototype;
23807
23808 // shims are shared among layer to keep from having 100 iframes
23809 var shims = [];
23810
23811 Roo.extend(Roo.Layer, Roo.Element, {
23812
23813     getZIndex : function(){
23814         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23815     },
23816
23817     getShim : function(){
23818         if(!this.useShim){
23819             return null;
23820         }
23821         if(this.shim){
23822             return this.shim;
23823         }
23824         var shim = shims.shift();
23825         if(!shim){
23826             shim = this.createShim();
23827             shim.enableDisplayMode('block');
23828             shim.dom.style.display = 'none';
23829             shim.dom.style.visibility = 'visible';
23830         }
23831         var pn = this.dom.parentNode;
23832         if(shim.dom.parentNode != pn){
23833             pn.insertBefore(shim.dom, this.dom);
23834         }
23835         shim.setStyle('z-index', this.getZIndex()-2);
23836         this.shim = shim;
23837         return shim;
23838     },
23839
23840     hideShim : function(){
23841         if(this.shim){
23842             this.shim.setDisplayed(false);
23843             shims.push(this.shim);
23844             delete this.shim;
23845         }
23846     },
23847
23848     disableShadow : function(){
23849         if(this.shadow){
23850             this.shadowDisabled = true;
23851             this.shadow.hide();
23852             this.lastShadowOffset = this.shadowOffset;
23853             this.shadowOffset = 0;
23854         }
23855     },
23856
23857     enableShadow : function(show){
23858         if(this.shadow){
23859             this.shadowDisabled = false;
23860             this.shadowOffset = this.lastShadowOffset;
23861             delete this.lastShadowOffset;
23862             if(show){
23863                 this.sync(true);
23864             }
23865         }
23866     },
23867
23868     // private
23869     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23870     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23871     sync : function(doShow){
23872         var sw = this.shadow;
23873         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23874             var sh = this.getShim();
23875
23876             var w = this.getWidth(),
23877                 h = this.getHeight();
23878
23879             var l = this.getLeft(true),
23880                 t = this.getTop(true);
23881
23882             if(sw && !this.shadowDisabled){
23883                 if(doShow && !sw.isVisible()){
23884                     sw.show(this);
23885                 }else{
23886                     sw.realign(l, t, w, h);
23887                 }
23888                 if(sh){
23889                     if(doShow){
23890                        sh.show();
23891                     }
23892                     // fit the shim behind the shadow, so it is shimmed too
23893                     var a = sw.adjusts, s = sh.dom.style;
23894                     s.left = (Math.min(l, l+a.l))+"px";
23895                     s.top = (Math.min(t, t+a.t))+"px";
23896                     s.width = (w+a.w)+"px";
23897                     s.height = (h+a.h)+"px";
23898                 }
23899             }else if(sh){
23900                 if(doShow){
23901                    sh.show();
23902                 }
23903                 sh.setSize(w, h);
23904                 sh.setLeftTop(l, t);
23905             }
23906             
23907         }
23908     },
23909
23910     // private
23911     destroy : function(){
23912         this.hideShim();
23913         if(this.shadow){
23914             this.shadow.hide();
23915         }
23916         this.removeAllListeners();
23917         var pn = this.dom.parentNode;
23918         if(pn){
23919             pn.removeChild(this.dom);
23920         }
23921         Roo.Element.uncache(this.id);
23922     },
23923
23924     remove : function(){
23925         this.destroy();
23926     },
23927
23928     // private
23929     beginUpdate : function(){
23930         this.updating = true;
23931     },
23932
23933     // private
23934     endUpdate : function(){
23935         this.updating = false;
23936         this.sync(true);
23937     },
23938
23939     // private
23940     hideUnders : function(negOffset){
23941         if(this.shadow){
23942             this.shadow.hide();
23943         }
23944         this.hideShim();
23945     },
23946
23947     // private
23948     constrainXY : function(){
23949         if(this.constrain){
23950             var vw = Roo.lib.Dom.getViewWidth(),
23951                 vh = Roo.lib.Dom.getViewHeight();
23952             var s = Roo.get(document).getScroll();
23953
23954             var xy = this.getXY();
23955             var x = xy[0], y = xy[1];   
23956             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23957             // only move it if it needs it
23958             var moved = false;
23959             // first validate right/bottom
23960             if((x + w) > vw+s.left){
23961                 x = vw - w - this.shadowOffset;
23962                 moved = true;
23963             }
23964             if((y + h) > vh+s.top){
23965                 y = vh - h - this.shadowOffset;
23966                 moved = true;
23967             }
23968             // then make sure top/left isn't negative
23969             if(x < s.left){
23970                 x = s.left;
23971                 moved = true;
23972             }
23973             if(y < s.top){
23974                 y = s.top;
23975                 moved = true;
23976             }
23977             if(moved){
23978                 if(this.avoidY){
23979                     var ay = this.avoidY;
23980                     if(y <= ay && (y+h) >= ay){
23981                         y = ay-h-5;   
23982                     }
23983                 }
23984                 xy = [x, y];
23985                 this.storeXY(xy);
23986                 supr.setXY.call(this, xy);
23987                 this.sync();
23988             }
23989         }
23990     },
23991
23992     isVisible : function(){
23993         return this.visible;    
23994     },
23995
23996     // private
23997     showAction : function(){
23998         this.visible = true; // track visibility to prevent getStyle calls
23999         if(this.useDisplay === true){
24000             this.setDisplayed("");
24001         }else if(this.lastXY){
24002             supr.setXY.call(this, this.lastXY);
24003         }else if(this.lastLT){
24004             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
24005         }
24006     },
24007
24008     // private
24009     hideAction : function(){
24010         this.visible = false;
24011         if(this.useDisplay === true){
24012             this.setDisplayed(false);
24013         }else{
24014             this.setLeftTop(-10000,-10000);
24015         }
24016     },
24017
24018     // overridden Element method
24019     setVisible : function(v, a, d, c, e){
24020         if(v){
24021             this.showAction();
24022         }
24023         if(a && v){
24024             var cb = function(){
24025                 this.sync(true);
24026                 if(c){
24027                     c();
24028                 }
24029             }.createDelegate(this);
24030             supr.setVisible.call(this, true, true, d, cb, e);
24031         }else{
24032             if(!v){
24033                 this.hideUnders(true);
24034             }
24035             var cb = c;
24036             if(a){
24037                 cb = function(){
24038                     this.hideAction();
24039                     if(c){
24040                         c();
24041                     }
24042                 }.createDelegate(this);
24043             }
24044             supr.setVisible.call(this, v, a, d, cb, e);
24045             if(v){
24046                 this.sync(true);
24047             }else if(!a){
24048                 this.hideAction();
24049             }
24050         }
24051     },
24052
24053     storeXY : function(xy){
24054         delete this.lastLT;
24055         this.lastXY = xy;
24056     },
24057
24058     storeLeftTop : function(left, top){
24059         delete this.lastXY;
24060         this.lastLT = [left, top];
24061     },
24062
24063     // private
24064     beforeFx : function(){
24065         this.beforeAction();
24066         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
24067     },
24068
24069     // private
24070     afterFx : function(){
24071         Roo.Layer.superclass.afterFx.apply(this, arguments);
24072         this.sync(this.isVisible());
24073     },
24074
24075     // private
24076     beforeAction : function(){
24077         if(!this.updating && this.shadow){
24078             this.shadow.hide();
24079         }
24080     },
24081
24082     // overridden Element method
24083     setLeft : function(left){
24084         this.storeLeftTop(left, this.getTop(true));
24085         supr.setLeft.apply(this, arguments);
24086         this.sync();
24087     },
24088
24089     setTop : function(top){
24090         this.storeLeftTop(this.getLeft(true), top);
24091         supr.setTop.apply(this, arguments);
24092         this.sync();
24093     },
24094
24095     setLeftTop : function(left, top){
24096         this.storeLeftTop(left, top);
24097         supr.setLeftTop.apply(this, arguments);
24098         this.sync();
24099     },
24100
24101     setXY : function(xy, a, d, c, e){
24102         this.fixDisplay();
24103         this.beforeAction();
24104         this.storeXY(xy);
24105         var cb = this.createCB(c);
24106         supr.setXY.call(this, xy, a, d, cb, e);
24107         if(!a){
24108             cb();
24109         }
24110     },
24111
24112     // private
24113     createCB : function(c){
24114         var el = this;
24115         return function(){
24116             el.constrainXY();
24117             el.sync(true);
24118             if(c){
24119                 c();
24120             }
24121         };
24122     },
24123
24124     // overridden Element method
24125     setX : function(x, a, d, c, e){
24126         this.setXY([x, this.getY()], a, d, c, e);
24127     },
24128
24129     // overridden Element method
24130     setY : function(y, a, d, c, e){
24131         this.setXY([this.getX(), y], a, d, c, e);
24132     },
24133
24134     // overridden Element method
24135     setSize : function(w, h, a, d, c, e){
24136         this.beforeAction();
24137         var cb = this.createCB(c);
24138         supr.setSize.call(this, w, h, a, d, cb, e);
24139         if(!a){
24140             cb();
24141         }
24142     },
24143
24144     // overridden Element method
24145     setWidth : function(w, a, d, c, e){
24146         this.beforeAction();
24147         var cb = this.createCB(c);
24148         supr.setWidth.call(this, w, a, d, cb, e);
24149         if(!a){
24150             cb();
24151         }
24152     },
24153
24154     // overridden Element method
24155     setHeight : function(h, a, d, c, e){
24156         this.beforeAction();
24157         var cb = this.createCB(c);
24158         supr.setHeight.call(this, h, a, d, cb, e);
24159         if(!a){
24160             cb();
24161         }
24162     },
24163
24164     // overridden Element method
24165     setBounds : function(x, y, w, h, a, d, c, e){
24166         this.beforeAction();
24167         var cb = this.createCB(c);
24168         if(!a){
24169             this.storeXY([x, y]);
24170             supr.setXY.call(this, [x, y]);
24171             supr.setSize.call(this, w, h, a, d, cb, e);
24172             cb();
24173         }else{
24174             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24175         }
24176         return this;
24177     },
24178     
24179     /**
24180      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24181      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24182      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24183      * @param {Number} zindex The new z-index to set
24184      * @return {this} The Layer
24185      */
24186     setZIndex : function(zindex){
24187         this.zindex = zindex;
24188         this.setStyle("z-index", zindex + 2);
24189         if(this.shadow){
24190             this.shadow.setZIndex(zindex + 1);
24191         }
24192         if(this.shim){
24193             this.shim.setStyle("z-index", zindex);
24194         }
24195     }
24196 });
24197 })();/*
24198  * Based on:
24199  * Ext JS Library 1.1.1
24200  * Copyright(c) 2006-2007, Ext JS, LLC.
24201  *
24202  * Originally Released Under LGPL - original licence link has changed is not relivant.
24203  *
24204  * Fork - LGPL
24205  * <script type="text/javascript">
24206  */
24207
24208
24209 /**
24210  * @class Roo.Shadow
24211  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24212  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24213  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24214  * @constructor
24215  * Create a new Shadow
24216  * @param {Object} config The config object
24217  */
24218 Roo.Shadow = function(config){
24219     Roo.apply(this, config);
24220     if(typeof this.mode != "string"){
24221         this.mode = this.defaultMode;
24222     }
24223     var o = this.offset, a = {h: 0};
24224     var rad = Math.floor(this.offset/2);
24225     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24226         case "drop":
24227             a.w = 0;
24228             a.l = a.t = o;
24229             a.t -= 1;
24230             if(Roo.isIE){
24231                 a.l -= this.offset + rad;
24232                 a.t -= this.offset + rad;
24233                 a.w -= rad;
24234                 a.h -= rad;
24235                 a.t += 1;
24236             }
24237         break;
24238         case "sides":
24239             a.w = (o*2);
24240             a.l = -o;
24241             a.t = o-1;
24242             if(Roo.isIE){
24243                 a.l -= (this.offset - rad);
24244                 a.t -= this.offset + rad;
24245                 a.l += 1;
24246                 a.w -= (this.offset - rad)*2;
24247                 a.w -= rad + 1;
24248                 a.h -= 1;
24249             }
24250         break;
24251         case "frame":
24252             a.w = a.h = (o*2);
24253             a.l = a.t = -o;
24254             a.t += 1;
24255             a.h -= 2;
24256             if(Roo.isIE){
24257                 a.l -= (this.offset - rad);
24258                 a.t -= (this.offset - rad);
24259                 a.l += 1;
24260                 a.w -= (this.offset + rad + 1);
24261                 a.h -= (this.offset + rad);
24262                 a.h += 1;
24263             }
24264         break;
24265     };
24266
24267     this.adjusts = a;
24268 };
24269
24270 Roo.Shadow.prototype = {
24271     /**
24272      * @cfg {String} mode
24273      * The shadow display mode.  Supports the following options:<br />
24274      * sides: Shadow displays on both sides and bottom only<br />
24275      * frame: Shadow displays equally on all four sides<br />
24276      * drop: Traditional bottom-right drop shadow (default)
24277      */
24278     /**
24279      * @cfg {String} offset
24280      * The number of pixels to offset the shadow from the element (defaults to 4)
24281      */
24282     offset: 4,
24283
24284     // private
24285     defaultMode: "drop",
24286
24287     /**
24288      * Displays the shadow under the target element
24289      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24290      */
24291     show : function(target){
24292         target = Roo.get(target);
24293         if(!this.el){
24294             this.el = Roo.Shadow.Pool.pull();
24295             if(this.el.dom.nextSibling != target.dom){
24296                 this.el.insertBefore(target);
24297             }
24298         }
24299         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24300         if(Roo.isIE){
24301             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24302         }
24303         this.realign(
24304             target.getLeft(true),
24305             target.getTop(true),
24306             target.getWidth(),
24307             target.getHeight()
24308         );
24309         this.el.dom.style.display = "block";
24310     },
24311
24312     /**
24313      * Returns true if the shadow is visible, else false
24314      */
24315     isVisible : function(){
24316         return this.el ? true : false;  
24317     },
24318
24319     /**
24320      * Direct alignment when values are already available. Show must be called at least once before
24321      * calling this method to ensure it is initialized.
24322      * @param {Number} left The target element left position
24323      * @param {Number} top The target element top position
24324      * @param {Number} width The target element width
24325      * @param {Number} height The target element height
24326      */
24327     realign : function(l, t, w, h){
24328         if(!this.el){
24329             return;
24330         }
24331         var a = this.adjusts, d = this.el.dom, s = d.style;
24332         var iea = 0;
24333         s.left = (l+a.l)+"px";
24334         s.top = (t+a.t)+"px";
24335         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24336  
24337         if(s.width != sws || s.height != shs){
24338             s.width = sws;
24339             s.height = shs;
24340             if(!Roo.isIE){
24341                 var cn = d.childNodes;
24342                 var sww = Math.max(0, (sw-12))+"px";
24343                 cn[0].childNodes[1].style.width = sww;
24344                 cn[1].childNodes[1].style.width = sww;
24345                 cn[2].childNodes[1].style.width = sww;
24346                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24347             }
24348         }
24349     },
24350
24351     /**
24352      * Hides this shadow
24353      */
24354     hide : function(){
24355         if(this.el){
24356             this.el.dom.style.display = "none";
24357             Roo.Shadow.Pool.push(this.el);
24358             delete this.el;
24359         }
24360     },
24361
24362     /**
24363      * Adjust the z-index of this shadow
24364      * @param {Number} zindex The new z-index
24365      */
24366     setZIndex : function(z){
24367         this.zIndex = z;
24368         if(this.el){
24369             this.el.setStyle("z-index", z);
24370         }
24371     }
24372 };
24373
24374 // Private utility class that manages the internal Shadow cache
24375 Roo.Shadow.Pool = function(){
24376     var p = [];
24377     var markup = Roo.isIE ?
24378                  '<div class="x-ie-shadow"></div>' :
24379                  '<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>';
24380     return {
24381         pull : function(){
24382             var sh = p.shift();
24383             if(!sh){
24384                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24385                 sh.autoBoxAdjust = false;
24386             }
24387             return sh;
24388         },
24389
24390         push : function(sh){
24391             p.push(sh);
24392         }
24393     };
24394 }();/*
24395  * Based on:
24396  * Ext JS Library 1.1.1
24397  * Copyright(c) 2006-2007, Ext JS, LLC.
24398  *
24399  * Originally Released Under LGPL - original licence link has changed is not relivant.
24400  *
24401  * Fork - LGPL
24402  * <script type="text/javascript">
24403  */
24404
24405
24406 /**
24407  * @class Roo.SplitBar
24408  * @extends Roo.util.Observable
24409  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24410  * <br><br>
24411  * Usage:
24412  * <pre><code>
24413 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24414                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24415 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24416 split.minSize = 100;
24417 split.maxSize = 600;
24418 split.animate = true;
24419 split.on('moved', splitterMoved);
24420 </code></pre>
24421  * @constructor
24422  * Create a new SplitBar
24423  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24424  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24425  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24426  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24427                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24428                         position of the SplitBar).
24429  */
24430 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24431     
24432     /** @private */
24433     this.el = Roo.get(dragElement, true);
24434     this.el.dom.unselectable = "on";
24435     /** @private */
24436     this.resizingEl = Roo.get(resizingElement, true);
24437
24438     /**
24439      * @private
24440      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24441      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24442      * @type Number
24443      */
24444     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24445     
24446     /**
24447      * The minimum size of the resizing element. (Defaults to 0)
24448      * @type Number
24449      */
24450     this.minSize = 0;
24451     
24452     /**
24453      * The maximum size of the resizing element. (Defaults to 2000)
24454      * @type Number
24455      */
24456     this.maxSize = 2000;
24457     
24458     /**
24459      * Whether to animate the transition to the new size
24460      * @type Boolean
24461      */
24462     this.animate = false;
24463     
24464     /**
24465      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24466      * @type Boolean
24467      */
24468     this.useShim = false;
24469     
24470     /** @private */
24471     this.shim = null;
24472     
24473     if(!existingProxy){
24474         /** @private */
24475         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24476     }else{
24477         this.proxy = Roo.get(existingProxy).dom;
24478     }
24479     /** @private */
24480     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24481     
24482     /** @private */
24483     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24484     
24485     /** @private */
24486     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24487     
24488     /** @private */
24489     this.dragSpecs = {};
24490     
24491     /**
24492      * @private The adapter to use to positon and resize elements
24493      */
24494     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24495     this.adapter.init(this);
24496     
24497     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24498         /** @private */
24499         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24500         this.el.addClass("x-splitbar-h");
24501     }else{
24502         /** @private */
24503         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24504         this.el.addClass("x-splitbar-v");
24505     }
24506     
24507     this.addEvents({
24508         /**
24509          * @event resize
24510          * Fires when the splitter is moved (alias for {@link #event-moved})
24511          * @param {Roo.SplitBar} this
24512          * @param {Number} newSize the new width or height
24513          */
24514         "resize" : true,
24515         /**
24516          * @event moved
24517          * Fires when the splitter is moved
24518          * @param {Roo.SplitBar} this
24519          * @param {Number} newSize the new width or height
24520          */
24521         "moved" : true,
24522         /**
24523          * @event beforeresize
24524          * Fires before the splitter is dragged
24525          * @param {Roo.SplitBar} this
24526          */
24527         "beforeresize" : true,
24528
24529         "beforeapply" : true
24530     });
24531
24532     Roo.util.Observable.call(this);
24533 };
24534
24535 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24536     onStartProxyDrag : function(x, y){
24537         this.fireEvent("beforeresize", this);
24538         if(!this.overlay){
24539             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24540             o.unselectable();
24541             o.enableDisplayMode("block");
24542             // all splitbars share the same overlay
24543             Roo.SplitBar.prototype.overlay = o;
24544         }
24545         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24546         this.overlay.show();
24547         Roo.get(this.proxy).setDisplayed("block");
24548         var size = this.adapter.getElementSize(this);
24549         this.activeMinSize = this.getMinimumSize();;
24550         this.activeMaxSize = this.getMaximumSize();;
24551         var c1 = size - this.activeMinSize;
24552         var c2 = Math.max(this.activeMaxSize - size, 0);
24553         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24554             this.dd.resetConstraints();
24555             this.dd.setXConstraint(
24556                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24557                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24558             );
24559             this.dd.setYConstraint(0, 0);
24560         }else{
24561             this.dd.resetConstraints();
24562             this.dd.setXConstraint(0, 0);
24563             this.dd.setYConstraint(
24564                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24565                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24566             );
24567          }
24568         this.dragSpecs.startSize = size;
24569         this.dragSpecs.startPoint = [x, y];
24570         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24571     },
24572     
24573     /** 
24574      * @private Called after the drag operation by the DDProxy
24575      */
24576     onEndProxyDrag : function(e){
24577         Roo.get(this.proxy).setDisplayed(false);
24578         var endPoint = Roo.lib.Event.getXY(e);
24579         if(this.overlay){
24580             this.overlay.hide();
24581         }
24582         var newSize;
24583         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24584             newSize = this.dragSpecs.startSize + 
24585                 (this.placement == Roo.SplitBar.LEFT ?
24586                     endPoint[0] - this.dragSpecs.startPoint[0] :
24587                     this.dragSpecs.startPoint[0] - endPoint[0]
24588                 );
24589         }else{
24590             newSize = this.dragSpecs.startSize + 
24591                 (this.placement == Roo.SplitBar.TOP ?
24592                     endPoint[1] - this.dragSpecs.startPoint[1] :
24593                     this.dragSpecs.startPoint[1] - endPoint[1]
24594                 );
24595         }
24596         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24597         if(newSize != this.dragSpecs.startSize){
24598             if(this.fireEvent('beforeapply', this, newSize) !== false){
24599                 this.adapter.setElementSize(this, newSize);
24600                 this.fireEvent("moved", this, newSize);
24601                 this.fireEvent("resize", this, newSize);
24602             }
24603         }
24604     },
24605     
24606     /**
24607      * Get the adapter this SplitBar uses
24608      * @return The adapter object
24609      */
24610     getAdapter : function(){
24611         return this.adapter;
24612     },
24613     
24614     /**
24615      * Set the adapter this SplitBar uses
24616      * @param {Object} adapter A SplitBar adapter object
24617      */
24618     setAdapter : function(adapter){
24619         this.adapter = adapter;
24620         this.adapter.init(this);
24621     },
24622     
24623     /**
24624      * Gets the minimum size for the resizing element
24625      * @return {Number} The minimum size
24626      */
24627     getMinimumSize : function(){
24628         return this.minSize;
24629     },
24630     
24631     /**
24632      * Sets the minimum size for the resizing element
24633      * @param {Number} minSize The minimum size
24634      */
24635     setMinimumSize : function(minSize){
24636         this.minSize = minSize;
24637     },
24638     
24639     /**
24640      * Gets the maximum size for the resizing element
24641      * @return {Number} The maximum size
24642      */
24643     getMaximumSize : function(){
24644         return this.maxSize;
24645     },
24646     
24647     /**
24648      * Sets the maximum size for the resizing element
24649      * @param {Number} maxSize The maximum size
24650      */
24651     setMaximumSize : function(maxSize){
24652         this.maxSize = maxSize;
24653     },
24654     
24655     /**
24656      * Sets the initialize size for the resizing element
24657      * @param {Number} size The initial size
24658      */
24659     setCurrentSize : function(size){
24660         var oldAnimate = this.animate;
24661         this.animate = false;
24662         this.adapter.setElementSize(this, size);
24663         this.animate = oldAnimate;
24664     },
24665     
24666     /**
24667      * Destroy this splitbar. 
24668      * @param {Boolean} removeEl True to remove the element
24669      */
24670     destroy : function(removeEl){
24671         if(this.shim){
24672             this.shim.remove();
24673         }
24674         this.dd.unreg();
24675         this.proxy.parentNode.removeChild(this.proxy);
24676         if(removeEl){
24677             this.el.remove();
24678         }
24679     }
24680 });
24681
24682 /**
24683  * @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.
24684  */
24685 Roo.SplitBar.createProxy = function(dir){
24686     var proxy = new Roo.Element(document.createElement("div"));
24687     proxy.unselectable();
24688     var cls = 'x-splitbar-proxy';
24689     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24690     document.body.appendChild(proxy.dom);
24691     return proxy.dom;
24692 };
24693
24694 /** 
24695  * @class Roo.SplitBar.BasicLayoutAdapter
24696  * Default Adapter. It assumes the splitter and resizing element are not positioned
24697  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24698  */
24699 Roo.SplitBar.BasicLayoutAdapter = function(){
24700 };
24701
24702 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24703     // do nothing for now
24704     init : function(s){
24705     
24706     },
24707     /**
24708      * Called before drag operations to get the current size of the resizing element. 
24709      * @param {Roo.SplitBar} s The SplitBar using this adapter
24710      */
24711      getElementSize : function(s){
24712         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24713             return s.resizingEl.getWidth();
24714         }else{
24715             return s.resizingEl.getHeight();
24716         }
24717     },
24718     
24719     /**
24720      * Called after drag operations to set the size of the resizing element.
24721      * @param {Roo.SplitBar} s The SplitBar using this adapter
24722      * @param {Number} newSize The new size to set
24723      * @param {Function} onComplete A function to be invoked when resizing is complete
24724      */
24725     setElementSize : function(s, newSize, onComplete){
24726         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24727             if(!s.animate){
24728                 s.resizingEl.setWidth(newSize);
24729                 if(onComplete){
24730                     onComplete(s, newSize);
24731                 }
24732             }else{
24733                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24734             }
24735         }else{
24736             
24737             if(!s.animate){
24738                 s.resizingEl.setHeight(newSize);
24739                 if(onComplete){
24740                     onComplete(s, newSize);
24741                 }
24742             }else{
24743                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24744             }
24745         }
24746     }
24747 };
24748
24749 /** 
24750  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24751  * @extends Roo.SplitBar.BasicLayoutAdapter
24752  * Adapter that  moves the splitter element to align with the resized sizing element. 
24753  * Used with an absolute positioned SplitBar.
24754  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24755  * document.body, make sure you assign an id to the body element.
24756  */
24757 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24758     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24759     this.container = Roo.get(container);
24760 };
24761
24762 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24763     init : function(s){
24764         this.basic.init(s);
24765     },
24766     
24767     getElementSize : function(s){
24768         return this.basic.getElementSize(s);
24769     },
24770     
24771     setElementSize : function(s, newSize, onComplete){
24772         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24773     },
24774     
24775     moveSplitter : function(s){
24776         var yes = Roo.SplitBar;
24777         switch(s.placement){
24778             case yes.LEFT:
24779                 s.el.setX(s.resizingEl.getRight());
24780                 break;
24781             case yes.RIGHT:
24782                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24783                 break;
24784             case yes.TOP:
24785                 s.el.setY(s.resizingEl.getBottom());
24786                 break;
24787             case yes.BOTTOM:
24788                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24789                 break;
24790         }
24791     }
24792 };
24793
24794 /**
24795  * Orientation constant - Create a vertical SplitBar
24796  * @static
24797  * @type Number
24798  */
24799 Roo.SplitBar.VERTICAL = 1;
24800
24801 /**
24802  * Orientation constant - Create a horizontal SplitBar
24803  * @static
24804  * @type Number
24805  */
24806 Roo.SplitBar.HORIZONTAL = 2;
24807
24808 /**
24809  * Placement constant - The resizing element is to the left of the splitter element
24810  * @static
24811  * @type Number
24812  */
24813 Roo.SplitBar.LEFT = 1;
24814
24815 /**
24816  * Placement constant - The resizing element is to the right of the splitter element
24817  * @static
24818  * @type Number
24819  */
24820 Roo.SplitBar.RIGHT = 2;
24821
24822 /**
24823  * Placement constant - The resizing element is positioned above the splitter element
24824  * @static
24825  * @type Number
24826  */
24827 Roo.SplitBar.TOP = 3;
24828
24829 /**
24830  * Placement constant - The resizing element is positioned under splitter element
24831  * @static
24832  * @type Number
24833  */
24834 Roo.SplitBar.BOTTOM = 4;
24835 /*
24836  * Based on:
24837  * Ext JS Library 1.1.1
24838  * Copyright(c) 2006-2007, Ext JS, LLC.
24839  *
24840  * Originally Released Under LGPL - original licence link has changed is not relivant.
24841  *
24842  * Fork - LGPL
24843  * <script type="text/javascript">
24844  */
24845
24846 /**
24847  * @class Roo.View
24848  * @extends Roo.util.Observable
24849  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24850  * This class also supports single and multi selection modes. <br>
24851  * Create a data model bound view:
24852  <pre><code>
24853  var store = new Roo.data.Store(...);
24854
24855  var view = new Roo.View({
24856     el : "my-element",
24857     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24858  
24859     singleSelect: true,
24860     selectedClass: "ydataview-selected",
24861     store: store
24862  });
24863
24864  // listen for node click?
24865  view.on("click", function(vw, index, node, e){
24866  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24867  });
24868
24869  // load XML data
24870  dataModel.load("foobar.xml");
24871  </code></pre>
24872  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24873  * <br><br>
24874  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24875  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24876  * 
24877  * Note: old style constructor is still suported (container, template, config)
24878  * 
24879  * @constructor
24880  * Create a new View
24881  * @param {Object} config The config object
24882  * 
24883  */
24884 Roo.View = function(config, depreciated_tpl, depreciated_config){
24885     
24886     this.parent = false;
24887     
24888     if (typeof(depreciated_tpl) == 'undefined') {
24889         // new way.. - universal constructor.
24890         Roo.apply(this, config);
24891         this.el  = Roo.get(this.el);
24892     } else {
24893         // old format..
24894         this.el  = Roo.get(config);
24895         this.tpl = depreciated_tpl;
24896         Roo.apply(this, depreciated_config);
24897     }
24898     this.wrapEl  = this.el.wrap().wrap();
24899     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24900     
24901     
24902     if(typeof(this.tpl) == "string"){
24903         this.tpl = new Roo.Template(this.tpl);
24904     } else {
24905         // support xtype ctors..
24906         this.tpl = new Roo.factory(this.tpl, Roo);
24907     }
24908     
24909     
24910     this.tpl.compile();
24911     
24912     /** @private */
24913     this.addEvents({
24914         /**
24915          * @event beforeclick
24916          * Fires before a click is processed. Returns false to cancel the default action.
24917          * @param {Roo.View} this
24918          * @param {Number} index The index of the target node
24919          * @param {HTMLElement} node The target node
24920          * @param {Roo.EventObject} e The raw event object
24921          */
24922             "beforeclick" : true,
24923         /**
24924          * @event click
24925          * Fires when a template node is clicked.
24926          * @param {Roo.View} this
24927          * @param {Number} index The index of the target node
24928          * @param {HTMLElement} node The target node
24929          * @param {Roo.EventObject} e The raw event object
24930          */
24931             "click" : true,
24932         /**
24933          * @event dblclick
24934          * Fires when a template node is double clicked.
24935          * @param {Roo.View} this
24936          * @param {Number} index The index of the target node
24937          * @param {HTMLElement} node The target node
24938          * @param {Roo.EventObject} e The raw event object
24939          */
24940             "dblclick" : true,
24941         /**
24942          * @event contextmenu
24943          * Fires when a template node is right clicked.
24944          * @param {Roo.View} this
24945          * @param {Number} index The index of the target node
24946          * @param {HTMLElement} node The target node
24947          * @param {Roo.EventObject} e The raw event object
24948          */
24949             "contextmenu" : true,
24950         /**
24951          * @event selectionchange
24952          * Fires when the selected nodes change.
24953          * @param {Roo.View} this
24954          * @param {Array} selections Array of the selected nodes
24955          */
24956             "selectionchange" : true,
24957     
24958         /**
24959          * @event beforeselect
24960          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24961          * @param {Roo.View} this
24962          * @param {HTMLElement} node The node to be selected
24963          * @param {Array} selections Array of currently selected nodes
24964          */
24965             "beforeselect" : true,
24966         /**
24967          * @event preparedata
24968          * Fires on every row to render, to allow you to change the data.
24969          * @param {Roo.View} this
24970          * @param {Object} data to be rendered (change this)
24971          */
24972           "preparedata" : true
24973           
24974           
24975         });
24976
24977
24978
24979     this.el.on({
24980         "click": this.onClick,
24981         "dblclick": this.onDblClick,
24982         "contextmenu": this.onContextMenu,
24983         scope:this
24984     });
24985
24986     this.selections = [];
24987     this.nodes = [];
24988     this.cmp = new Roo.CompositeElementLite([]);
24989     if(this.store){
24990         this.store = Roo.factory(this.store, Roo.data);
24991         this.setStore(this.store, true);
24992     }
24993     
24994     if ( this.footer && this.footer.xtype) {
24995            
24996          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24997         
24998         this.footer.dataSource = this.store
24999         this.footer.container = fctr;
25000         this.footer = Roo.factory(this.footer, Roo);
25001         fctr.insertFirst(this.el);
25002         
25003         // this is a bit insane - as the paging toolbar seems to detach the el..
25004 //        dom.parentNode.parentNode.parentNode
25005          // they get detached?
25006     }
25007     
25008     
25009     Roo.View.superclass.constructor.call(this);
25010     
25011     
25012 };
25013
25014 Roo.extend(Roo.View, Roo.util.Observable, {
25015     
25016      /**
25017      * @cfg {Roo.data.Store} store Data store to load data from.
25018      */
25019     store : false,
25020     
25021     /**
25022      * @cfg {String|Roo.Element} el The container element.
25023      */
25024     el : '',
25025     
25026     /**
25027      * @cfg {String|Roo.Template} tpl The template used by this View 
25028      */
25029     tpl : false,
25030     /**
25031      * @cfg {String} dataName the named area of the template to use as the data area
25032      *                          Works with domtemplates roo-name="name"
25033      */
25034     dataName: false,
25035     /**
25036      * @cfg {String} selectedClass The css class to add to selected nodes
25037      */
25038     selectedClass : "x-view-selected",
25039      /**
25040      * @cfg {String} emptyText The empty text to show when nothing is loaded.
25041      */
25042     emptyText : "",
25043     
25044     /**
25045      * @cfg {String} text to display on mask (default Loading)
25046      */
25047     mask : false,
25048     /**
25049      * @cfg {Boolean} multiSelect Allow multiple selection
25050      */
25051     multiSelect : false,
25052     /**
25053      * @cfg {Boolean} singleSelect Allow single selection
25054      */
25055     singleSelect:  false,
25056     
25057     /**
25058      * @cfg {Boolean} toggleSelect - selecting 
25059      */
25060     toggleSelect : false,
25061     
25062     /**
25063      * @cfg {Boolean} tickable - selecting 
25064      */
25065     tickable : false,
25066     
25067     /**
25068      * Returns the element this view is bound to.
25069      * @return {Roo.Element}
25070      */
25071     getEl : function(){
25072         return this.wrapEl;
25073     },
25074     
25075     
25076
25077     /**
25078      * Refreshes the view. - called by datachanged on the store. - do not call directly.
25079      */
25080     refresh : function(){
25081         //Roo.log('refresh');
25082         var t = this.tpl;
25083         
25084         // if we are using something like 'domtemplate', then
25085         // the what gets used is:
25086         // t.applySubtemplate(NAME, data, wrapping data..)
25087         // the outer template then get' applied with
25088         //     the store 'extra data'
25089         // and the body get's added to the
25090         //      roo-name="data" node?
25091         //      <span class='roo-tpl-{name}'></span> ?????
25092         
25093         
25094         
25095         this.clearSelections();
25096         this.el.update("");
25097         var html = [];
25098         var records = this.store.getRange();
25099         if(records.length < 1) {
25100             
25101             // is this valid??  = should it render a template??
25102             
25103             this.el.update(this.emptyText);
25104             return;
25105         }
25106         var el = this.el;
25107         if (this.dataName) {
25108             this.el.update(t.apply(this.store.meta)); //????
25109             el = this.el.child('.roo-tpl-' + this.dataName);
25110         }
25111         
25112         for(var i = 0, len = records.length; i < len; i++){
25113             var data = this.prepareData(records[i].data, i, records[i]);
25114             this.fireEvent("preparedata", this, data, i, records[i]);
25115             
25116             var d = Roo.apply({}, data);
25117             
25118             if(this.tickable){
25119                 Roo.apply(d, {'roo-id' : Roo.id()});
25120                 
25121                 var _this = this;
25122             
25123                 Roo.each(this.parent.item, function(item){
25124                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
25125                         return;
25126                     }
25127                     Roo.apply(d, {'roo-data-checked' : 'checked'});
25128                 });
25129             }
25130             
25131             html[html.length] = Roo.util.Format.trim(
25132                 this.dataName ?
25133                     t.applySubtemplate(this.dataName, d, this.store.meta) :
25134                     t.apply(d)
25135             );
25136         }
25137         
25138         
25139         
25140         el.update(html.join(""));
25141         this.nodes = el.dom.childNodes;
25142         this.updateIndexes(0);
25143     },
25144     
25145
25146     /**
25147      * Function to override to reformat the data that is sent to
25148      * the template for each node.
25149      * DEPRICATED - use the preparedata event handler.
25150      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
25151      * a JSON object for an UpdateManager bound view).
25152      */
25153     prepareData : function(data, index, record)
25154     {
25155         this.fireEvent("preparedata", this, data, index, record);
25156         return data;
25157     },
25158
25159     onUpdate : function(ds, record){
25160         // Roo.log('on update');   
25161         this.clearSelections();
25162         var index = this.store.indexOf(record);
25163         var n = this.nodes[index];
25164         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25165         n.parentNode.removeChild(n);
25166         this.updateIndexes(index, index);
25167     },
25168
25169     
25170     
25171 // --------- FIXME     
25172     onAdd : function(ds, records, index)
25173     {
25174         //Roo.log(['on Add', ds, records, index] );        
25175         this.clearSelections();
25176         if(this.nodes.length == 0){
25177             this.refresh();
25178             return;
25179         }
25180         var n = this.nodes[index];
25181         for(var i = 0, len = records.length; i < len; i++){
25182             var d = this.prepareData(records[i].data, i, records[i]);
25183             if(n){
25184                 this.tpl.insertBefore(n, d);
25185             }else{
25186                 
25187                 this.tpl.append(this.el, d);
25188             }
25189         }
25190         this.updateIndexes(index);
25191     },
25192
25193     onRemove : function(ds, record, index){
25194        // Roo.log('onRemove');
25195         this.clearSelections();
25196         var el = this.dataName  ?
25197             this.el.child('.roo-tpl-' + this.dataName) :
25198             this.el; 
25199         
25200         el.dom.removeChild(this.nodes[index]);
25201         this.updateIndexes(index);
25202     },
25203
25204     /**
25205      * Refresh an individual node.
25206      * @param {Number} index
25207      */
25208     refreshNode : function(index){
25209         this.onUpdate(this.store, this.store.getAt(index));
25210     },
25211
25212     updateIndexes : function(startIndex, endIndex){
25213         var ns = this.nodes;
25214         startIndex = startIndex || 0;
25215         endIndex = endIndex || ns.length - 1;
25216         for(var i = startIndex; i <= endIndex; i++){
25217             ns[i].nodeIndex = i;
25218         }
25219     },
25220
25221     /**
25222      * Changes the data store this view uses and refresh the view.
25223      * @param {Store} store
25224      */
25225     setStore : function(store, initial){
25226         if(!initial && this.store){
25227             this.store.un("datachanged", this.refresh);
25228             this.store.un("add", this.onAdd);
25229             this.store.un("remove", this.onRemove);
25230             this.store.un("update", this.onUpdate);
25231             this.store.un("clear", this.refresh);
25232             this.store.un("beforeload", this.onBeforeLoad);
25233             this.store.un("load", this.onLoad);
25234             this.store.un("loadexception", this.onLoad);
25235         }
25236         if(store){
25237           
25238             store.on("datachanged", this.refresh, this);
25239             store.on("add", this.onAdd, this);
25240             store.on("remove", this.onRemove, this);
25241             store.on("update", this.onUpdate, this);
25242             store.on("clear", this.refresh, this);
25243             store.on("beforeload", this.onBeforeLoad, this);
25244             store.on("load", this.onLoad, this);
25245             store.on("loadexception", this.onLoad, this);
25246         }
25247         
25248         if(store){
25249             this.refresh();
25250         }
25251     },
25252     /**
25253      * onbeforeLoad - masks the loading area.
25254      *
25255      */
25256     onBeforeLoad : function(store,opts)
25257     {
25258          //Roo.log('onBeforeLoad');   
25259         if (!opts.add) {
25260             this.el.update("");
25261         }
25262         this.el.mask(this.mask ? this.mask : "Loading" ); 
25263     },
25264     onLoad : function ()
25265     {
25266         this.el.unmask();
25267     },
25268     
25269
25270     /**
25271      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25272      * @param {HTMLElement} node
25273      * @return {HTMLElement} The template node
25274      */
25275     findItemFromChild : function(node){
25276         var el = this.dataName  ?
25277             this.el.child('.roo-tpl-' + this.dataName,true) :
25278             this.el.dom; 
25279         
25280         if(!node || node.parentNode == el){
25281                     return node;
25282             }
25283             var p = node.parentNode;
25284             while(p && p != el){
25285             if(p.parentNode == el){
25286                 return p;
25287             }
25288             p = p.parentNode;
25289         }
25290             return null;
25291     },
25292
25293     /** @ignore */
25294     onClick : function(e){
25295         var item = this.findItemFromChild(e.getTarget());
25296         if(item){
25297             var index = this.indexOf(item);
25298             if(this.onItemClick(item, index, e) !== false){
25299                 this.fireEvent("click", this, index, item, e);
25300             }
25301         }else{
25302             this.clearSelections();
25303         }
25304     },
25305
25306     /** @ignore */
25307     onContextMenu : function(e){
25308         var item = this.findItemFromChild(e.getTarget());
25309         if(item){
25310             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25311         }
25312     },
25313
25314     /** @ignore */
25315     onDblClick : function(e){
25316         var item = this.findItemFromChild(e.getTarget());
25317         if(item){
25318             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25319         }
25320     },
25321
25322     onItemClick : function(item, index, e)
25323     {
25324         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25325             return false;
25326         }
25327         if (this.toggleSelect) {
25328             var m = this.isSelected(item) ? 'unselect' : 'select';
25329             //Roo.log(m);
25330             var _t = this;
25331             _t[m](item, true, false);
25332             return true;
25333         }
25334         if(this.multiSelect || this.singleSelect){
25335             if(this.multiSelect && e.shiftKey && this.lastSelection){
25336                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25337             }else{
25338                 this.select(item, this.multiSelect && e.ctrlKey);
25339                 this.lastSelection = item;
25340             }
25341             
25342             if(!this.tickable){
25343                 e.preventDefault();
25344             }
25345             
25346         }
25347         return true;
25348     },
25349
25350     /**
25351      * Get the number of selected nodes.
25352      * @return {Number}
25353      */
25354     getSelectionCount : function(){
25355         return this.selections.length;
25356     },
25357
25358     /**
25359      * Get the currently selected nodes.
25360      * @return {Array} An array of HTMLElements
25361      */
25362     getSelectedNodes : function(){
25363         return this.selections;
25364     },
25365
25366     /**
25367      * Get the indexes of the selected nodes.
25368      * @return {Array}
25369      */
25370     getSelectedIndexes : function(){
25371         var indexes = [], s = this.selections;
25372         for(var i = 0, len = s.length; i < len; i++){
25373             indexes.push(s[i].nodeIndex);
25374         }
25375         return indexes;
25376     },
25377
25378     /**
25379      * Clear all selections
25380      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25381      */
25382     clearSelections : function(suppressEvent){
25383         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25384             this.cmp.elements = this.selections;
25385             this.cmp.removeClass(this.selectedClass);
25386             this.selections = [];
25387             if(!suppressEvent){
25388                 this.fireEvent("selectionchange", this, this.selections);
25389             }
25390         }
25391     },
25392
25393     /**
25394      * Returns true if the passed node is selected
25395      * @param {HTMLElement/Number} node The node or node index
25396      * @return {Boolean}
25397      */
25398     isSelected : function(node){
25399         var s = this.selections;
25400         if(s.length < 1){
25401             return false;
25402         }
25403         node = this.getNode(node);
25404         return s.indexOf(node) !== -1;
25405     },
25406
25407     /**
25408      * Selects nodes.
25409      * @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
25410      * @param {Boolean} keepExisting (optional) true to keep existing selections
25411      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25412      */
25413     select : function(nodeInfo, keepExisting, suppressEvent){
25414         if(nodeInfo instanceof Array){
25415             if(!keepExisting){
25416                 this.clearSelections(true);
25417             }
25418             for(var i = 0, len = nodeInfo.length; i < len; i++){
25419                 this.select(nodeInfo[i], true, true);
25420             }
25421             return;
25422         } 
25423         var node = this.getNode(nodeInfo);
25424         if(!node || this.isSelected(node)){
25425             return; // already selected.
25426         }
25427         if(!keepExisting){
25428             this.clearSelections(true);
25429         }
25430         
25431         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25432             Roo.fly(node).addClass(this.selectedClass);
25433             this.selections.push(node);
25434             if(!suppressEvent){
25435                 this.fireEvent("selectionchange", this, this.selections);
25436             }
25437         }
25438         
25439         
25440     },
25441       /**
25442      * Unselects nodes.
25443      * @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
25444      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25445      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25446      */
25447     unselect : function(nodeInfo, keepExisting, suppressEvent)
25448     {
25449         if(nodeInfo instanceof Array){
25450             Roo.each(this.selections, function(s) {
25451                 this.unselect(s, nodeInfo);
25452             }, this);
25453             return;
25454         }
25455         var node = this.getNode(nodeInfo);
25456         if(!node || !this.isSelected(node)){
25457             //Roo.log("not selected");
25458             return; // not selected.
25459         }
25460         // fireevent???
25461         var ns = [];
25462         Roo.each(this.selections, function(s) {
25463             if (s == node ) {
25464                 Roo.fly(node).removeClass(this.selectedClass);
25465
25466                 return;
25467             }
25468             ns.push(s);
25469         },this);
25470         
25471         this.selections= ns;
25472         this.fireEvent("selectionchange", this, this.selections);
25473     },
25474
25475     /**
25476      * Gets a template node.
25477      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25478      * @return {HTMLElement} The node or null if it wasn't found
25479      */
25480     getNode : function(nodeInfo){
25481         if(typeof nodeInfo == "string"){
25482             return document.getElementById(nodeInfo);
25483         }else if(typeof nodeInfo == "number"){
25484             return this.nodes[nodeInfo];
25485         }
25486         return nodeInfo;
25487     },
25488
25489     /**
25490      * Gets a range template nodes.
25491      * @param {Number} startIndex
25492      * @param {Number} endIndex
25493      * @return {Array} An array of nodes
25494      */
25495     getNodes : function(start, end){
25496         var ns = this.nodes;
25497         start = start || 0;
25498         end = typeof end == "undefined" ? ns.length - 1 : end;
25499         var nodes = [];
25500         if(start <= end){
25501             for(var i = start; i <= end; i++){
25502                 nodes.push(ns[i]);
25503             }
25504         } else{
25505             for(var i = start; i >= end; i--){
25506                 nodes.push(ns[i]);
25507             }
25508         }
25509         return nodes;
25510     },
25511
25512     /**
25513      * Finds the index of the passed node
25514      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25515      * @return {Number} The index of the node or -1
25516      */
25517     indexOf : function(node){
25518         node = this.getNode(node);
25519         if(typeof node.nodeIndex == "number"){
25520             return node.nodeIndex;
25521         }
25522         var ns = this.nodes;
25523         for(var i = 0, len = ns.length; i < len; i++){
25524             if(ns[i] == node){
25525                 return i;
25526             }
25527         }
25528         return -1;
25529     }
25530 });
25531 /*
25532  * Based on:
25533  * Ext JS Library 1.1.1
25534  * Copyright(c) 2006-2007, Ext JS, LLC.
25535  *
25536  * Originally Released Under LGPL - original licence link has changed is not relivant.
25537  *
25538  * Fork - LGPL
25539  * <script type="text/javascript">
25540  */
25541
25542 /**
25543  * @class Roo.JsonView
25544  * @extends Roo.View
25545  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25546 <pre><code>
25547 var view = new Roo.JsonView({
25548     container: "my-element",
25549     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25550     multiSelect: true, 
25551     jsonRoot: "data" 
25552 });
25553
25554 // listen for node click?
25555 view.on("click", function(vw, index, node, e){
25556     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25557 });
25558
25559 // direct load of JSON data
25560 view.load("foobar.php");
25561
25562 // Example from my blog list
25563 var tpl = new Roo.Template(
25564     '&lt;div class="entry"&gt;' +
25565     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25566     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25567     "&lt;/div&gt;&lt;hr /&gt;"
25568 );
25569
25570 var moreView = new Roo.JsonView({
25571     container :  "entry-list", 
25572     template : tpl,
25573     jsonRoot: "posts"
25574 });
25575 moreView.on("beforerender", this.sortEntries, this);
25576 moreView.load({
25577     url: "/blog/get-posts.php",
25578     params: "allposts=true",
25579     text: "Loading Blog Entries..."
25580 });
25581 </code></pre>
25582
25583 * Note: old code is supported with arguments : (container, template, config)
25584
25585
25586  * @constructor
25587  * Create a new JsonView
25588  * 
25589  * @param {Object} config The config object
25590  * 
25591  */
25592 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25593     
25594     
25595     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25596
25597     var um = this.el.getUpdateManager();
25598     um.setRenderer(this);
25599     um.on("update", this.onLoad, this);
25600     um.on("failure", this.onLoadException, this);
25601
25602     /**
25603      * @event beforerender
25604      * Fires before rendering of the downloaded JSON data.
25605      * @param {Roo.JsonView} this
25606      * @param {Object} data The JSON data loaded
25607      */
25608     /**
25609      * @event load
25610      * Fires when data is loaded.
25611      * @param {Roo.JsonView} this
25612      * @param {Object} data The JSON data loaded
25613      * @param {Object} response The raw Connect response object
25614      */
25615     /**
25616      * @event loadexception
25617      * Fires when loading fails.
25618      * @param {Roo.JsonView} this
25619      * @param {Object} response The raw Connect response object
25620      */
25621     this.addEvents({
25622         'beforerender' : true,
25623         'load' : true,
25624         'loadexception' : true
25625     });
25626 };
25627 Roo.extend(Roo.JsonView, Roo.View, {
25628     /**
25629      * @type {String} The root property in the loaded JSON object that contains the data
25630      */
25631     jsonRoot : "",
25632
25633     /**
25634      * Refreshes the view.
25635      */
25636     refresh : function(){
25637         this.clearSelections();
25638         this.el.update("");
25639         var html = [];
25640         var o = this.jsonData;
25641         if(o && o.length > 0){
25642             for(var i = 0, len = o.length; i < len; i++){
25643                 var data = this.prepareData(o[i], i, o);
25644                 html[html.length] = this.tpl.apply(data);
25645             }
25646         }else{
25647             html.push(this.emptyText);
25648         }
25649         this.el.update(html.join(""));
25650         this.nodes = this.el.dom.childNodes;
25651         this.updateIndexes(0);
25652     },
25653
25654     /**
25655      * 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.
25656      * @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:
25657      <pre><code>
25658      view.load({
25659          url: "your-url.php",
25660          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25661          callback: yourFunction,
25662          scope: yourObject, //(optional scope)
25663          discardUrl: false,
25664          nocache: false,
25665          text: "Loading...",
25666          timeout: 30,
25667          scripts: false
25668      });
25669      </code></pre>
25670      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25671      * 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.
25672      * @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}
25673      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25674      * @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.
25675      */
25676     load : function(){
25677         var um = this.el.getUpdateManager();
25678         um.update.apply(um, arguments);
25679     },
25680
25681     render : function(el, response){
25682         this.clearSelections();
25683         this.el.update("");
25684         var o;
25685         try{
25686             o = Roo.util.JSON.decode(response.responseText);
25687             if(this.jsonRoot){
25688                 
25689                 o = o[this.jsonRoot];
25690             }
25691         } catch(e){
25692         }
25693         /**
25694          * The current JSON data or null
25695          */
25696         this.jsonData = o;
25697         this.beforeRender();
25698         this.refresh();
25699     },
25700
25701 /**
25702  * Get the number of records in the current JSON dataset
25703  * @return {Number}
25704  */
25705     getCount : function(){
25706         return this.jsonData ? this.jsonData.length : 0;
25707     },
25708
25709 /**
25710  * Returns the JSON object for the specified node(s)
25711  * @param {HTMLElement/Array} node The node or an array of nodes
25712  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25713  * you get the JSON object for the node
25714  */
25715     getNodeData : function(node){
25716         if(node instanceof Array){
25717             var data = [];
25718             for(var i = 0, len = node.length; i < len; i++){
25719                 data.push(this.getNodeData(node[i]));
25720             }
25721             return data;
25722         }
25723         return this.jsonData[this.indexOf(node)] || null;
25724     },
25725
25726     beforeRender : function(){
25727         this.snapshot = this.jsonData;
25728         if(this.sortInfo){
25729             this.sort.apply(this, this.sortInfo);
25730         }
25731         this.fireEvent("beforerender", this, this.jsonData);
25732     },
25733
25734     onLoad : function(el, o){
25735         this.fireEvent("load", this, this.jsonData, o);
25736     },
25737
25738     onLoadException : function(el, o){
25739         this.fireEvent("loadexception", this, o);
25740     },
25741
25742 /**
25743  * Filter the data by a specific property.
25744  * @param {String} property A property on your JSON objects
25745  * @param {String/RegExp} value Either string that the property values
25746  * should start with, or a RegExp to test against the property
25747  */
25748     filter : function(property, value){
25749         if(this.jsonData){
25750             var data = [];
25751             var ss = this.snapshot;
25752             if(typeof value == "string"){
25753                 var vlen = value.length;
25754                 if(vlen == 0){
25755                     this.clearFilter();
25756                     return;
25757                 }
25758                 value = value.toLowerCase();
25759                 for(var i = 0, len = ss.length; i < len; i++){
25760                     var o = ss[i];
25761                     if(o[property].substr(0, vlen).toLowerCase() == value){
25762                         data.push(o);
25763                     }
25764                 }
25765             } else if(value.exec){ // regex?
25766                 for(var i = 0, len = ss.length; i < len; i++){
25767                     var o = ss[i];
25768                     if(value.test(o[property])){
25769                         data.push(o);
25770                     }
25771                 }
25772             } else{
25773                 return;
25774             }
25775             this.jsonData = data;
25776             this.refresh();
25777         }
25778     },
25779
25780 /**
25781  * Filter by a function. The passed function will be called with each
25782  * object in the current dataset. If the function returns true the value is kept,
25783  * otherwise it is filtered.
25784  * @param {Function} fn
25785  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25786  */
25787     filterBy : function(fn, scope){
25788         if(this.jsonData){
25789             var data = [];
25790             var ss = this.snapshot;
25791             for(var i = 0, len = ss.length; i < len; i++){
25792                 var o = ss[i];
25793                 if(fn.call(scope || this, o)){
25794                     data.push(o);
25795                 }
25796             }
25797             this.jsonData = data;
25798             this.refresh();
25799         }
25800     },
25801
25802 /**
25803  * Clears the current filter.
25804  */
25805     clearFilter : function(){
25806         if(this.snapshot && this.jsonData != this.snapshot){
25807             this.jsonData = this.snapshot;
25808             this.refresh();
25809         }
25810     },
25811
25812
25813 /**
25814  * Sorts the data for this view and refreshes it.
25815  * @param {String} property A property on your JSON objects to sort on
25816  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25817  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25818  */
25819     sort : function(property, dir, sortType){
25820         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25821         if(this.jsonData){
25822             var p = property;
25823             var dsc = dir && dir.toLowerCase() == "desc";
25824             var f = function(o1, o2){
25825                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25826                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25827                 ;
25828                 if(v1 < v2){
25829                     return dsc ? +1 : -1;
25830                 } else if(v1 > v2){
25831                     return dsc ? -1 : +1;
25832                 } else{
25833                     return 0;
25834                 }
25835             };
25836             this.jsonData.sort(f);
25837             this.refresh();
25838             if(this.jsonData != this.snapshot){
25839                 this.snapshot.sort(f);
25840             }
25841         }
25842     }
25843 });/*
25844  * Based on:
25845  * Ext JS Library 1.1.1
25846  * Copyright(c) 2006-2007, Ext JS, LLC.
25847  *
25848  * Originally Released Under LGPL - original licence link has changed is not relivant.
25849  *
25850  * Fork - LGPL
25851  * <script type="text/javascript">
25852  */
25853  
25854
25855 /**
25856  * @class Roo.ColorPalette
25857  * @extends Roo.Component
25858  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25859  * Here's an example of typical usage:
25860  * <pre><code>
25861 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25862 cp.render('my-div');
25863
25864 cp.on('select', function(palette, selColor){
25865     // do something with selColor
25866 });
25867 </code></pre>
25868  * @constructor
25869  * Create a new ColorPalette
25870  * @param {Object} config The config object
25871  */
25872 Roo.ColorPalette = function(config){
25873     Roo.ColorPalette.superclass.constructor.call(this, config);
25874     this.addEvents({
25875         /**
25876              * @event select
25877              * Fires when a color is selected
25878              * @param {ColorPalette} this
25879              * @param {String} color The 6-digit color hex code (without the # symbol)
25880              */
25881         select: true
25882     });
25883
25884     if(this.handler){
25885         this.on("select", this.handler, this.scope, true);
25886     }
25887 };
25888 Roo.extend(Roo.ColorPalette, Roo.Component, {
25889     /**
25890      * @cfg {String} itemCls
25891      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25892      */
25893     itemCls : "x-color-palette",
25894     /**
25895      * @cfg {String} value
25896      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25897      * the hex codes are case-sensitive.
25898      */
25899     value : null,
25900     clickEvent:'click',
25901     // private
25902     ctype: "Roo.ColorPalette",
25903
25904     /**
25905      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25906      */
25907     allowReselect : false,
25908
25909     /**
25910      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25911      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25912      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25913      * of colors with the width setting until the box is symmetrical.</p>
25914      * <p>You can override individual colors if needed:</p>
25915      * <pre><code>
25916 var cp = new Roo.ColorPalette();
25917 cp.colors[0] = "FF0000";  // change the first box to red
25918 </code></pre>
25919
25920 Or you can provide a custom array of your own for complete control:
25921 <pre><code>
25922 var cp = new Roo.ColorPalette();
25923 cp.colors = ["000000", "993300", "333300"];
25924 </code></pre>
25925      * @type Array
25926      */
25927     colors : [
25928         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25929         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25930         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25931         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25932         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25933     ],
25934
25935     // private
25936     onRender : function(container, position){
25937         var t = new Roo.MasterTemplate(
25938             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25939         );
25940         var c = this.colors;
25941         for(var i = 0, len = c.length; i < len; i++){
25942             t.add([c[i]]);
25943         }
25944         var el = document.createElement("div");
25945         el.className = this.itemCls;
25946         t.overwrite(el);
25947         container.dom.insertBefore(el, position);
25948         this.el = Roo.get(el);
25949         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25950         if(this.clickEvent != 'click'){
25951             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25952         }
25953     },
25954
25955     // private
25956     afterRender : function(){
25957         Roo.ColorPalette.superclass.afterRender.call(this);
25958         if(this.value){
25959             var s = this.value;
25960             this.value = null;
25961             this.select(s);
25962         }
25963     },
25964
25965     // private
25966     handleClick : function(e, t){
25967         e.preventDefault();
25968         if(!this.disabled){
25969             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25970             this.select(c.toUpperCase());
25971         }
25972     },
25973
25974     /**
25975      * Selects the specified color in the palette (fires the select event)
25976      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25977      */
25978     select : function(color){
25979         color = color.replace("#", "");
25980         if(color != this.value || this.allowReselect){
25981             var el = this.el;
25982             if(this.value){
25983                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25984             }
25985             el.child("a.color-"+color).addClass("x-color-palette-sel");
25986             this.value = color;
25987             this.fireEvent("select", this, color);
25988         }
25989     }
25990 });/*
25991  * Based on:
25992  * Ext JS Library 1.1.1
25993  * Copyright(c) 2006-2007, Ext JS, LLC.
25994  *
25995  * Originally Released Under LGPL - original licence link has changed is not relivant.
25996  *
25997  * Fork - LGPL
25998  * <script type="text/javascript">
25999  */
26000  
26001 /**
26002  * @class Roo.DatePicker
26003  * @extends Roo.Component
26004  * Simple date picker class.
26005  * @constructor
26006  * Create a new DatePicker
26007  * @param {Object} config The config object
26008  */
26009 Roo.DatePicker = function(config){
26010     Roo.DatePicker.superclass.constructor.call(this, config);
26011
26012     this.value = config && config.value ?
26013                  config.value.clearTime() : new Date().clearTime();
26014
26015     this.addEvents({
26016         /**
26017              * @event select
26018              * Fires when a date is selected
26019              * @param {DatePicker} this
26020              * @param {Date} date The selected date
26021              */
26022         'select': true,
26023         /**
26024              * @event monthchange
26025              * Fires when the displayed month changes 
26026              * @param {DatePicker} this
26027              * @param {Date} date The selected month
26028              */
26029         'monthchange': true
26030     });
26031
26032     if(this.handler){
26033         this.on("select", this.handler,  this.scope || this);
26034     }
26035     // build the disabledDatesRE
26036     if(!this.disabledDatesRE && this.disabledDates){
26037         var dd = this.disabledDates;
26038         var re = "(?:";
26039         for(var i = 0; i < dd.length; i++){
26040             re += dd[i];
26041             if(i != dd.length-1) re += "|";
26042         }
26043         this.disabledDatesRE = new RegExp(re + ")");
26044     }
26045 };
26046
26047 Roo.extend(Roo.DatePicker, Roo.Component, {
26048     /**
26049      * @cfg {String} todayText
26050      * The text to display on the button that selects the current date (defaults to "Today")
26051      */
26052     todayText : "Today",
26053     /**
26054      * @cfg {String} okText
26055      * The text to display on the ok button
26056      */
26057     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
26058     /**
26059      * @cfg {String} cancelText
26060      * The text to display on the cancel button
26061      */
26062     cancelText : "Cancel",
26063     /**
26064      * @cfg {String} todayTip
26065      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
26066      */
26067     todayTip : "{0} (Spacebar)",
26068     /**
26069      * @cfg {Date} minDate
26070      * Minimum allowable date (JavaScript date object, defaults to null)
26071      */
26072     minDate : null,
26073     /**
26074      * @cfg {Date} maxDate
26075      * Maximum allowable date (JavaScript date object, defaults to null)
26076      */
26077     maxDate : null,
26078     /**
26079      * @cfg {String} minText
26080      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
26081      */
26082     minText : "This date is before the minimum date",
26083     /**
26084      * @cfg {String} maxText
26085      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
26086      */
26087     maxText : "This date is after the maximum date",
26088     /**
26089      * @cfg {String} format
26090      * The default date format string which can be overriden for localization support.  The format must be
26091      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
26092      */
26093     format : "m/d/y",
26094     /**
26095      * @cfg {Array} disabledDays
26096      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
26097      */
26098     disabledDays : null,
26099     /**
26100      * @cfg {String} disabledDaysText
26101      * The tooltip to display when the date falls on a disabled day (defaults to "")
26102      */
26103     disabledDaysText : "",
26104     /**
26105      * @cfg {RegExp} disabledDatesRE
26106      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
26107      */
26108     disabledDatesRE : null,
26109     /**
26110      * @cfg {String} disabledDatesText
26111      * The tooltip text to display when the date falls on a disabled date (defaults to "")
26112      */
26113     disabledDatesText : "",
26114     /**
26115      * @cfg {Boolean} constrainToViewport
26116      * True to constrain the date picker to the viewport (defaults to true)
26117      */
26118     constrainToViewport : true,
26119     /**
26120      * @cfg {Array} monthNames
26121      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
26122      */
26123     monthNames : Date.monthNames,
26124     /**
26125      * @cfg {Array} dayNames
26126      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
26127      */
26128     dayNames : Date.dayNames,
26129     /**
26130      * @cfg {String} nextText
26131      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
26132      */
26133     nextText: 'Next Month (Control+Right)',
26134     /**
26135      * @cfg {String} prevText
26136      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
26137      */
26138     prevText: 'Previous Month (Control+Left)',
26139     /**
26140      * @cfg {String} monthYearText
26141      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
26142      */
26143     monthYearText: 'Choose a month (Control+Up/Down to move years)',
26144     /**
26145      * @cfg {Number} startDay
26146      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
26147      */
26148     startDay : 0,
26149     /**
26150      * @cfg {Bool} showClear
26151      * Show a clear button (usefull for date form elements that can be blank.)
26152      */
26153     
26154     showClear: false,
26155     
26156     /**
26157      * Sets the value of the date field
26158      * @param {Date} value The date to set
26159      */
26160     setValue : function(value){
26161         var old = this.value;
26162         
26163         if (typeof(value) == 'string') {
26164          
26165             value = Date.parseDate(value, this.format);
26166         }
26167         if (!value) {
26168             value = new Date();
26169         }
26170         
26171         this.value = value.clearTime(true);
26172         if(this.el){
26173             this.update(this.value);
26174         }
26175     },
26176
26177     /**
26178      * Gets the current selected value of the date field
26179      * @return {Date} The selected date
26180      */
26181     getValue : function(){
26182         return this.value;
26183     },
26184
26185     // private
26186     focus : function(){
26187         if(this.el){
26188             this.update(this.activeDate);
26189         }
26190     },
26191
26192     // privateval
26193     onRender : function(container, position){
26194         
26195         var m = [
26196              '<table cellspacing="0">',
26197                 '<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>',
26198                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26199         var dn = this.dayNames;
26200         for(var i = 0; i < 7; i++){
26201             var d = this.startDay+i;
26202             if(d > 6){
26203                 d = d-7;
26204             }
26205             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26206         }
26207         m[m.length] = "</tr></thead><tbody><tr>";
26208         for(var i = 0; i < 42; i++) {
26209             if(i % 7 == 0 && i != 0){
26210                 m[m.length] = "</tr><tr>";
26211             }
26212             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26213         }
26214         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26215             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26216
26217         var el = document.createElement("div");
26218         el.className = "x-date-picker";
26219         el.innerHTML = m.join("");
26220
26221         container.dom.insertBefore(el, position);
26222
26223         this.el = Roo.get(el);
26224         this.eventEl = Roo.get(el.firstChild);
26225
26226         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26227             handler: this.showPrevMonth,
26228             scope: this,
26229             preventDefault:true,
26230             stopDefault:true
26231         });
26232
26233         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26234             handler: this.showNextMonth,
26235             scope: this,
26236             preventDefault:true,
26237             stopDefault:true
26238         });
26239
26240         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26241
26242         this.monthPicker = this.el.down('div.x-date-mp');
26243         this.monthPicker.enableDisplayMode('block');
26244         
26245         var kn = new Roo.KeyNav(this.eventEl, {
26246             "left" : function(e){
26247                 e.ctrlKey ?
26248                     this.showPrevMonth() :
26249                     this.update(this.activeDate.add("d", -1));
26250             },
26251
26252             "right" : function(e){
26253                 e.ctrlKey ?
26254                     this.showNextMonth() :
26255                     this.update(this.activeDate.add("d", 1));
26256             },
26257
26258             "up" : function(e){
26259                 e.ctrlKey ?
26260                     this.showNextYear() :
26261                     this.update(this.activeDate.add("d", -7));
26262             },
26263
26264             "down" : function(e){
26265                 e.ctrlKey ?
26266                     this.showPrevYear() :
26267                     this.update(this.activeDate.add("d", 7));
26268             },
26269
26270             "pageUp" : function(e){
26271                 this.showNextMonth();
26272             },
26273
26274             "pageDown" : function(e){
26275                 this.showPrevMonth();
26276             },
26277
26278             "enter" : function(e){
26279                 e.stopPropagation();
26280                 return true;
26281             },
26282
26283             scope : this
26284         });
26285
26286         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26287
26288         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26289
26290         this.el.unselectable();
26291         
26292         this.cells = this.el.select("table.x-date-inner tbody td");
26293         this.textNodes = this.el.query("table.x-date-inner tbody span");
26294
26295         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26296             text: "&#160;",
26297             tooltip: this.monthYearText
26298         });
26299
26300         this.mbtn.on('click', this.showMonthPicker, this);
26301         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26302
26303
26304         var today = (new Date()).dateFormat(this.format);
26305         
26306         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26307         if (this.showClear) {
26308             baseTb.add( new Roo.Toolbar.Fill());
26309         }
26310         baseTb.add({
26311             text: String.format(this.todayText, today),
26312             tooltip: String.format(this.todayTip, today),
26313             handler: this.selectToday,
26314             scope: this
26315         });
26316         
26317         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26318             
26319         //});
26320         if (this.showClear) {
26321             
26322             baseTb.add( new Roo.Toolbar.Fill());
26323             baseTb.add({
26324                 text: '&#160;',
26325                 cls: 'x-btn-icon x-btn-clear',
26326                 handler: function() {
26327                     //this.value = '';
26328                     this.fireEvent("select", this, '');
26329                 },
26330                 scope: this
26331             });
26332         }
26333         
26334         
26335         if(Roo.isIE){
26336             this.el.repaint();
26337         }
26338         this.update(this.value);
26339     },
26340
26341     createMonthPicker : function(){
26342         if(!this.monthPicker.dom.firstChild){
26343             var buf = ['<table border="0" cellspacing="0">'];
26344             for(var i = 0; i < 6; i++){
26345                 buf.push(
26346                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26347                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26348                     i == 0 ?
26349                     '<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>' :
26350                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26351                 );
26352             }
26353             buf.push(
26354                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26355                     this.okText,
26356                     '</button><button type="button" class="x-date-mp-cancel">',
26357                     this.cancelText,
26358                     '</button></td></tr>',
26359                 '</table>'
26360             );
26361             this.monthPicker.update(buf.join(''));
26362             this.monthPicker.on('click', this.onMonthClick, this);
26363             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26364
26365             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26366             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26367
26368             this.mpMonths.each(function(m, a, i){
26369                 i += 1;
26370                 if((i%2) == 0){
26371                     m.dom.xmonth = 5 + Math.round(i * .5);
26372                 }else{
26373                     m.dom.xmonth = Math.round((i-1) * .5);
26374                 }
26375             });
26376         }
26377     },
26378
26379     showMonthPicker : function(){
26380         this.createMonthPicker();
26381         var size = this.el.getSize();
26382         this.monthPicker.setSize(size);
26383         this.monthPicker.child('table').setSize(size);
26384
26385         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26386         this.updateMPMonth(this.mpSelMonth);
26387         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26388         this.updateMPYear(this.mpSelYear);
26389
26390         this.monthPicker.slideIn('t', {duration:.2});
26391     },
26392
26393     updateMPYear : function(y){
26394         this.mpyear = y;
26395         var ys = this.mpYears.elements;
26396         for(var i = 1; i <= 10; i++){
26397             var td = ys[i-1], y2;
26398             if((i%2) == 0){
26399                 y2 = y + Math.round(i * .5);
26400                 td.firstChild.innerHTML = y2;
26401                 td.xyear = y2;
26402             }else{
26403                 y2 = y - (5-Math.round(i * .5));
26404                 td.firstChild.innerHTML = y2;
26405                 td.xyear = y2;
26406             }
26407             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26408         }
26409     },
26410
26411     updateMPMonth : function(sm){
26412         this.mpMonths.each(function(m, a, i){
26413             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26414         });
26415     },
26416
26417     selectMPMonth: function(m){
26418         
26419     },
26420
26421     onMonthClick : function(e, t){
26422         e.stopEvent();
26423         var el = new Roo.Element(t), pn;
26424         if(el.is('button.x-date-mp-cancel')){
26425             this.hideMonthPicker();
26426         }
26427         else if(el.is('button.x-date-mp-ok')){
26428             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26429             this.hideMonthPicker();
26430         }
26431         else if(pn = el.up('td.x-date-mp-month', 2)){
26432             this.mpMonths.removeClass('x-date-mp-sel');
26433             pn.addClass('x-date-mp-sel');
26434             this.mpSelMonth = pn.dom.xmonth;
26435         }
26436         else if(pn = el.up('td.x-date-mp-year', 2)){
26437             this.mpYears.removeClass('x-date-mp-sel');
26438             pn.addClass('x-date-mp-sel');
26439             this.mpSelYear = pn.dom.xyear;
26440         }
26441         else if(el.is('a.x-date-mp-prev')){
26442             this.updateMPYear(this.mpyear-10);
26443         }
26444         else if(el.is('a.x-date-mp-next')){
26445             this.updateMPYear(this.mpyear+10);
26446         }
26447     },
26448
26449     onMonthDblClick : function(e, t){
26450         e.stopEvent();
26451         var el = new Roo.Element(t), pn;
26452         if(pn = el.up('td.x-date-mp-month', 2)){
26453             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26454             this.hideMonthPicker();
26455         }
26456         else if(pn = el.up('td.x-date-mp-year', 2)){
26457             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26458             this.hideMonthPicker();
26459         }
26460     },
26461
26462     hideMonthPicker : function(disableAnim){
26463         if(this.monthPicker){
26464             if(disableAnim === true){
26465                 this.monthPicker.hide();
26466             }else{
26467                 this.monthPicker.slideOut('t', {duration:.2});
26468             }
26469         }
26470     },
26471
26472     // private
26473     showPrevMonth : function(e){
26474         this.update(this.activeDate.add("mo", -1));
26475     },
26476
26477     // private
26478     showNextMonth : function(e){
26479         this.update(this.activeDate.add("mo", 1));
26480     },
26481
26482     // private
26483     showPrevYear : function(){
26484         this.update(this.activeDate.add("y", -1));
26485     },
26486
26487     // private
26488     showNextYear : function(){
26489         this.update(this.activeDate.add("y", 1));
26490     },
26491
26492     // private
26493     handleMouseWheel : function(e){
26494         var delta = e.getWheelDelta();
26495         if(delta > 0){
26496             this.showPrevMonth();
26497             e.stopEvent();
26498         } else if(delta < 0){
26499             this.showNextMonth();
26500             e.stopEvent();
26501         }
26502     },
26503
26504     // private
26505     handleDateClick : function(e, t){
26506         e.stopEvent();
26507         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26508             this.setValue(new Date(t.dateValue));
26509             this.fireEvent("select", this, this.value);
26510         }
26511     },
26512
26513     // private
26514     selectToday : function(){
26515         this.setValue(new Date().clearTime());
26516         this.fireEvent("select", this, this.value);
26517     },
26518
26519     // private
26520     update : function(date)
26521     {
26522         var vd = this.activeDate;
26523         this.activeDate = date;
26524         if(vd && this.el){
26525             var t = date.getTime();
26526             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26527                 this.cells.removeClass("x-date-selected");
26528                 this.cells.each(function(c){
26529                    if(c.dom.firstChild.dateValue == t){
26530                        c.addClass("x-date-selected");
26531                        setTimeout(function(){
26532                             try{c.dom.firstChild.focus();}catch(e){}
26533                        }, 50);
26534                        return false;
26535                    }
26536                 });
26537                 return;
26538             }
26539         }
26540         
26541         var days = date.getDaysInMonth();
26542         var firstOfMonth = date.getFirstDateOfMonth();
26543         var startingPos = firstOfMonth.getDay()-this.startDay;
26544
26545         if(startingPos <= this.startDay){
26546             startingPos += 7;
26547         }
26548
26549         var pm = date.add("mo", -1);
26550         var prevStart = pm.getDaysInMonth()-startingPos;
26551
26552         var cells = this.cells.elements;
26553         var textEls = this.textNodes;
26554         days += startingPos;
26555
26556         // convert everything to numbers so it's fast
26557         var day = 86400000;
26558         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26559         var today = new Date().clearTime().getTime();
26560         var sel = date.clearTime().getTime();
26561         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26562         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26563         var ddMatch = this.disabledDatesRE;
26564         var ddText = this.disabledDatesText;
26565         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26566         var ddaysText = this.disabledDaysText;
26567         var format = this.format;
26568
26569         var setCellClass = function(cal, cell){
26570             cell.title = "";
26571             var t = d.getTime();
26572             cell.firstChild.dateValue = t;
26573             if(t == today){
26574                 cell.className += " x-date-today";
26575                 cell.title = cal.todayText;
26576             }
26577             if(t == sel){
26578                 cell.className += " x-date-selected";
26579                 setTimeout(function(){
26580                     try{cell.firstChild.focus();}catch(e){}
26581                 }, 50);
26582             }
26583             // disabling
26584             if(t < min) {
26585                 cell.className = " x-date-disabled";
26586                 cell.title = cal.minText;
26587                 return;
26588             }
26589             if(t > max) {
26590                 cell.className = " x-date-disabled";
26591                 cell.title = cal.maxText;
26592                 return;
26593             }
26594             if(ddays){
26595                 if(ddays.indexOf(d.getDay()) != -1){
26596                     cell.title = ddaysText;
26597                     cell.className = " x-date-disabled";
26598                 }
26599             }
26600             if(ddMatch && format){
26601                 var fvalue = d.dateFormat(format);
26602                 if(ddMatch.test(fvalue)){
26603                     cell.title = ddText.replace("%0", fvalue);
26604                     cell.className = " x-date-disabled";
26605                 }
26606             }
26607         };
26608
26609         var i = 0;
26610         for(; i < startingPos; i++) {
26611             textEls[i].innerHTML = (++prevStart);
26612             d.setDate(d.getDate()+1);
26613             cells[i].className = "x-date-prevday";
26614             setCellClass(this, cells[i]);
26615         }
26616         for(; i < days; i++){
26617             intDay = i - startingPos + 1;
26618             textEls[i].innerHTML = (intDay);
26619             d.setDate(d.getDate()+1);
26620             cells[i].className = "x-date-active";
26621             setCellClass(this, cells[i]);
26622         }
26623         var extraDays = 0;
26624         for(; i < 42; i++) {
26625              textEls[i].innerHTML = (++extraDays);
26626              d.setDate(d.getDate()+1);
26627              cells[i].className = "x-date-nextday";
26628              setCellClass(this, cells[i]);
26629         }
26630
26631         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26632         this.fireEvent('monthchange', this, date);
26633         
26634         if(!this.internalRender){
26635             var main = this.el.dom.firstChild;
26636             var w = main.offsetWidth;
26637             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26638             Roo.fly(main).setWidth(w);
26639             this.internalRender = true;
26640             // opera does not respect the auto grow header center column
26641             // then, after it gets a width opera refuses to recalculate
26642             // without a second pass
26643             if(Roo.isOpera && !this.secondPass){
26644                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26645                 this.secondPass = true;
26646                 this.update.defer(10, this, [date]);
26647             }
26648         }
26649         
26650         
26651     }
26652 });        /*
26653  * Based on:
26654  * Ext JS Library 1.1.1
26655  * Copyright(c) 2006-2007, Ext JS, LLC.
26656  *
26657  * Originally Released Under LGPL - original licence link has changed is not relivant.
26658  *
26659  * Fork - LGPL
26660  * <script type="text/javascript">
26661  */
26662 /**
26663  * @class Roo.TabPanel
26664  * @extends Roo.util.Observable
26665  * A lightweight tab container.
26666  * <br><br>
26667  * Usage:
26668  * <pre><code>
26669 // basic tabs 1, built from existing content
26670 var tabs = new Roo.TabPanel("tabs1");
26671 tabs.addTab("script", "View Script");
26672 tabs.addTab("markup", "View Markup");
26673 tabs.activate("script");
26674
26675 // more advanced tabs, built from javascript
26676 var jtabs = new Roo.TabPanel("jtabs");
26677 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26678
26679 // set up the UpdateManager
26680 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26681 var updater = tab2.getUpdateManager();
26682 updater.setDefaultUrl("ajax1.htm");
26683 tab2.on('activate', updater.refresh, updater, true);
26684
26685 // Use setUrl for Ajax loading
26686 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26687 tab3.setUrl("ajax2.htm", null, true);
26688
26689 // Disabled tab
26690 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26691 tab4.disable();
26692
26693 jtabs.activate("jtabs-1");
26694  * </code></pre>
26695  * @constructor
26696  * Create a new TabPanel.
26697  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26698  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26699  */
26700 Roo.TabPanel = function(container, config){
26701     /**
26702     * The container element for this TabPanel.
26703     * @type Roo.Element
26704     */
26705     this.el = Roo.get(container, true);
26706     if(config){
26707         if(typeof config == "boolean"){
26708             this.tabPosition = config ? "bottom" : "top";
26709         }else{
26710             Roo.apply(this, config);
26711         }
26712     }
26713     if(this.tabPosition == "bottom"){
26714         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26715         this.el.addClass("x-tabs-bottom");
26716     }
26717     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26718     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26719     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26720     if(Roo.isIE){
26721         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26722     }
26723     if(this.tabPosition != "bottom"){
26724         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26725          * @type Roo.Element
26726          */
26727         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26728         this.el.addClass("x-tabs-top");
26729     }
26730     this.items = [];
26731
26732     this.bodyEl.setStyle("position", "relative");
26733
26734     this.active = null;
26735     this.activateDelegate = this.activate.createDelegate(this);
26736
26737     this.addEvents({
26738         /**
26739          * @event tabchange
26740          * Fires when the active tab changes
26741          * @param {Roo.TabPanel} this
26742          * @param {Roo.TabPanelItem} activePanel The new active tab
26743          */
26744         "tabchange": true,
26745         /**
26746          * @event beforetabchange
26747          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26748          * @param {Roo.TabPanel} this
26749          * @param {Object} e Set cancel to true on this object to cancel the tab change
26750          * @param {Roo.TabPanelItem} tab The tab being changed to
26751          */
26752         "beforetabchange" : true
26753     });
26754
26755     Roo.EventManager.onWindowResize(this.onResize, this);
26756     this.cpad = this.el.getPadding("lr");
26757     this.hiddenCount = 0;
26758
26759
26760     // toolbar on the tabbar support...
26761     if (this.toolbar) {
26762         var tcfg = this.toolbar;
26763         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26764         this.toolbar = new Roo.Toolbar(tcfg);
26765         if (Roo.isSafari) {
26766             var tbl = tcfg.container.child('table', true);
26767             tbl.setAttribute('width', '100%');
26768         }
26769         
26770     }
26771    
26772
26773
26774     Roo.TabPanel.superclass.constructor.call(this);
26775 };
26776
26777 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26778     /*
26779      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26780      */
26781     tabPosition : "top",
26782     /*
26783      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26784      */
26785     currentTabWidth : 0,
26786     /*
26787      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26788      */
26789     minTabWidth : 40,
26790     /*
26791      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26792      */
26793     maxTabWidth : 250,
26794     /*
26795      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26796      */
26797     preferredTabWidth : 175,
26798     /*
26799      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26800      */
26801     resizeTabs : false,
26802     /*
26803      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26804      */
26805     monitorResize : true,
26806     /*
26807      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26808      */
26809     toolbar : false,
26810
26811     /**
26812      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26813      * @param {String} id The id of the div to use <b>or create</b>
26814      * @param {String} text The text for the tab
26815      * @param {String} content (optional) Content to put in the TabPanelItem body
26816      * @param {Boolean} closable (optional) True to create a close icon on the tab
26817      * @return {Roo.TabPanelItem} The created TabPanelItem
26818      */
26819     addTab : function(id, text, content, closable){
26820         var item = new Roo.TabPanelItem(this, id, text, closable);
26821         this.addTabItem(item);
26822         if(content){
26823             item.setContent(content);
26824         }
26825         return item;
26826     },
26827
26828     /**
26829      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26830      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26831      * @return {Roo.TabPanelItem}
26832      */
26833     getTab : function(id){
26834         return this.items[id];
26835     },
26836
26837     /**
26838      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26839      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26840      */
26841     hideTab : function(id){
26842         var t = this.items[id];
26843         if(!t.isHidden()){
26844            t.setHidden(true);
26845            this.hiddenCount++;
26846            this.autoSizeTabs();
26847         }
26848     },
26849
26850     /**
26851      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26852      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26853      */
26854     unhideTab : function(id){
26855         var t = this.items[id];
26856         if(t.isHidden()){
26857            t.setHidden(false);
26858            this.hiddenCount--;
26859            this.autoSizeTabs();
26860         }
26861     },
26862
26863     /**
26864      * Adds an existing {@link Roo.TabPanelItem}.
26865      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26866      */
26867     addTabItem : function(item){
26868         this.items[item.id] = item;
26869         this.items.push(item);
26870         if(this.resizeTabs){
26871            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26872            this.autoSizeTabs();
26873         }else{
26874             item.autoSize();
26875         }
26876     },
26877
26878     /**
26879      * Removes a {@link Roo.TabPanelItem}.
26880      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26881      */
26882     removeTab : function(id){
26883         var items = this.items;
26884         var tab = items[id];
26885         if(!tab) { return; }
26886         var index = items.indexOf(tab);
26887         if(this.active == tab && items.length > 1){
26888             var newTab = this.getNextAvailable(index);
26889             if(newTab) {
26890                 newTab.activate();
26891             }
26892         }
26893         this.stripEl.dom.removeChild(tab.pnode.dom);
26894         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26895             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26896         }
26897         items.splice(index, 1);
26898         delete this.items[tab.id];
26899         tab.fireEvent("close", tab);
26900         tab.purgeListeners();
26901         this.autoSizeTabs();
26902     },
26903
26904     getNextAvailable : function(start){
26905         var items = this.items;
26906         var index = start;
26907         // look for a next tab that will slide over to
26908         // replace the one being removed
26909         while(index < items.length){
26910             var item = items[++index];
26911             if(item && !item.isHidden()){
26912                 return item;
26913             }
26914         }
26915         // if one isn't found select the previous tab (on the left)
26916         index = start;
26917         while(index >= 0){
26918             var item = items[--index];
26919             if(item && !item.isHidden()){
26920                 return item;
26921             }
26922         }
26923         return null;
26924     },
26925
26926     /**
26927      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26928      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26929      */
26930     disableTab : function(id){
26931         var tab = this.items[id];
26932         if(tab && this.active != tab){
26933             tab.disable();
26934         }
26935     },
26936
26937     /**
26938      * Enables a {@link Roo.TabPanelItem} that is disabled.
26939      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26940      */
26941     enableTab : function(id){
26942         var tab = this.items[id];
26943         tab.enable();
26944     },
26945
26946     /**
26947      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26948      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26949      * @return {Roo.TabPanelItem} The TabPanelItem.
26950      */
26951     activate : function(id){
26952         var tab = this.items[id];
26953         if(!tab){
26954             return null;
26955         }
26956         if(tab == this.active || tab.disabled){
26957             return tab;
26958         }
26959         var e = {};
26960         this.fireEvent("beforetabchange", this, e, tab);
26961         if(e.cancel !== true && !tab.disabled){
26962             if(this.active){
26963                 this.active.hide();
26964             }
26965             this.active = this.items[id];
26966             this.active.show();
26967             this.fireEvent("tabchange", this, this.active);
26968         }
26969         return tab;
26970     },
26971
26972     /**
26973      * Gets the active {@link Roo.TabPanelItem}.
26974      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26975      */
26976     getActiveTab : function(){
26977         return this.active;
26978     },
26979
26980     /**
26981      * Updates the tab body element to fit the height of the container element
26982      * for overflow scrolling
26983      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26984      */
26985     syncHeight : function(targetHeight){
26986         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26987         var bm = this.bodyEl.getMargins();
26988         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26989         this.bodyEl.setHeight(newHeight);
26990         return newHeight;
26991     },
26992
26993     onResize : function(){
26994         if(this.monitorResize){
26995             this.autoSizeTabs();
26996         }
26997     },
26998
26999     /**
27000      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
27001      */
27002     beginUpdate : function(){
27003         this.updating = true;
27004     },
27005
27006     /**
27007      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
27008      */
27009     endUpdate : function(){
27010         this.updating = false;
27011         this.autoSizeTabs();
27012     },
27013
27014     /**
27015      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
27016      */
27017     autoSizeTabs : function(){
27018         var count = this.items.length;
27019         var vcount = count - this.hiddenCount;
27020         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
27021         var w = Math.max(this.el.getWidth() - this.cpad, 10);
27022         var availWidth = Math.floor(w / vcount);
27023         var b = this.stripBody;
27024         if(b.getWidth() > w){
27025             var tabs = this.items;
27026             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
27027             if(availWidth < this.minTabWidth){
27028                 /*if(!this.sleft){    // incomplete scrolling code
27029                     this.createScrollButtons();
27030                 }
27031                 this.showScroll();
27032                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
27033             }
27034         }else{
27035             if(this.currentTabWidth < this.preferredTabWidth){
27036                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
27037             }
27038         }
27039     },
27040
27041     /**
27042      * Returns the number of tabs in this TabPanel.
27043      * @return {Number}
27044      */
27045      getCount : function(){
27046          return this.items.length;
27047      },
27048
27049     /**
27050      * Resizes all the tabs to the passed width
27051      * @param {Number} The new width
27052      */
27053     setTabWidth : function(width){
27054         this.currentTabWidth = width;
27055         for(var i = 0, len = this.items.length; i < len; i++) {
27056                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
27057         }
27058     },
27059
27060     /**
27061      * Destroys this TabPanel
27062      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
27063      */
27064     destroy : function(removeEl){
27065         Roo.EventManager.removeResizeListener(this.onResize, this);
27066         for(var i = 0, len = this.items.length; i < len; i++){
27067             this.items[i].purgeListeners();
27068         }
27069         if(removeEl === true){
27070             this.el.update("");
27071             this.el.remove();
27072         }
27073     }
27074 });
27075
27076 /**
27077  * @class Roo.TabPanelItem
27078  * @extends Roo.util.Observable
27079  * Represents an individual item (tab plus body) in a TabPanel.
27080  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
27081  * @param {String} id The id of this TabPanelItem
27082  * @param {String} text The text for the tab of this TabPanelItem
27083  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
27084  */
27085 Roo.TabPanelItem = function(tabPanel, id, text, closable){
27086     /**
27087      * The {@link Roo.TabPanel} this TabPanelItem belongs to
27088      * @type Roo.TabPanel
27089      */
27090     this.tabPanel = tabPanel;
27091     /**
27092      * The id for this TabPanelItem
27093      * @type String
27094      */
27095     this.id = id;
27096     /** @private */
27097     this.disabled = false;
27098     /** @private */
27099     this.text = text;
27100     /** @private */
27101     this.loaded = false;
27102     this.closable = closable;
27103
27104     /**
27105      * The body element for this TabPanelItem.
27106      * @type Roo.Element
27107      */
27108     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
27109     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
27110     this.bodyEl.setStyle("display", "block");
27111     this.bodyEl.setStyle("zoom", "1");
27112     this.hideAction();
27113
27114     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
27115     /** @private */
27116     this.el = Roo.get(els.el, true);
27117     this.inner = Roo.get(els.inner, true);
27118     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
27119     this.pnode = Roo.get(els.el.parentNode, true);
27120     this.el.on("mousedown", this.onTabMouseDown, this);
27121     this.el.on("click", this.onTabClick, this);
27122     /** @private */
27123     if(closable){
27124         var c = Roo.get(els.close, true);
27125         c.dom.title = this.closeText;
27126         c.addClassOnOver("close-over");
27127         c.on("click", this.closeClick, this);
27128      }
27129
27130     this.addEvents({
27131          /**
27132          * @event activate
27133          * Fires when this tab becomes the active tab.
27134          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27135          * @param {Roo.TabPanelItem} this
27136          */
27137         "activate": true,
27138         /**
27139          * @event beforeclose
27140          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
27141          * @param {Roo.TabPanelItem} this
27142          * @param {Object} e Set cancel to true on this object to cancel the close.
27143          */
27144         "beforeclose": true,
27145         /**
27146          * @event close
27147          * Fires when this tab is closed.
27148          * @param {Roo.TabPanelItem} this
27149          */
27150          "close": true,
27151         /**
27152          * @event deactivate
27153          * Fires when this tab is no longer the active tab.
27154          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27155          * @param {Roo.TabPanelItem} this
27156          */
27157          "deactivate" : true
27158     });
27159     this.hidden = false;
27160
27161     Roo.TabPanelItem.superclass.constructor.call(this);
27162 };
27163
27164 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27165     purgeListeners : function(){
27166        Roo.util.Observable.prototype.purgeListeners.call(this);
27167        this.el.removeAllListeners();
27168     },
27169     /**
27170      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27171      */
27172     show : function(){
27173         this.pnode.addClass("on");
27174         this.showAction();
27175         if(Roo.isOpera){
27176             this.tabPanel.stripWrap.repaint();
27177         }
27178         this.fireEvent("activate", this.tabPanel, this);
27179     },
27180
27181     /**
27182      * Returns true if this tab is the active tab.
27183      * @return {Boolean}
27184      */
27185     isActive : function(){
27186         return this.tabPanel.getActiveTab() == this;
27187     },
27188
27189     /**
27190      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27191      */
27192     hide : function(){
27193         this.pnode.removeClass("on");
27194         this.hideAction();
27195         this.fireEvent("deactivate", this.tabPanel, this);
27196     },
27197
27198     hideAction : function(){
27199         this.bodyEl.hide();
27200         this.bodyEl.setStyle("position", "absolute");
27201         this.bodyEl.setLeft("-20000px");
27202         this.bodyEl.setTop("-20000px");
27203     },
27204
27205     showAction : function(){
27206         this.bodyEl.setStyle("position", "relative");
27207         this.bodyEl.setTop("");
27208         this.bodyEl.setLeft("");
27209         this.bodyEl.show();
27210     },
27211
27212     /**
27213      * Set the tooltip for the tab.
27214      * @param {String} tooltip The tab's tooltip
27215      */
27216     setTooltip : function(text){
27217         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27218             this.textEl.dom.qtip = text;
27219             this.textEl.dom.removeAttribute('title');
27220         }else{
27221             this.textEl.dom.title = text;
27222         }
27223     },
27224
27225     onTabClick : function(e){
27226         e.preventDefault();
27227         this.tabPanel.activate(this.id);
27228     },
27229
27230     onTabMouseDown : function(e){
27231         e.preventDefault();
27232         this.tabPanel.activate(this.id);
27233     },
27234
27235     getWidth : function(){
27236         return this.inner.getWidth();
27237     },
27238
27239     setWidth : function(width){
27240         var iwidth = width - this.pnode.getPadding("lr");
27241         this.inner.setWidth(iwidth);
27242         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27243         this.pnode.setWidth(width);
27244     },
27245
27246     /**
27247      * Show or hide the tab
27248      * @param {Boolean} hidden True to hide or false to show.
27249      */
27250     setHidden : function(hidden){
27251         this.hidden = hidden;
27252         this.pnode.setStyle("display", hidden ? "none" : "");
27253     },
27254
27255     /**
27256      * Returns true if this tab is "hidden"
27257      * @return {Boolean}
27258      */
27259     isHidden : function(){
27260         return this.hidden;
27261     },
27262
27263     /**
27264      * Returns the text for this tab
27265      * @return {String}
27266      */
27267     getText : function(){
27268         return this.text;
27269     },
27270
27271     autoSize : function(){
27272         //this.el.beginMeasure();
27273         this.textEl.setWidth(1);
27274         /*
27275          *  #2804 [new] Tabs in Roojs
27276          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
27277          */
27278         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
27279         //this.el.endMeasure();
27280     },
27281
27282     /**
27283      * Sets the text for the tab (Note: this also sets the tooltip text)
27284      * @param {String} text The tab's text and tooltip
27285      */
27286     setText : function(text){
27287         this.text = text;
27288         this.textEl.update(text);
27289         this.setTooltip(text);
27290         if(!this.tabPanel.resizeTabs){
27291             this.autoSize();
27292         }
27293     },
27294     /**
27295      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27296      */
27297     activate : function(){
27298         this.tabPanel.activate(this.id);
27299     },
27300
27301     /**
27302      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27303      */
27304     disable : function(){
27305         if(this.tabPanel.active != this){
27306             this.disabled = true;
27307             this.pnode.addClass("disabled");
27308         }
27309     },
27310
27311     /**
27312      * Enables this TabPanelItem if it was previously disabled.
27313      */
27314     enable : function(){
27315         this.disabled = false;
27316         this.pnode.removeClass("disabled");
27317     },
27318
27319     /**
27320      * Sets the content for this TabPanelItem.
27321      * @param {String} content The content
27322      * @param {Boolean} loadScripts true to look for and load scripts
27323      */
27324     setContent : function(content, loadScripts){
27325         this.bodyEl.update(content, loadScripts);
27326     },
27327
27328     /**
27329      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27330      * @return {Roo.UpdateManager} The UpdateManager
27331      */
27332     getUpdateManager : function(){
27333         return this.bodyEl.getUpdateManager();
27334     },
27335
27336     /**
27337      * Set a URL to be used to load the content for this TabPanelItem.
27338      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27339      * @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)
27340      * @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)
27341      * @return {Roo.UpdateManager} The UpdateManager
27342      */
27343     setUrl : function(url, params, loadOnce){
27344         if(this.refreshDelegate){
27345             this.un('activate', this.refreshDelegate);
27346         }
27347         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27348         this.on("activate", this.refreshDelegate);
27349         return this.bodyEl.getUpdateManager();
27350     },
27351
27352     /** @private */
27353     _handleRefresh : function(url, params, loadOnce){
27354         if(!loadOnce || !this.loaded){
27355             var updater = this.bodyEl.getUpdateManager();
27356             updater.update(url, params, this._setLoaded.createDelegate(this));
27357         }
27358     },
27359
27360     /**
27361      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27362      *   Will fail silently if the setUrl method has not been called.
27363      *   This does not activate the panel, just updates its content.
27364      */
27365     refresh : function(){
27366         if(this.refreshDelegate){
27367            this.loaded = false;
27368            this.refreshDelegate();
27369         }
27370     },
27371
27372     /** @private */
27373     _setLoaded : function(){
27374         this.loaded = true;
27375     },
27376
27377     /** @private */
27378     closeClick : function(e){
27379         var o = {};
27380         e.stopEvent();
27381         this.fireEvent("beforeclose", this, o);
27382         if(o.cancel !== true){
27383             this.tabPanel.removeTab(this.id);
27384         }
27385     },
27386     /**
27387      * The text displayed in the tooltip for the close icon.
27388      * @type String
27389      */
27390     closeText : "Close this tab"
27391 });
27392
27393 /** @private */
27394 Roo.TabPanel.prototype.createStrip = function(container){
27395     var strip = document.createElement("div");
27396     strip.className = "x-tabs-wrap";
27397     container.appendChild(strip);
27398     return strip;
27399 };
27400 /** @private */
27401 Roo.TabPanel.prototype.createStripList = function(strip){
27402     // div wrapper for retard IE
27403     // returns the "tr" element.
27404     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27405         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27406         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27407     return strip.firstChild.firstChild.firstChild.firstChild;
27408 };
27409 /** @private */
27410 Roo.TabPanel.prototype.createBody = function(container){
27411     var body = document.createElement("div");
27412     Roo.id(body, "tab-body");
27413     Roo.fly(body).addClass("x-tabs-body");
27414     container.appendChild(body);
27415     return body;
27416 };
27417 /** @private */
27418 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27419     var body = Roo.getDom(id);
27420     if(!body){
27421         body = document.createElement("div");
27422         body.id = id;
27423     }
27424     Roo.fly(body).addClass("x-tabs-item-body");
27425     bodyEl.insertBefore(body, bodyEl.firstChild);
27426     return body;
27427 };
27428 /** @private */
27429 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27430     var td = document.createElement("td");
27431     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27432     //stripEl.appendChild(td);
27433     if(closable){
27434         td.className = "x-tabs-closable";
27435         if(!this.closeTpl){
27436             this.closeTpl = new Roo.Template(
27437                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27438                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27439                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27440             );
27441         }
27442         var el = this.closeTpl.overwrite(td, {"text": text});
27443         var close = el.getElementsByTagName("div")[0];
27444         var inner = el.getElementsByTagName("em")[0];
27445         return {"el": el, "close": close, "inner": inner};
27446     } else {
27447         if(!this.tabTpl){
27448             this.tabTpl = new Roo.Template(
27449                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27450                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27451             );
27452         }
27453         var el = this.tabTpl.overwrite(td, {"text": text});
27454         var inner = el.getElementsByTagName("em")[0];
27455         return {"el": el, "inner": inner};
27456     }
27457 };/*
27458  * Based on:
27459  * Ext JS Library 1.1.1
27460  * Copyright(c) 2006-2007, Ext JS, LLC.
27461  *
27462  * Originally Released Under LGPL - original licence link has changed is not relivant.
27463  *
27464  * Fork - LGPL
27465  * <script type="text/javascript">
27466  */
27467
27468 /**
27469  * @class Roo.Button
27470  * @extends Roo.util.Observable
27471  * Simple Button class
27472  * @cfg {String} text The button text
27473  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27474  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27475  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27476  * @cfg {Object} scope The scope of the handler
27477  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27478  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27479  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27480  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27481  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27482  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27483    applies if enableToggle = true)
27484  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27485  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27486   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27487  * @constructor
27488  * Create a new button
27489  * @param {Object} config The config object
27490  */
27491 Roo.Button = function(renderTo, config)
27492 {
27493     if (!config) {
27494         config = renderTo;
27495         renderTo = config.renderTo || false;
27496     }
27497     
27498     Roo.apply(this, config);
27499     this.addEvents({
27500         /**
27501              * @event click
27502              * Fires when this button is clicked
27503              * @param {Button} this
27504              * @param {EventObject} e The click event
27505              */
27506             "click" : true,
27507         /**
27508              * @event toggle
27509              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27510              * @param {Button} this
27511              * @param {Boolean} pressed
27512              */
27513             "toggle" : true,
27514         /**
27515              * @event mouseover
27516              * Fires when the mouse hovers over the button
27517              * @param {Button} this
27518              * @param {Event} e The event object
27519              */
27520         'mouseover' : true,
27521         /**
27522              * @event mouseout
27523              * Fires when the mouse exits the button
27524              * @param {Button} this
27525              * @param {Event} e The event object
27526              */
27527         'mouseout': true,
27528          /**
27529              * @event render
27530              * Fires when the button is rendered
27531              * @param {Button} this
27532              */
27533         'render': true
27534     });
27535     if(this.menu){
27536         this.menu = Roo.menu.MenuMgr.get(this.menu);
27537     }
27538     // register listeners first!!  - so render can be captured..
27539     Roo.util.Observable.call(this);
27540     if(renderTo){
27541         this.render(renderTo);
27542     }
27543     
27544   
27545 };
27546
27547 Roo.extend(Roo.Button, Roo.util.Observable, {
27548     /**
27549      * 
27550      */
27551     
27552     /**
27553      * Read-only. True if this button is hidden
27554      * @type Boolean
27555      */
27556     hidden : false,
27557     /**
27558      * Read-only. True if this button is disabled
27559      * @type Boolean
27560      */
27561     disabled : false,
27562     /**
27563      * Read-only. True if this button is pressed (only if enableToggle = true)
27564      * @type Boolean
27565      */
27566     pressed : false,
27567
27568     /**
27569      * @cfg {Number} tabIndex 
27570      * The DOM tabIndex for this button (defaults to undefined)
27571      */
27572     tabIndex : undefined,
27573
27574     /**
27575      * @cfg {Boolean} enableToggle
27576      * True to enable pressed/not pressed toggling (defaults to false)
27577      */
27578     enableToggle: false,
27579     /**
27580      * @cfg {Mixed} menu
27581      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27582      */
27583     menu : undefined,
27584     /**
27585      * @cfg {String} menuAlign
27586      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27587      */
27588     menuAlign : "tl-bl?",
27589
27590     /**
27591      * @cfg {String} iconCls
27592      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27593      */
27594     iconCls : undefined,
27595     /**
27596      * @cfg {String} type
27597      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27598      */
27599     type : 'button',
27600
27601     // private
27602     menuClassTarget: 'tr',
27603
27604     /**
27605      * @cfg {String} clickEvent
27606      * The type of event to map to the button's event handler (defaults to 'click')
27607      */
27608     clickEvent : 'click',
27609
27610     /**
27611      * @cfg {Boolean} handleMouseEvents
27612      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27613      */
27614     handleMouseEvents : true,
27615
27616     /**
27617      * @cfg {String} tooltipType
27618      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27619      */
27620     tooltipType : 'qtip',
27621
27622     /**
27623      * @cfg {String} cls
27624      * A CSS class to apply to the button's main element.
27625      */
27626     
27627     /**
27628      * @cfg {Roo.Template} template (Optional)
27629      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27630      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27631      * require code modifications if required elements (e.g. a button) aren't present.
27632      */
27633
27634     // private
27635     render : function(renderTo){
27636         var btn;
27637         if(this.hideParent){
27638             this.parentEl = Roo.get(renderTo);
27639         }
27640         if(!this.dhconfig){
27641             if(!this.template){
27642                 if(!Roo.Button.buttonTemplate){
27643                     // hideous table template
27644                     Roo.Button.buttonTemplate = new Roo.Template(
27645                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27646                         '<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>',
27647                         "</tr></tbody></table>");
27648                 }
27649                 this.template = Roo.Button.buttonTemplate;
27650             }
27651             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27652             var btnEl = btn.child("button:first");
27653             btnEl.on('focus', this.onFocus, this);
27654             btnEl.on('blur', this.onBlur, this);
27655             if(this.cls){
27656                 btn.addClass(this.cls);
27657             }
27658             if(this.icon){
27659                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27660             }
27661             if(this.iconCls){
27662                 btnEl.addClass(this.iconCls);
27663                 if(!this.cls){
27664                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27665                 }
27666             }
27667             if(this.tabIndex !== undefined){
27668                 btnEl.dom.tabIndex = this.tabIndex;
27669             }
27670             if(this.tooltip){
27671                 if(typeof this.tooltip == 'object'){
27672                     Roo.QuickTips.tips(Roo.apply({
27673                           target: btnEl.id
27674                     }, this.tooltip));
27675                 } else {
27676                     btnEl.dom[this.tooltipType] = this.tooltip;
27677                 }
27678             }
27679         }else{
27680             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27681         }
27682         this.el = btn;
27683         if(this.id){
27684             this.el.dom.id = this.el.id = this.id;
27685         }
27686         if(this.menu){
27687             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27688             this.menu.on("show", this.onMenuShow, this);
27689             this.menu.on("hide", this.onMenuHide, this);
27690         }
27691         btn.addClass("x-btn");
27692         if(Roo.isIE && !Roo.isIE7){
27693             this.autoWidth.defer(1, this);
27694         }else{
27695             this.autoWidth();
27696         }
27697         if(this.handleMouseEvents){
27698             btn.on("mouseover", this.onMouseOver, this);
27699             btn.on("mouseout", this.onMouseOut, this);
27700             btn.on("mousedown", this.onMouseDown, this);
27701         }
27702         btn.on(this.clickEvent, this.onClick, this);
27703         //btn.on("mouseup", this.onMouseUp, this);
27704         if(this.hidden){
27705             this.hide();
27706         }
27707         if(this.disabled){
27708             this.disable();
27709         }
27710         Roo.ButtonToggleMgr.register(this);
27711         if(this.pressed){
27712             this.el.addClass("x-btn-pressed");
27713         }
27714         if(this.repeat){
27715             var repeater = new Roo.util.ClickRepeater(btn,
27716                 typeof this.repeat == "object" ? this.repeat : {}
27717             );
27718             repeater.on("click", this.onClick,  this);
27719         }
27720         
27721         this.fireEvent('render', this);
27722         
27723     },
27724     /**
27725      * Returns the button's underlying element
27726      * @return {Roo.Element} The element
27727      */
27728     getEl : function(){
27729         return this.el;  
27730     },
27731     
27732     /**
27733      * Destroys this Button and removes any listeners.
27734      */
27735     destroy : function(){
27736         Roo.ButtonToggleMgr.unregister(this);
27737         this.el.removeAllListeners();
27738         this.purgeListeners();
27739         this.el.remove();
27740     },
27741
27742     // private
27743     autoWidth : function(){
27744         if(this.el){
27745             this.el.setWidth("auto");
27746             if(Roo.isIE7 && Roo.isStrict){
27747                 var ib = this.el.child('button');
27748                 if(ib && ib.getWidth() > 20){
27749                     ib.clip();
27750                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27751                 }
27752             }
27753             if(this.minWidth){
27754                 if(this.hidden){
27755                     this.el.beginMeasure();
27756                 }
27757                 if(this.el.getWidth() < this.minWidth){
27758                     this.el.setWidth(this.minWidth);
27759                 }
27760                 if(this.hidden){
27761                     this.el.endMeasure();
27762                 }
27763             }
27764         }
27765     },
27766
27767     /**
27768      * Assigns this button's click handler
27769      * @param {Function} handler The function to call when the button is clicked
27770      * @param {Object} scope (optional) Scope for the function passed in
27771      */
27772     setHandler : function(handler, scope){
27773         this.handler = handler;
27774         this.scope = scope;  
27775     },
27776     
27777     /**
27778      * Sets this button's text
27779      * @param {String} text The button text
27780      */
27781     setText : function(text){
27782         this.text = text;
27783         if(this.el){
27784             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27785         }
27786         this.autoWidth();
27787     },
27788     
27789     /**
27790      * Gets the text for this button
27791      * @return {String} The button text
27792      */
27793     getText : function(){
27794         return this.text;  
27795     },
27796     
27797     /**
27798      * Show this button
27799      */
27800     show: function(){
27801         this.hidden = false;
27802         if(this.el){
27803             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27804         }
27805     },
27806     
27807     /**
27808      * Hide this button
27809      */
27810     hide: function(){
27811         this.hidden = true;
27812         if(this.el){
27813             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27814         }
27815     },
27816     
27817     /**
27818      * Convenience function for boolean show/hide
27819      * @param {Boolean} visible True to show, false to hide
27820      */
27821     setVisible: function(visible){
27822         if(visible) {
27823             this.show();
27824         }else{
27825             this.hide();
27826         }
27827     },
27828     
27829     /**
27830      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27831      * @param {Boolean} state (optional) Force a particular state
27832      */
27833     toggle : function(state){
27834         state = state === undefined ? !this.pressed : state;
27835         if(state != this.pressed){
27836             if(state){
27837                 this.el.addClass("x-btn-pressed");
27838                 this.pressed = true;
27839                 this.fireEvent("toggle", this, true);
27840             }else{
27841                 this.el.removeClass("x-btn-pressed");
27842                 this.pressed = false;
27843                 this.fireEvent("toggle", this, false);
27844             }
27845             if(this.toggleHandler){
27846                 this.toggleHandler.call(this.scope || this, this, state);
27847             }
27848         }
27849     },
27850     
27851     /**
27852      * Focus the button
27853      */
27854     focus : function(){
27855         this.el.child('button:first').focus();
27856     },
27857     
27858     /**
27859      * Disable this button
27860      */
27861     disable : function(){
27862         if(this.el){
27863             this.el.addClass("x-btn-disabled");
27864         }
27865         this.disabled = true;
27866     },
27867     
27868     /**
27869      * Enable this button
27870      */
27871     enable : function(){
27872         if(this.el){
27873             this.el.removeClass("x-btn-disabled");
27874         }
27875         this.disabled = false;
27876     },
27877
27878     /**
27879      * Convenience function for boolean enable/disable
27880      * @param {Boolean} enabled True to enable, false to disable
27881      */
27882     setDisabled : function(v){
27883         this[v !== true ? "enable" : "disable"]();
27884     },
27885
27886     // private
27887     onClick : function(e)
27888     {
27889         if(e){
27890             e.preventDefault();
27891         }
27892         if(e.button != 0){
27893             return;
27894         }
27895         if(!this.disabled){
27896             if(this.enableToggle){
27897                 this.toggle();
27898             }
27899             if(this.menu && !this.menu.isVisible()){
27900                 this.menu.show(this.el, this.menuAlign);
27901             }
27902             this.fireEvent("click", this, e);
27903             if(this.handler){
27904                 this.el.removeClass("x-btn-over");
27905                 this.handler.call(this.scope || this, this, e);
27906             }
27907         }
27908     },
27909     // private
27910     onMouseOver : function(e){
27911         if(!this.disabled){
27912             this.el.addClass("x-btn-over");
27913             this.fireEvent('mouseover', this, e);
27914         }
27915     },
27916     // private
27917     onMouseOut : function(e){
27918         if(!e.within(this.el,  true)){
27919             this.el.removeClass("x-btn-over");
27920             this.fireEvent('mouseout', this, e);
27921         }
27922     },
27923     // private
27924     onFocus : function(e){
27925         if(!this.disabled){
27926             this.el.addClass("x-btn-focus");
27927         }
27928     },
27929     // private
27930     onBlur : function(e){
27931         this.el.removeClass("x-btn-focus");
27932     },
27933     // private
27934     onMouseDown : function(e){
27935         if(!this.disabled && e.button == 0){
27936             this.el.addClass("x-btn-click");
27937             Roo.get(document).on('mouseup', this.onMouseUp, this);
27938         }
27939     },
27940     // private
27941     onMouseUp : function(e){
27942         if(e.button == 0){
27943             this.el.removeClass("x-btn-click");
27944             Roo.get(document).un('mouseup', this.onMouseUp, this);
27945         }
27946     },
27947     // private
27948     onMenuShow : function(e){
27949         this.el.addClass("x-btn-menu-active");
27950     },
27951     // private
27952     onMenuHide : function(e){
27953         this.el.removeClass("x-btn-menu-active");
27954     }   
27955 });
27956
27957 // Private utility class used by Button
27958 Roo.ButtonToggleMgr = function(){
27959    var groups = {};
27960    
27961    function toggleGroup(btn, state){
27962        if(state){
27963            var g = groups[btn.toggleGroup];
27964            for(var i = 0, l = g.length; i < l; i++){
27965                if(g[i] != btn){
27966                    g[i].toggle(false);
27967                }
27968            }
27969        }
27970    }
27971    
27972    return {
27973        register : function(btn){
27974            if(!btn.toggleGroup){
27975                return;
27976            }
27977            var g = groups[btn.toggleGroup];
27978            if(!g){
27979                g = groups[btn.toggleGroup] = [];
27980            }
27981            g.push(btn);
27982            btn.on("toggle", toggleGroup);
27983        },
27984        
27985        unregister : function(btn){
27986            if(!btn.toggleGroup){
27987                return;
27988            }
27989            var g = groups[btn.toggleGroup];
27990            if(g){
27991                g.remove(btn);
27992                btn.un("toggle", toggleGroup);
27993            }
27994        }
27995    };
27996 }();/*
27997  * Based on:
27998  * Ext JS Library 1.1.1
27999  * Copyright(c) 2006-2007, Ext JS, LLC.
28000  *
28001  * Originally Released Under LGPL - original licence link has changed is not relivant.
28002  *
28003  * Fork - LGPL
28004  * <script type="text/javascript">
28005  */
28006  
28007 /**
28008  * @class Roo.SplitButton
28009  * @extends Roo.Button
28010  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
28011  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
28012  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
28013  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
28014  * @cfg {String} arrowTooltip The title attribute of the arrow
28015  * @constructor
28016  * Create a new menu button
28017  * @param {String/HTMLElement/Element} renderTo The element to append the button to
28018  * @param {Object} config The config object
28019  */
28020 Roo.SplitButton = function(renderTo, config){
28021     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
28022     /**
28023      * @event arrowclick
28024      * Fires when this button's arrow is clicked
28025      * @param {SplitButton} this
28026      * @param {EventObject} e The click event
28027      */
28028     this.addEvents({"arrowclick":true});
28029 };
28030
28031 Roo.extend(Roo.SplitButton, Roo.Button, {
28032     render : function(renderTo){
28033         // this is one sweet looking template!
28034         var tpl = new Roo.Template(
28035             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
28036             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
28037             '<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>',
28038             "</tbody></table></td><td>",
28039             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
28040             '<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>',
28041             "</tbody></table></td></tr></table>"
28042         );
28043         var btn = tpl.append(renderTo, [this.text, this.type], true);
28044         var btnEl = btn.child("button");
28045         if(this.cls){
28046             btn.addClass(this.cls);
28047         }
28048         if(this.icon){
28049             btnEl.setStyle('background-image', 'url(' +this.icon +')');
28050         }
28051         if(this.iconCls){
28052             btnEl.addClass(this.iconCls);
28053             if(!this.cls){
28054                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
28055             }
28056         }
28057         this.el = btn;
28058         if(this.handleMouseEvents){
28059             btn.on("mouseover", this.onMouseOver, this);
28060             btn.on("mouseout", this.onMouseOut, this);
28061             btn.on("mousedown", this.onMouseDown, this);
28062             btn.on("mouseup", this.onMouseUp, this);
28063         }
28064         btn.on(this.clickEvent, this.onClick, this);
28065         if(this.tooltip){
28066             if(typeof this.tooltip == 'object'){
28067                 Roo.QuickTips.tips(Roo.apply({
28068                       target: btnEl.id
28069                 }, this.tooltip));
28070             } else {
28071                 btnEl.dom[this.tooltipType] = this.tooltip;
28072             }
28073         }
28074         if(this.arrowTooltip){
28075             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
28076         }
28077         if(this.hidden){
28078             this.hide();
28079         }
28080         if(this.disabled){
28081             this.disable();
28082         }
28083         if(this.pressed){
28084             this.el.addClass("x-btn-pressed");
28085         }
28086         if(Roo.isIE && !Roo.isIE7){
28087             this.autoWidth.defer(1, this);
28088         }else{
28089             this.autoWidth();
28090         }
28091         if(this.menu){
28092             this.menu.on("show", this.onMenuShow, this);
28093             this.menu.on("hide", this.onMenuHide, this);
28094         }
28095         this.fireEvent('render', this);
28096     },
28097
28098     // private
28099     autoWidth : function(){
28100         if(this.el){
28101             var tbl = this.el.child("table:first");
28102             var tbl2 = this.el.child("table:last");
28103             this.el.setWidth("auto");
28104             tbl.setWidth("auto");
28105             if(Roo.isIE7 && Roo.isStrict){
28106                 var ib = this.el.child('button:first');
28107                 if(ib && ib.getWidth() > 20){
28108                     ib.clip();
28109                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
28110                 }
28111             }
28112             if(this.minWidth){
28113                 if(this.hidden){
28114                     this.el.beginMeasure();
28115                 }
28116                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
28117                     tbl.setWidth(this.minWidth-tbl2.getWidth());
28118                 }
28119                 if(this.hidden){
28120                     this.el.endMeasure();
28121                 }
28122             }
28123             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
28124         } 
28125     },
28126     /**
28127      * Sets this button's click handler
28128      * @param {Function} handler The function to call when the button is clicked
28129      * @param {Object} scope (optional) Scope for the function passed above
28130      */
28131     setHandler : function(handler, scope){
28132         this.handler = handler;
28133         this.scope = scope;  
28134     },
28135     
28136     /**
28137      * Sets this button's arrow click handler
28138      * @param {Function} handler The function to call when the arrow is clicked
28139      * @param {Object} scope (optional) Scope for the function passed above
28140      */
28141     setArrowHandler : function(handler, scope){
28142         this.arrowHandler = handler;
28143         this.scope = scope;  
28144     },
28145     
28146     /**
28147      * Focus the button
28148      */
28149     focus : function(){
28150         if(this.el){
28151             this.el.child("button:first").focus();
28152         }
28153     },
28154
28155     // private
28156     onClick : function(e){
28157         e.preventDefault();
28158         if(!this.disabled){
28159             if(e.getTarget(".x-btn-menu-arrow-wrap")){
28160                 if(this.menu && !this.menu.isVisible()){
28161                     this.menu.show(this.el, this.menuAlign);
28162                 }
28163                 this.fireEvent("arrowclick", this, e);
28164                 if(this.arrowHandler){
28165                     this.arrowHandler.call(this.scope || this, this, e);
28166                 }
28167             }else{
28168                 this.fireEvent("click", this, e);
28169                 if(this.handler){
28170                     this.handler.call(this.scope || this, this, e);
28171                 }
28172             }
28173         }
28174     },
28175     // private
28176     onMouseDown : function(e){
28177         if(!this.disabled){
28178             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28179         }
28180     },
28181     // private
28182     onMouseUp : function(e){
28183         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28184     }   
28185 });
28186
28187
28188 // backwards compat
28189 Roo.MenuButton = Roo.SplitButton;/*
28190  * Based on:
28191  * Ext JS Library 1.1.1
28192  * Copyright(c) 2006-2007, Ext JS, LLC.
28193  *
28194  * Originally Released Under LGPL - original licence link has changed is not relivant.
28195  *
28196  * Fork - LGPL
28197  * <script type="text/javascript">
28198  */
28199
28200 /**
28201  * @class Roo.Toolbar
28202  * Basic Toolbar class.
28203  * @constructor
28204  * Creates a new Toolbar
28205  * @param {Object} container The config object
28206  */ 
28207 Roo.Toolbar = function(container, buttons, config)
28208 {
28209     /// old consturctor format still supported..
28210     if(container instanceof Array){ // omit the container for later rendering
28211         buttons = container;
28212         config = buttons;
28213         container = null;
28214     }
28215     if (typeof(container) == 'object' && container.xtype) {
28216         config = container;
28217         container = config.container;
28218         buttons = config.buttons || []; // not really - use items!!
28219     }
28220     var xitems = [];
28221     if (config && config.items) {
28222         xitems = config.items;
28223         delete config.items;
28224     }
28225     Roo.apply(this, config);
28226     this.buttons = buttons;
28227     
28228     if(container){
28229         this.render(container);
28230     }
28231     this.xitems = xitems;
28232     Roo.each(xitems, function(b) {
28233         this.add(b);
28234     }, this);
28235     
28236 };
28237
28238 Roo.Toolbar.prototype = {
28239     /**
28240      * @cfg {Array} items
28241      * array of button configs or elements to add (will be converted to a MixedCollection)
28242      */
28243     
28244     /**
28245      * @cfg {String/HTMLElement/Element} container
28246      * The id or element that will contain the toolbar
28247      */
28248     // private
28249     render : function(ct){
28250         this.el = Roo.get(ct);
28251         if(this.cls){
28252             this.el.addClass(this.cls);
28253         }
28254         // using a table allows for vertical alignment
28255         // 100% width is needed by Safari...
28256         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28257         this.tr = this.el.child("tr", true);
28258         var autoId = 0;
28259         this.items = new Roo.util.MixedCollection(false, function(o){
28260             return o.id || ("item" + (++autoId));
28261         });
28262         if(this.buttons){
28263             this.add.apply(this, this.buttons);
28264             delete this.buttons;
28265         }
28266     },
28267
28268     /**
28269      * Adds element(s) to the toolbar -- this function takes a variable number of 
28270      * arguments of mixed type and adds them to the toolbar.
28271      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28272      * <ul>
28273      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28274      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28275      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28276      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28277      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28278      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28279      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28280      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28281      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28282      * </ul>
28283      * @param {Mixed} arg2
28284      * @param {Mixed} etc.
28285      */
28286     add : function(){
28287         var a = arguments, l = a.length;
28288         for(var i = 0; i < l; i++){
28289             this._add(a[i]);
28290         }
28291     },
28292     // private..
28293     _add : function(el) {
28294         
28295         if (el.xtype) {
28296             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28297         }
28298         
28299         if (el.applyTo){ // some kind of form field
28300             return this.addField(el);
28301         } 
28302         if (el.render){ // some kind of Toolbar.Item
28303             return this.addItem(el);
28304         }
28305         if (typeof el == "string"){ // string
28306             if(el == "separator" || el == "-"){
28307                 return this.addSeparator();
28308             }
28309             if (el == " "){
28310                 return this.addSpacer();
28311             }
28312             if(el == "->"){
28313                 return this.addFill();
28314             }
28315             return this.addText(el);
28316             
28317         }
28318         if(el.tagName){ // element
28319             return this.addElement(el);
28320         }
28321         if(typeof el == "object"){ // must be button config?
28322             return this.addButton(el);
28323         }
28324         // and now what?!?!
28325         return false;
28326         
28327     },
28328     
28329     /**
28330      * Add an Xtype element
28331      * @param {Object} xtype Xtype Object
28332      * @return {Object} created Object
28333      */
28334     addxtype : function(e){
28335         return this.add(e);  
28336     },
28337     
28338     /**
28339      * Returns the Element for this toolbar.
28340      * @return {Roo.Element}
28341      */
28342     getEl : function(){
28343         return this.el;  
28344     },
28345     
28346     /**
28347      * Adds a separator
28348      * @return {Roo.Toolbar.Item} The separator item
28349      */
28350     addSeparator : function(){
28351         return this.addItem(new Roo.Toolbar.Separator());
28352     },
28353
28354     /**
28355      * Adds a spacer element
28356      * @return {Roo.Toolbar.Spacer} The spacer item
28357      */
28358     addSpacer : function(){
28359         return this.addItem(new Roo.Toolbar.Spacer());
28360     },
28361
28362     /**
28363      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28364      * @return {Roo.Toolbar.Fill} The fill item
28365      */
28366     addFill : function(){
28367         return this.addItem(new Roo.Toolbar.Fill());
28368     },
28369
28370     /**
28371      * Adds any standard HTML element to the toolbar
28372      * @param {String/HTMLElement/Element} el The element or id of the element to add
28373      * @return {Roo.Toolbar.Item} The element's item
28374      */
28375     addElement : function(el){
28376         return this.addItem(new Roo.Toolbar.Item(el));
28377     },
28378     /**
28379      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28380      * @type Roo.util.MixedCollection  
28381      */
28382     items : false,
28383      
28384     /**
28385      * Adds any Toolbar.Item or subclass
28386      * @param {Roo.Toolbar.Item} item
28387      * @return {Roo.Toolbar.Item} The item
28388      */
28389     addItem : function(item){
28390         var td = this.nextBlock();
28391         item.render(td);
28392         this.items.add(item);
28393         return item;
28394     },
28395     
28396     /**
28397      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28398      * @param {Object/Array} config A button config or array of configs
28399      * @return {Roo.Toolbar.Button/Array}
28400      */
28401     addButton : function(config){
28402         if(config instanceof Array){
28403             var buttons = [];
28404             for(var i = 0, len = config.length; i < len; i++) {
28405                 buttons.push(this.addButton(config[i]));
28406             }
28407             return buttons;
28408         }
28409         var b = config;
28410         if(!(config instanceof Roo.Toolbar.Button)){
28411             b = config.split ?
28412                 new Roo.Toolbar.SplitButton(config) :
28413                 new Roo.Toolbar.Button(config);
28414         }
28415         var td = this.nextBlock();
28416         b.render(td);
28417         this.items.add(b);
28418         return b;
28419     },
28420     
28421     /**
28422      * Adds text to the toolbar
28423      * @param {String} text The text to add
28424      * @return {Roo.Toolbar.Item} The element's item
28425      */
28426     addText : function(text){
28427         return this.addItem(new Roo.Toolbar.TextItem(text));
28428     },
28429     
28430     /**
28431      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28432      * @param {Number} index The index where the item is to be inserted
28433      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28434      * @return {Roo.Toolbar.Button/Item}
28435      */
28436     insertButton : function(index, item){
28437         if(item instanceof Array){
28438             var buttons = [];
28439             for(var i = 0, len = item.length; i < len; i++) {
28440                buttons.push(this.insertButton(index + i, item[i]));
28441             }
28442             return buttons;
28443         }
28444         if (!(item instanceof Roo.Toolbar.Button)){
28445            item = new Roo.Toolbar.Button(item);
28446         }
28447         var td = document.createElement("td");
28448         this.tr.insertBefore(td, this.tr.childNodes[index]);
28449         item.render(td);
28450         this.items.insert(index, item);
28451         return item;
28452     },
28453     
28454     /**
28455      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28456      * @param {Object} config
28457      * @return {Roo.Toolbar.Item} The element's item
28458      */
28459     addDom : function(config, returnEl){
28460         var td = this.nextBlock();
28461         Roo.DomHelper.overwrite(td, config);
28462         var ti = new Roo.Toolbar.Item(td.firstChild);
28463         ti.render(td);
28464         this.items.add(ti);
28465         return ti;
28466     },
28467
28468     /**
28469      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28470      * @type Roo.util.MixedCollection  
28471      */
28472     fields : false,
28473     
28474     /**
28475      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28476      * Note: the field should not have been rendered yet. For a field that has already been
28477      * rendered, use {@link #addElement}.
28478      * @param {Roo.form.Field} field
28479      * @return {Roo.ToolbarItem}
28480      */
28481      
28482       
28483     addField : function(field) {
28484         if (!this.fields) {
28485             var autoId = 0;
28486             this.fields = new Roo.util.MixedCollection(false, function(o){
28487                 return o.id || ("item" + (++autoId));
28488             });
28489
28490         }
28491         
28492         var td = this.nextBlock();
28493         field.render(td);
28494         var ti = new Roo.Toolbar.Item(td.firstChild);
28495         ti.render(td);
28496         this.items.add(ti);
28497         this.fields.add(field);
28498         return ti;
28499     },
28500     /**
28501      * Hide the toolbar
28502      * @method hide
28503      */
28504      
28505       
28506     hide : function()
28507     {
28508         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28509         this.el.child('div').hide();
28510     },
28511     /**
28512      * Show the toolbar
28513      * @method show
28514      */
28515     show : function()
28516     {
28517         this.el.child('div').show();
28518     },
28519       
28520     // private
28521     nextBlock : function(){
28522         var td = document.createElement("td");
28523         this.tr.appendChild(td);
28524         return td;
28525     },
28526
28527     // private
28528     destroy : function(){
28529         if(this.items){ // rendered?
28530             Roo.destroy.apply(Roo, this.items.items);
28531         }
28532         if(this.fields){ // rendered?
28533             Roo.destroy.apply(Roo, this.fields.items);
28534         }
28535         Roo.Element.uncache(this.el, this.tr);
28536     }
28537 };
28538
28539 /**
28540  * @class Roo.Toolbar.Item
28541  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28542  * @constructor
28543  * Creates a new Item
28544  * @param {HTMLElement} el 
28545  */
28546 Roo.Toolbar.Item = function(el){
28547     var cfg = {};
28548     if (typeof (el.xtype) != 'undefined') {
28549         cfg = el;
28550         el = cfg.el;
28551     }
28552     
28553     this.el = Roo.getDom(el);
28554     this.id = Roo.id(this.el);
28555     this.hidden = false;
28556     
28557     this.addEvents({
28558          /**
28559              * @event render
28560              * Fires when the button is rendered
28561              * @param {Button} this
28562              */
28563         'render': true
28564     });
28565     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
28566 };
28567 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
28568 //Roo.Toolbar.Item.prototype = {
28569     
28570     /**
28571      * Get this item's HTML Element
28572      * @return {HTMLElement}
28573      */
28574     getEl : function(){
28575        return this.el;  
28576     },
28577
28578     // private
28579     render : function(td){
28580         
28581          this.td = td;
28582         td.appendChild(this.el);
28583         
28584         this.fireEvent('render', this);
28585     },
28586     
28587     /**
28588      * Removes and destroys this item.
28589      */
28590     destroy : function(){
28591         this.td.parentNode.removeChild(this.td);
28592     },
28593     
28594     /**
28595      * Shows this item.
28596      */
28597     show: function(){
28598         this.hidden = false;
28599         this.td.style.display = "";
28600     },
28601     
28602     /**
28603      * Hides this item.
28604      */
28605     hide: function(){
28606         this.hidden = true;
28607         this.td.style.display = "none";
28608     },
28609     
28610     /**
28611      * Convenience function for boolean show/hide.
28612      * @param {Boolean} visible true to show/false to hide
28613      */
28614     setVisible: function(visible){
28615         if(visible) {
28616             this.show();
28617         }else{
28618             this.hide();
28619         }
28620     },
28621     
28622     /**
28623      * Try to focus this item.
28624      */
28625     focus : function(){
28626         Roo.fly(this.el).focus();
28627     },
28628     
28629     /**
28630      * Disables this item.
28631      */
28632     disable : function(){
28633         Roo.fly(this.td).addClass("x-item-disabled");
28634         this.disabled = true;
28635         this.el.disabled = true;
28636     },
28637     
28638     /**
28639      * Enables this item.
28640      */
28641     enable : function(){
28642         Roo.fly(this.td).removeClass("x-item-disabled");
28643         this.disabled = false;
28644         this.el.disabled = false;
28645     }
28646 });
28647
28648
28649 /**
28650  * @class Roo.Toolbar.Separator
28651  * @extends Roo.Toolbar.Item
28652  * A simple toolbar separator class
28653  * @constructor
28654  * Creates a new Separator
28655  */
28656 Roo.Toolbar.Separator = function(cfg){
28657     
28658     var s = document.createElement("span");
28659     s.className = "ytb-sep";
28660     if (cfg) {
28661         cfg.el = s;
28662     }
28663     
28664     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
28665 };
28666 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28667     enable:Roo.emptyFn,
28668     disable:Roo.emptyFn,
28669     focus:Roo.emptyFn
28670 });
28671
28672 /**
28673  * @class Roo.Toolbar.Spacer
28674  * @extends Roo.Toolbar.Item
28675  * A simple element that adds extra horizontal space to a toolbar.
28676  * @constructor
28677  * Creates a new Spacer
28678  */
28679 Roo.Toolbar.Spacer = function(cfg){
28680     var s = document.createElement("div");
28681     s.className = "ytb-spacer";
28682     if (cfg) {
28683         cfg.el = s;
28684     }
28685     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
28686 };
28687 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28688     enable:Roo.emptyFn,
28689     disable:Roo.emptyFn,
28690     focus:Roo.emptyFn
28691 });
28692
28693 /**
28694  * @class Roo.Toolbar.Fill
28695  * @extends Roo.Toolbar.Spacer
28696  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28697  * @constructor
28698  * Creates a new Spacer
28699  */
28700 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28701     // private
28702     render : function(td){
28703         td.style.width = '100%';
28704         Roo.Toolbar.Fill.superclass.render.call(this, td);
28705     }
28706 });
28707
28708 /**
28709  * @class Roo.Toolbar.TextItem
28710  * @extends Roo.Toolbar.Item
28711  * A simple class that renders text directly into a toolbar.
28712  * @constructor
28713  * Creates a new TextItem
28714  * @param {String} text
28715  */
28716 Roo.Toolbar.TextItem = function(cfg){
28717     var  text = cfg || "";
28718     if (typeof(cfg) == 'object') {
28719         text = cfg.text || "";
28720     }  else {
28721         cfg = null;
28722     }
28723     var s = document.createElement("span");
28724     s.className = "ytb-text";
28725     s.innerHTML = text;
28726     if (cfg) {
28727         cfg.el  = s;
28728     }
28729     
28730     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
28731 };
28732 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28733     
28734      
28735     enable:Roo.emptyFn,
28736     disable:Roo.emptyFn,
28737     focus:Roo.emptyFn
28738 });
28739
28740 /**
28741  * @class Roo.Toolbar.Button
28742  * @extends Roo.Button
28743  * A button that renders into a toolbar.
28744  * @constructor
28745  * Creates a new Button
28746  * @param {Object} config A standard {@link Roo.Button} config object
28747  */
28748 Roo.Toolbar.Button = function(config){
28749     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28750 };
28751 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28752     render : function(td){
28753         this.td = td;
28754         Roo.Toolbar.Button.superclass.render.call(this, td);
28755     },
28756     
28757     /**
28758      * Removes and destroys this button
28759      */
28760     destroy : function(){
28761         Roo.Toolbar.Button.superclass.destroy.call(this);
28762         this.td.parentNode.removeChild(this.td);
28763     },
28764     
28765     /**
28766      * Shows this button
28767      */
28768     show: function(){
28769         this.hidden = false;
28770         this.td.style.display = "";
28771     },
28772     
28773     /**
28774      * Hides this button
28775      */
28776     hide: function(){
28777         this.hidden = true;
28778         this.td.style.display = "none";
28779     },
28780
28781     /**
28782      * Disables this item
28783      */
28784     disable : function(){
28785         Roo.fly(this.td).addClass("x-item-disabled");
28786         this.disabled = true;
28787     },
28788
28789     /**
28790      * Enables this item
28791      */
28792     enable : function(){
28793         Roo.fly(this.td).removeClass("x-item-disabled");
28794         this.disabled = false;
28795     }
28796 });
28797 // backwards compat
28798 Roo.ToolbarButton = Roo.Toolbar.Button;
28799
28800 /**
28801  * @class Roo.Toolbar.SplitButton
28802  * @extends Roo.SplitButton
28803  * A menu button that renders into a toolbar.
28804  * @constructor
28805  * Creates a new SplitButton
28806  * @param {Object} config A standard {@link Roo.SplitButton} config object
28807  */
28808 Roo.Toolbar.SplitButton = function(config){
28809     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28810 };
28811 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28812     render : function(td){
28813         this.td = td;
28814         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28815     },
28816     
28817     /**
28818      * Removes and destroys this button
28819      */
28820     destroy : function(){
28821         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28822         this.td.parentNode.removeChild(this.td);
28823     },
28824     
28825     /**
28826      * Shows this button
28827      */
28828     show: function(){
28829         this.hidden = false;
28830         this.td.style.display = "";
28831     },
28832     
28833     /**
28834      * Hides this button
28835      */
28836     hide: function(){
28837         this.hidden = true;
28838         this.td.style.display = "none";
28839     }
28840 });
28841
28842 // backwards compat
28843 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28844  * Based on:
28845  * Ext JS Library 1.1.1
28846  * Copyright(c) 2006-2007, Ext JS, LLC.
28847  *
28848  * Originally Released Under LGPL - original licence link has changed is not relivant.
28849  *
28850  * Fork - LGPL
28851  * <script type="text/javascript">
28852  */
28853  
28854 /**
28855  * @class Roo.PagingToolbar
28856  * @extends Roo.Toolbar
28857  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28858  * @constructor
28859  * Create a new PagingToolbar
28860  * @param {Object} config The config object
28861  */
28862 Roo.PagingToolbar = function(el, ds, config)
28863 {
28864     // old args format still supported... - xtype is prefered..
28865     if (typeof(el) == 'object' && el.xtype) {
28866         // created from xtype...
28867         config = el;
28868         ds = el.dataSource;
28869         el = config.container;
28870     }
28871     var items = [];
28872     if (config.items) {
28873         items = config.items;
28874         config.items = [];
28875     }
28876     
28877     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28878     this.ds = ds;
28879     this.cursor = 0;
28880     this.renderButtons(this.el);
28881     this.bind(ds);
28882     
28883     // supprot items array.
28884    
28885     Roo.each(items, function(e) {
28886         this.add(Roo.factory(e));
28887     },this);
28888     
28889 };
28890
28891 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28892     /**
28893      * @cfg {Roo.data.Store} dataSource
28894      * The underlying data store providing the paged data
28895      */
28896     /**
28897      * @cfg {String/HTMLElement/Element} container
28898      * container The id or element that will contain the toolbar
28899      */
28900     /**
28901      * @cfg {Boolean} displayInfo
28902      * True to display the displayMsg (defaults to false)
28903      */
28904     /**
28905      * @cfg {Number} pageSize
28906      * The number of records to display per page (defaults to 20)
28907      */
28908     pageSize: 20,
28909     /**
28910      * @cfg {String} displayMsg
28911      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28912      */
28913     displayMsg : 'Displaying {0} - {1} of {2}',
28914     /**
28915      * @cfg {String} emptyMsg
28916      * The message to display when no records are found (defaults to "No data to display")
28917      */
28918     emptyMsg : 'No data to display',
28919     /**
28920      * Customizable piece of the default paging text (defaults to "Page")
28921      * @type String
28922      */
28923     beforePageText : "Page",
28924     /**
28925      * Customizable piece of the default paging text (defaults to "of %0")
28926      * @type String
28927      */
28928     afterPageText : "of {0}",
28929     /**
28930      * Customizable piece of the default paging text (defaults to "First Page")
28931      * @type String
28932      */
28933     firstText : "First Page",
28934     /**
28935      * Customizable piece of the default paging text (defaults to "Previous Page")
28936      * @type String
28937      */
28938     prevText : "Previous Page",
28939     /**
28940      * Customizable piece of the default paging text (defaults to "Next Page")
28941      * @type String
28942      */
28943     nextText : "Next Page",
28944     /**
28945      * Customizable piece of the default paging text (defaults to "Last Page")
28946      * @type String
28947      */
28948     lastText : "Last Page",
28949     /**
28950      * Customizable piece of the default paging text (defaults to "Refresh")
28951      * @type String
28952      */
28953     refreshText : "Refresh",
28954
28955     // private
28956     renderButtons : function(el){
28957         Roo.PagingToolbar.superclass.render.call(this, el);
28958         this.first = this.addButton({
28959             tooltip: this.firstText,
28960             cls: "x-btn-icon x-grid-page-first",
28961             disabled: true,
28962             handler: this.onClick.createDelegate(this, ["first"])
28963         });
28964         this.prev = this.addButton({
28965             tooltip: this.prevText,
28966             cls: "x-btn-icon x-grid-page-prev",
28967             disabled: true,
28968             handler: this.onClick.createDelegate(this, ["prev"])
28969         });
28970         //this.addSeparator();
28971         this.add(this.beforePageText);
28972         this.field = Roo.get(this.addDom({
28973            tag: "input",
28974            type: "text",
28975            size: "3",
28976            value: "1",
28977            cls: "x-grid-page-number"
28978         }).el);
28979         this.field.on("keydown", this.onPagingKeydown, this);
28980         this.field.on("focus", function(){this.dom.select();});
28981         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28982         this.field.setHeight(18);
28983         //this.addSeparator();
28984         this.next = this.addButton({
28985             tooltip: this.nextText,
28986             cls: "x-btn-icon x-grid-page-next",
28987             disabled: true,
28988             handler: this.onClick.createDelegate(this, ["next"])
28989         });
28990         this.last = this.addButton({
28991             tooltip: this.lastText,
28992             cls: "x-btn-icon x-grid-page-last",
28993             disabled: true,
28994             handler: this.onClick.createDelegate(this, ["last"])
28995         });
28996         //this.addSeparator();
28997         this.loading = this.addButton({
28998             tooltip: this.refreshText,
28999             cls: "x-btn-icon x-grid-loading",
29000             handler: this.onClick.createDelegate(this, ["refresh"])
29001         });
29002
29003         if(this.displayInfo){
29004             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
29005         }
29006     },
29007
29008     // private
29009     updateInfo : function(){
29010         if(this.displayEl){
29011             var count = this.ds.getCount();
29012             var msg = count == 0 ?
29013                 this.emptyMsg :
29014                 String.format(
29015                     this.displayMsg,
29016                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
29017                 );
29018             this.displayEl.update(msg);
29019         }
29020     },
29021
29022     // private
29023     onLoad : function(ds, r, o){
29024        this.cursor = o.params ? o.params.start : 0;
29025        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
29026
29027        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
29028        this.field.dom.value = ap;
29029        this.first.setDisabled(ap == 1);
29030        this.prev.setDisabled(ap == 1);
29031        this.next.setDisabled(ap == ps);
29032        this.last.setDisabled(ap == ps);
29033        this.loading.enable();
29034        this.updateInfo();
29035     },
29036
29037     // private
29038     getPageData : function(){
29039         var total = this.ds.getTotalCount();
29040         return {
29041             total : total,
29042             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
29043             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
29044         };
29045     },
29046
29047     // private
29048     onLoadError : function(){
29049         this.loading.enable();
29050     },
29051
29052     // private
29053     onPagingKeydown : function(e){
29054         var k = e.getKey();
29055         var d = this.getPageData();
29056         if(k == e.RETURN){
29057             var v = this.field.dom.value, pageNum;
29058             if(!v || isNaN(pageNum = parseInt(v, 10))){
29059                 this.field.dom.value = d.activePage;
29060                 return;
29061             }
29062             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
29063             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29064             e.stopEvent();
29065         }
29066         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))
29067         {
29068           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
29069           this.field.dom.value = pageNum;
29070           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
29071           e.stopEvent();
29072         }
29073         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29074         {
29075           var v = this.field.dom.value, pageNum; 
29076           var increment = (e.shiftKey) ? 10 : 1;
29077           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29078             increment *= -1;
29079           if(!v || isNaN(pageNum = parseInt(v, 10))) {
29080             this.field.dom.value = d.activePage;
29081             return;
29082           }
29083           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
29084           {
29085             this.field.dom.value = parseInt(v, 10) + increment;
29086             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
29087             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29088           }
29089           e.stopEvent();
29090         }
29091     },
29092
29093     // private
29094     beforeLoad : function(){
29095         if(this.loading){
29096             this.loading.disable();
29097         }
29098     },
29099
29100     // private
29101     onClick : function(which){
29102         var ds = this.ds;
29103         switch(which){
29104             case "first":
29105                 ds.load({params:{start: 0, limit: this.pageSize}});
29106             break;
29107             case "prev":
29108                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
29109             break;
29110             case "next":
29111                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
29112             break;
29113             case "last":
29114                 var total = ds.getTotalCount();
29115                 var extra = total % this.pageSize;
29116                 var lastStart = extra ? (total - extra) : total-this.pageSize;
29117                 ds.load({params:{start: lastStart, limit: this.pageSize}});
29118             break;
29119             case "refresh":
29120                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
29121             break;
29122         }
29123     },
29124
29125     /**
29126      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
29127      * @param {Roo.data.Store} store The data store to unbind
29128      */
29129     unbind : function(ds){
29130         ds.un("beforeload", this.beforeLoad, this);
29131         ds.un("load", this.onLoad, this);
29132         ds.un("loadexception", this.onLoadError, this);
29133         ds.un("remove", this.updateInfo, this);
29134         ds.un("add", this.updateInfo, this);
29135         this.ds = undefined;
29136     },
29137
29138     /**
29139      * Binds the paging toolbar to the specified {@link Roo.data.Store}
29140      * @param {Roo.data.Store} store The data store to bind
29141      */
29142     bind : function(ds){
29143         ds.on("beforeload", this.beforeLoad, this);
29144         ds.on("load", this.onLoad, this);
29145         ds.on("loadexception", this.onLoadError, this);
29146         ds.on("remove", this.updateInfo, this);
29147         ds.on("add", this.updateInfo, this);
29148         this.ds = ds;
29149     }
29150 });/*
29151  * Based on:
29152  * Ext JS Library 1.1.1
29153  * Copyright(c) 2006-2007, Ext JS, LLC.
29154  *
29155  * Originally Released Under LGPL - original licence link has changed is not relivant.
29156  *
29157  * Fork - LGPL
29158  * <script type="text/javascript">
29159  */
29160
29161 /**
29162  * @class Roo.Resizable
29163  * @extends Roo.util.Observable
29164  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
29165  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
29166  * 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
29167  * the element will be wrapped for you automatically.</p>
29168  * <p>Here is the list of valid resize handles:</p>
29169  * <pre>
29170 Value   Description
29171 ------  -------------------
29172  'n'     north
29173  's'     south
29174  'e'     east
29175  'w'     west
29176  'nw'    northwest
29177  'sw'    southwest
29178  'se'    southeast
29179  'ne'    northeast
29180  'hd'    horizontal drag
29181  'all'   all
29182 </pre>
29183  * <p>Here's an example showing the creation of a typical Resizable:</p>
29184  * <pre><code>
29185 var resizer = new Roo.Resizable("element-id", {
29186     handles: 'all',
29187     minWidth: 200,
29188     minHeight: 100,
29189     maxWidth: 500,
29190     maxHeight: 400,
29191     pinned: true
29192 });
29193 resizer.on("resize", myHandler);
29194 </code></pre>
29195  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
29196  * resizer.east.setDisplayed(false);</p>
29197  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
29198  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
29199  * resize operation's new size (defaults to [0, 0])
29200  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
29201  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
29202  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29203  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29204  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29205  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29206  * @cfg {Number} width The width of the element in pixels (defaults to null)
29207  * @cfg {Number} height The height of the element in pixels (defaults to null)
29208  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29209  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29210  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29211  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29212  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29213  * in favor of the handles config option (defaults to false)
29214  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29215  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29216  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29217  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29218  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29219  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29220  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29221  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29222  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29223  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29224  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29225  * @constructor
29226  * Create a new resizable component
29227  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29228  * @param {Object} config configuration options
29229   */
29230 Roo.Resizable = function(el, config)
29231 {
29232     this.el = Roo.get(el);
29233
29234     if(config && config.wrap){
29235         config.resizeChild = this.el;
29236         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29237         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29238         this.el.setStyle("overflow", "hidden");
29239         this.el.setPositioning(config.resizeChild.getPositioning());
29240         config.resizeChild.clearPositioning();
29241         if(!config.width || !config.height){
29242             var csize = config.resizeChild.getSize();
29243             this.el.setSize(csize.width, csize.height);
29244         }
29245         if(config.pinned && !config.adjustments){
29246             config.adjustments = "auto";
29247         }
29248     }
29249
29250     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29251     this.proxy.unselectable();
29252     this.proxy.enableDisplayMode('block');
29253
29254     Roo.apply(this, config);
29255
29256     if(this.pinned){
29257         this.disableTrackOver = true;
29258         this.el.addClass("x-resizable-pinned");
29259     }
29260     // if the element isn't positioned, make it relative
29261     var position = this.el.getStyle("position");
29262     if(position != "absolute" && position != "fixed"){
29263         this.el.setStyle("position", "relative");
29264     }
29265     if(!this.handles){ // no handles passed, must be legacy style
29266         this.handles = 's,e,se';
29267         if(this.multiDirectional){
29268             this.handles += ',n,w';
29269         }
29270     }
29271     if(this.handles == "all"){
29272         this.handles = "n s e w ne nw se sw";
29273     }
29274     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29275     var ps = Roo.Resizable.positions;
29276     for(var i = 0, len = hs.length; i < len; i++){
29277         if(hs[i] && ps[hs[i]]){
29278             var pos = ps[hs[i]];
29279             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29280         }
29281     }
29282     // legacy
29283     this.corner = this.southeast;
29284     
29285     // updateBox = the box can move..
29286     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29287         this.updateBox = true;
29288     }
29289
29290     this.activeHandle = null;
29291
29292     if(this.resizeChild){
29293         if(typeof this.resizeChild == "boolean"){
29294             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29295         }else{
29296             this.resizeChild = Roo.get(this.resizeChild, true);
29297         }
29298     }
29299     
29300     if(this.adjustments == "auto"){
29301         var rc = this.resizeChild;
29302         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29303         if(rc && (hw || hn)){
29304             rc.position("relative");
29305             rc.setLeft(hw ? hw.el.getWidth() : 0);
29306             rc.setTop(hn ? hn.el.getHeight() : 0);
29307         }
29308         this.adjustments = [
29309             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29310             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29311         ];
29312     }
29313
29314     if(this.draggable){
29315         this.dd = this.dynamic ?
29316             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29317         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29318     }
29319
29320     // public events
29321     this.addEvents({
29322         /**
29323          * @event beforeresize
29324          * Fired before resize is allowed. Set enabled to false to cancel resize.
29325          * @param {Roo.Resizable} this
29326          * @param {Roo.EventObject} e The mousedown event
29327          */
29328         "beforeresize" : true,
29329         /**
29330          * @event resizing
29331          * Fired a resizing.
29332          * @param {Roo.Resizable} this
29333          * @param {Number} x The new x position
29334          * @param {Number} y The new y position
29335          * @param {Number} w The new w width
29336          * @param {Number} h The new h hight
29337          * @param {Roo.EventObject} e The mouseup event
29338          */
29339         "resizing" : true,
29340         /**
29341          * @event resize
29342          * Fired after a resize.
29343          * @param {Roo.Resizable} this
29344          * @param {Number} width The new width
29345          * @param {Number} height The new height
29346          * @param {Roo.EventObject} e The mouseup event
29347          */
29348         "resize" : true
29349     });
29350
29351     if(this.width !== null && this.height !== null){
29352         this.resizeTo(this.width, this.height);
29353     }else{
29354         this.updateChildSize();
29355     }
29356     if(Roo.isIE){
29357         this.el.dom.style.zoom = 1;
29358     }
29359     Roo.Resizable.superclass.constructor.call(this);
29360 };
29361
29362 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29363         resizeChild : false,
29364         adjustments : [0, 0],
29365         minWidth : 5,
29366         minHeight : 5,
29367         maxWidth : 10000,
29368         maxHeight : 10000,
29369         enabled : true,
29370         animate : false,
29371         duration : .35,
29372         dynamic : false,
29373         handles : false,
29374         multiDirectional : false,
29375         disableTrackOver : false,
29376         easing : 'easeOutStrong',
29377         widthIncrement : 0,
29378         heightIncrement : 0,
29379         pinned : false,
29380         width : null,
29381         height : null,
29382         preserveRatio : false,
29383         transparent: false,
29384         minX: 0,
29385         minY: 0,
29386         draggable: false,
29387
29388         /**
29389          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29390          */
29391         constrainTo: undefined,
29392         /**
29393          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29394          */
29395         resizeRegion: undefined,
29396
29397
29398     /**
29399      * Perform a manual resize
29400      * @param {Number} width
29401      * @param {Number} height
29402      */
29403     resizeTo : function(width, height){
29404         this.el.setSize(width, height);
29405         this.updateChildSize();
29406         this.fireEvent("resize", this, width, height, null);
29407     },
29408
29409     // private
29410     startSizing : function(e, handle){
29411         this.fireEvent("beforeresize", this, e);
29412         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29413
29414             if(!this.overlay){
29415                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29416                 this.overlay.unselectable();
29417                 this.overlay.enableDisplayMode("block");
29418                 this.overlay.on("mousemove", this.onMouseMove, this);
29419                 this.overlay.on("mouseup", this.onMouseUp, this);
29420             }
29421             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29422
29423             this.resizing = true;
29424             this.startBox = this.el.getBox();
29425             this.startPoint = e.getXY();
29426             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29427                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29428
29429             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29430             this.overlay.show();
29431
29432             if(this.constrainTo) {
29433                 var ct = Roo.get(this.constrainTo);
29434                 this.resizeRegion = ct.getRegion().adjust(
29435                     ct.getFrameWidth('t'),
29436                     ct.getFrameWidth('l'),
29437                     -ct.getFrameWidth('b'),
29438                     -ct.getFrameWidth('r')
29439                 );
29440             }
29441
29442             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29443             this.proxy.show();
29444             this.proxy.setBox(this.startBox);
29445             if(!this.dynamic){
29446                 this.proxy.setStyle('visibility', 'visible');
29447             }
29448         }
29449     },
29450
29451     // private
29452     onMouseDown : function(handle, e){
29453         if(this.enabled){
29454             e.stopEvent();
29455             this.activeHandle = handle;
29456             this.startSizing(e, handle);
29457         }
29458     },
29459
29460     // private
29461     onMouseUp : function(e){
29462         var size = this.resizeElement();
29463         this.resizing = false;
29464         this.handleOut();
29465         this.overlay.hide();
29466         this.proxy.hide();
29467         this.fireEvent("resize", this, size.width, size.height, e);
29468     },
29469
29470     // private
29471     updateChildSize : function(){
29472         
29473         if(this.resizeChild){
29474             var el = this.el;
29475             var child = this.resizeChild;
29476             var adj = this.adjustments;
29477             if(el.dom.offsetWidth){
29478                 var b = el.getSize(true);
29479                 child.setSize(b.width+adj[0], b.height+adj[1]);
29480             }
29481             // Second call here for IE
29482             // The first call enables instant resizing and
29483             // the second call corrects scroll bars if they
29484             // exist
29485             if(Roo.isIE){
29486                 setTimeout(function(){
29487                     if(el.dom.offsetWidth){
29488                         var b = el.getSize(true);
29489                         child.setSize(b.width+adj[0], b.height+adj[1]);
29490                     }
29491                 }, 10);
29492             }
29493         }
29494     },
29495
29496     // private
29497     snap : function(value, inc, min){
29498         if(!inc || !value) return value;
29499         var newValue = value;
29500         var m = value % inc;
29501         if(m > 0){
29502             if(m > (inc/2)){
29503                 newValue = value + (inc-m);
29504             }else{
29505                 newValue = value - m;
29506             }
29507         }
29508         return Math.max(min, newValue);
29509     },
29510
29511     // private
29512     resizeElement : function(){
29513         var box = this.proxy.getBox();
29514         if(this.updateBox){
29515             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29516         }else{
29517             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29518         }
29519         this.updateChildSize();
29520         if(!this.dynamic){
29521             this.proxy.hide();
29522         }
29523         return box;
29524     },
29525
29526     // private
29527     constrain : function(v, diff, m, mx){
29528         if(v - diff < m){
29529             diff = v - m;
29530         }else if(v - diff > mx){
29531             diff = mx - v;
29532         }
29533         return diff;
29534     },
29535
29536     // private
29537     onMouseMove : function(e){
29538         
29539         if(this.enabled){
29540             try{// try catch so if something goes wrong the user doesn't get hung
29541
29542             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29543                 return;
29544             }
29545
29546             //var curXY = this.startPoint;
29547             var curSize = this.curSize || this.startBox;
29548             var x = this.startBox.x, y = this.startBox.y;
29549             var ox = x, oy = y;
29550             var w = curSize.width, h = curSize.height;
29551             var ow = w, oh = h;
29552             var mw = this.minWidth, mh = this.minHeight;
29553             var mxw = this.maxWidth, mxh = this.maxHeight;
29554             var wi = this.widthIncrement;
29555             var hi = this.heightIncrement;
29556
29557             var eventXY = e.getXY();
29558             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29559             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29560
29561             var pos = this.activeHandle.position;
29562
29563             switch(pos){
29564                 case "east":
29565                     w += diffX;
29566                     w = Math.min(Math.max(mw, w), mxw);
29567                     break;
29568              
29569                 case "south":
29570                     h += diffY;
29571                     h = Math.min(Math.max(mh, h), mxh);
29572                     break;
29573                 case "southeast":
29574                     w += diffX;
29575                     h += diffY;
29576                     w = Math.min(Math.max(mw, w), mxw);
29577                     h = Math.min(Math.max(mh, h), mxh);
29578                     break;
29579                 case "north":
29580                     diffY = this.constrain(h, diffY, mh, mxh);
29581                     y += diffY;
29582                     h -= diffY;
29583                     break;
29584                 case "hdrag":
29585                     
29586                     if (wi) {
29587                         var adiffX = Math.abs(diffX);
29588                         var sub = (adiffX % wi); // how much 
29589                         if (sub > (wi/2)) { // far enough to snap
29590                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29591                         } else {
29592                             // remove difference.. 
29593                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29594                         }
29595                     }
29596                     x += diffX;
29597                     x = Math.max(this.minX, x);
29598                     break;
29599                 case "west":
29600                     diffX = this.constrain(w, diffX, mw, mxw);
29601                     x += diffX;
29602                     w -= diffX;
29603                     break;
29604                 case "northeast":
29605                     w += diffX;
29606                     w = Math.min(Math.max(mw, w), mxw);
29607                     diffY = this.constrain(h, diffY, mh, mxh);
29608                     y += diffY;
29609                     h -= diffY;
29610                     break;
29611                 case "northwest":
29612                     diffX = this.constrain(w, diffX, mw, mxw);
29613                     diffY = this.constrain(h, diffY, mh, mxh);
29614                     y += diffY;
29615                     h -= diffY;
29616                     x += diffX;
29617                     w -= diffX;
29618                     break;
29619                case "southwest":
29620                     diffX = this.constrain(w, diffX, mw, mxw);
29621                     h += diffY;
29622                     h = Math.min(Math.max(mh, h), mxh);
29623                     x += diffX;
29624                     w -= diffX;
29625                     break;
29626             }
29627
29628             var sw = this.snap(w, wi, mw);
29629             var sh = this.snap(h, hi, mh);
29630             if(sw != w || sh != h){
29631                 switch(pos){
29632                     case "northeast":
29633                         y -= sh - h;
29634                     break;
29635                     case "north":
29636                         y -= sh - h;
29637                         break;
29638                     case "southwest":
29639                         x -= sw - w;
29640                     break;
29641                     case "west":
29642                         x -= sw - w;
29643                         break;
29644                     case "northwest":
29645                         x -= sw - w;
29646                         y -= sh - h;
29647                     break;
29648                 }
29649                 w = sw;
29650                 h = sh;
29651             }
29652
29653             if(this.preserveRatio){
29654                 switch(pos){
29655                     case "southeast":
29656                     case "east":
29657                         h = oh * (w/ow);
29658                         h = Math.min(Math.max(mh, h), mxh);
29659                         w = ow * (h/oh);
29660                        break;
29661                     case "south":
29662                         w = ow * (h/oh);
29663                         w = Math.min(Math.max(mw, w), mxw);
29664                         h = oh * (w/ow);
29665                         break;
29666                     case "northeast":
29667                         w = ow * (h/oh);
29668                         w = Math.min(Math.max(mw, w), mxw);
29669                         h = oh * (w/ow);
29670                     break;
29671                     case "north":
29672                         var tw = w;
29673                         w = ow * (h/oh);
29674                         w = Math.min(Math.max(mw, w), mxw);
29675                         h = oh * (w/ow);
29676                         x += (tw - w) / 2;
29677                         break;
29678                     case "southwest":
29679                         h = oh * (w/ow);
29680                         h = Math.min(Math.max(mh, h), mxh);
29681                         var tw = w;
29682                         w = ow * (h/oh);
29683                         x += tw - w;
29684                         break;
29685                     case "west":
29686                         var th = h;
29687                         h = oh * (w/ow);
29688                         h = Math.min(Math.max(mh, h), mxh);
29689                         y += (th - h) / 2;
29690                         var tw = w;
29691                         w = ow * (h/oh);
29692                         x += tw - w;
29693                        break;
29694                     case "northwest":
29695                         var tw = w;
29696                         var th = h;
29697                         h = oh * (w/ow);
29698                         h = Math.min(Math.max(mh, h), mxh);
29699                         w = ow * (h/oh);
29700                         y += th - h;
29701                         x += tw - w;
29702                        break;
29703
29704                 }
29705             }
29706             if (pos == 'hdrag') {
29707                 w = ow;
29708             }
29709             this.proxy.setBounds(x, y, w, h);
29710             if(this.dynamic){
29711                 this.resizeElement();
29712             }
29713             }catch(e){}
29714         }
29715         this.fireEvent("resizing", this, x, y, w, h, e);
29716     },
29717
29718     // private
29719     handleOver : function(){
29720         if(this.enabled){
29721             this.el.addClass("x-resizable-over");
29722         }
29723     },
29724
29725     // private
29726     handleOut : function(){
29727         if(!this.resizing){
29728             this.el.removeClass("x-resizable-over");
29729         }
29730     },
29731
29732     /**
29733      * Returns the element this component is bound to.
29734      * @return {Roo.Element}
29735      */
29736     getEl : function(){
29737         return this.el;
29738     },
29739
29740     /**
29741      * Returns the resizeChild element (or null).
29742      * @return {Roo.Element}
29743      */
29744     getResizeChild : function(){
29745         return this.resizeChild;
29746     },
29747     groupHandler : function()
29748     {
29749         
29750     },
29751     /**
29752      * Destroys this resizable. If the element was wrapped and
29753      * removeEl is not true then the element remains.
29754      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29755      */
29756     destroy : function(removeEl){
29757         this.proxy.remove();
29758         if(this.overlay){
29759             this.overlay.removeAllListeners();
29760             this.overlay.remove();
29761         }
29762         var ps = Roo.Resizable.positions;
29763         for(var k in ps){
29764             if(typeof ps[k] != "function" && this[ps[k]]){
29765                 var h = this[ps[k]];
29766                 h.el.removeAllListeners();
29767                 h.el.remove();
29768             }
29769         }
29770         if(removeEl){
29771             this.el.update("");
29772             this.el.remove();
29773         }
29774     }
29775 });
29776
29777 // private
29778 // hash to map config positions to true positions
29779 Roo.Resizable.positions = {
29780     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29781     hd: "hdrag"
29782 };
29783
29784 // private
29785 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29786     if(!this.tpl){
29787         // only initialize the template if resizable is used
29788         var tpl = Roo.DomHelper.createTemplate(
29789             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29790         );
29791         tpl.compile();
29792         Roo.Resizable.Handle.prototype.tpl = tpl;
29793     }
29794     this.position = pos;
29795     this.rz = rz;
29796     // show north drag fro topdra
29797     var handlepos = pos == 'hdrag' ? 'north' : pos;
29798     
29799     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29800     if (pos == 'hdrag') {
29801         this.el.setStyle('cursor', 'pointer');
29802     }
29803     this.el.unselectable();
29804     if(transparent){
29805         this.el.setOpacity(0);
29806     }
29807     this.el.on("mousedown", this.onMouseDown, this);
29808     if(!disableTrackOver){
29809         this.el.on("mouseover", this.onMouseOver, this);
29810         this.el.on("mouseout", this.onMouseOut, this);
29811     }
29812 };
29813
29814 // private
29815 Roo.Resizable.Handle.prototype = {
29816     afterResize : function(rz){
29817         Roo.log('after?');
29818         // do nothing
29819     },
29820     // private
29821     onMouseDown : function(e){
29822         this.rz.onMouseDown(this, e);
29823     },
29824     // private
29825     onMouseOver : function(e){
29826         this.rz.handleOver(this, e);
29827     },
29828     // private
29829     onMouseOut : function(e){
29830         this.rz.handleOut(this, e);
29831     }
29832 };/*
29833  * Based on:
29834  * Ext JS Library 1.1.1
29835  * Copyright(c) 2006-2007, Ext JS, LLC.
29836  *
29837  * Originally Released Under LGPL - original licence link has changed is not relivant.
29838  *
29839  * Fork - LGPL
29840  * <script type="text/javascript">
29841  */
29842
29843 /**
29844  * @class Roo.Editor
29845  * @extends Roo.Component
29846  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29847  * @constructor
29848  * Create a new Editor
29849  * @param {Roo.form.Field} field The Field object (or descendant)
29850  * @param {Object} config The config object
29851  */
29852 Roo.Editor = function(field, config){
29853     Roo.Editor.superclass.constructor.call(this, config);
29854     this.field = field;
29855     this.addEvents({
29856         /**
29857              * @event beforestartedit
29858              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29859              * false from the handler of this event.
29860              * @param {Editor} this
29861              * @param {Roo.Element} boundEl The underlying element bound to this editor
29862              * @param {Mixed} value The field value being set
29863              */
29864         "beforestartedit" : true,
29865         /**
29866              * @event startedit
29867              * Fires when this editor is displayed
29868              * @param {Roo.Element} boundEl The underlying element bound to this editor
29869              * @param {Mixed} value The starting field value
29870              */
29871         "startedit" : true,
29872         /**
29873              * @event beforecomplete
29874              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29875              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29876              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29877              * event will not fire since no edit actually occurred.
29878              * @param {Editor} this
29879              * @param {Mixed} value The current field value
29880              * @param {Mixed} startValue The original field value
29881              */
29882         "beforecomplete" : true,
29883         /**
29884              * @event complete
29885              * Fires after editing is complete and any changed value has been written to the underlying field.
29886              * @param {Editor} this
29887              * @param {Mixed} value The current field value
29888              * @param {Mixed} startValue The original field value
29889              */
29890         "complete" : true,
29891         /**
29892          * @event specialkey
29893          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29894          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29895          * @param {Roo.form.Field} this
29896          * @param {Roo.EventObject} e The event object
29897          */
29898         "specialkey" : true
29899     });
29900 };
29901
29902 Roo.extend(Roo.Editor, Roo.Component, {
29903     /**
29904      * @cfg {Boolean/String} autosize
29905      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29906      * or "height" to adopt the height only (defaults to false)
29907      */
29908     /**
29909      * @cfg {Boolean} revertInvalid
29910      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29911      * validation fails (defaults to true)
29912      */
29913     /**
29914      * @cfg {Boolean} ignoreNoChange
29915      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29916      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29917      * will never be ignored.
29918      */
29919     /**
29920      * @cfg {Boolean} hideEl
29921      * False to keep the bound element visible while the editor is displayed (defaults to true)
29922      */
29923     /**
29924      * @cfg {Mixed} value
29925      * The data value of the underlying field (defaults to "")
29926      */
29927     value : "",
29928     /**
29929      * @cfg {String} alignment
29930      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29931      */
29932     alignment: "c-c?",
29933     /**
29934      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29935      * for bottom-right shadow (defaults to "frame")
29936      */
29937     shadow : "frame",
29938     /**
29939      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29940      */
29941     constrain : false,
29942     /**
29943      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29944      */
29945     completeOnEnter : false,
29946     /**
29947      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29948      */
29949     cancelOnEsc : false,
29950     /**
29951      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29952      */
29953     updateEl : false,
29954
29955     // private
29956     onRender : function(ct, position){
29957         this.el = new Roo.Layer({
29958             shadow: this.shadow,
29959             cls: "x-editor",
29960             parentEl : ct,
29961             shim : this.shim,
29962             shadowOffset:4,
29963             id: this.id,
29964             constrain: this.constrain
29965         });
29966         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29967         if(this.field.msgTarget != 'title'){
29968             this.field.msgTarget = 'qtip';
29969         }
29970         this.field.render(this.el);
29971         if(Roo.isGecko){
29972             this.field.el.dom.setAttribute('autocomplete', 'off');
29973         }
29974         this.field.on("specialkey", this.onSpecialKey, this);
29975         if(this.swallowKeys){
29976             this.field.el.swallowEvent(['keydown','keypress']);
29977         }
29978         this.field.show();
29979         this.field.on("blur", this.onBlur, this);
29980         if(this.field.grow){
29981             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29982         }
29983     },
29984
29985     onSpecialKey : function(field, e)
29986     {
29987         //Roo.log('editor onSpecialKey');
29988         if(this.completeOnEnter && e.getKey() == e.ENTER){
29989             e.stopEvent();
29990             this.completeEdit();
29991             return;
29992         }
29993         // do not fire special key otherwise it might hide close the editor...
29994         if(e.getKey() == e.ENTER){    
29995             return;
29996         }
29997         if(this.cancelOnEsc && e.getKey() == e.ESC){
29998             this.cancelEdit();
29999             return;
30000         } 
30001         this.fireEvent('specialkey', field, e);
30002     
30003     },
30004
30005     /**
30006      * Starts the editing process and shows the editor.
30007      * @param {String/HTMLElement/Element} el The element to edit
30008      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
30009       * to the innerHTML of el.
30010      */
30011     startEdit : function(el, value){
30012         if(this.editing){
30013             this.completeEdit();
30014         }
30015         this.boundEl = Roo.get(el);
30016         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
30017         if(!this.rendered){
30018             this.render(this.parentEl || document.body);
30019         }
30020         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
30021             return;
30022         }
30023         this.startValue = v;
30024         this.field.setValue(v);
30025         if(this.autoSize){
30026             var sz = this.boundEl.getSize();
30027             switch(this.autoSize){
30028                 case "width":
30029                 this.setSize(sz.width,  "");
30030                 break;
30031                 case "height":
30032                 this.setSize("",  sz.height);
30033                 break;
30034                 default:
30035                 this.setSize(sz.width,  sz.height);
30036             }
30037         }
30038         this.el.alignTo(this.boundEl, this.alignment);
30039         this.editing = true;
30040         if(Roo.QuickTips){
30041             Roo.QuickTips.disable();
30042         }
30043         this.show();
30044     },
30045
30046     /**
30047      * Sets the height and width of this editor.
30048      * @param {Number} width The new width
30049      * @param {Number} height The new height
30050      */
30051     setSize : function(w, h){
30052         this.field.setSize(w, h);
30053         if(this.el){
30054             this.el.sync();
30055         }
30056     },
30057
30058     /**
30059      * Realigns the editor to the bound field based on the current alignment config value.
30060      */
30061     realign : function(){
30062         this.el.alignTo(this.boundEl, this.alignment);
30063     },
30064
30065     /**
30066      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
30067      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
30068      */
30069     completeEdit : function(remainVisible){
30070         if(!this.editing){
30071             return;
30072         }
30073         var v = this.getValue();
30074         if(this.revertInvalid !== false && !this.field.isValid()){
30075             v = this.startValue;
30076             this.cancelEdit(true);
30077         }
30078         if(String(v) === String(this.startValue) && this.ignoreNoChange){
30079             this.editing = false;
30080             this.hide();
30081             return;
30082         }
30083         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
30084             this.editing = false;
30085             if(this.updateEl && this.boundEl){
30086                 this.boundEl.update(v);
30087             }
30088             if(remainVisible !== true){
30089                 this.hide();
30090             }
30091             this.fireEvent("complete", this, v, this.startValue);
30092         }
30093     },
30094
30095     // private
30096     onShow : function(){
30097         this.el.show();
30098         if(this.hideEl !== false){
30099             this.boundEl.hide();
30100         }
30101         this.field.show();
30102         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
30103             this.fixIEFocus = true;
30104             this.deferredFocus.defer(50, this);
30105         }else{
30106             this.field.focus();
30107         }
30108         this.fireEvent("startedit", this.boundEl, this.startValue);
30109     },
30110
30111     deferredFocus : function(){
30112         if(this.editing){
30113             this.field.focus();
30114         }
30115     },
30116
30117     /**
30118      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
30119      * reverted to the original starting value.
30120      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
30121      * cancel (defaults to false)
30122      */
30123     cancelEdit : function(remainVisible){
30124         if(this.editing){
30125             this.setValue(this.startValue);
30126             if(remainVisible !== true){
30127                 this.hide();
30128             }
30129         }
30130     },
30131
30132     // private
30133     onBlur : function(){
30134         if(this.allowBlur !== true && this.editing){
30135             this.completeEdit();
30136         }
30137     },
30138
30139     // private
30140     onHide : function(){
30141         if(this.editing){
30142             this.completeEdit();
30143             return;
30144         }
30145         this.field.blur();
30146         if(this.field.collapse){
30147             this.field.collapse();
30148         }
30149         this.el.hide();
30150         if(this.hideEl !== false){
30151             this.boundEl.show();
30152         }
30153         if(Roo.QuickTips){
30154             Roo.QuickTips.enable();
30155         }
30156     },
30157
30158     /**
30159      * Sets the data value of the editor
30160      * @param {Mixed} value Any valid value supported by the underlying field
30161      */
30162     setValue : function(v){
30163         this.field.setValue(v);
30164     },
30165
30166     /**
30167      * Gets the data value of the editor
30168      * @return {Mixed} The data value
30169      */
30170     getValue : function(){
30171         return this.field.getValue();
30172     }
30173 });/*
30174  * Based on:
30175  * Ext JS Library 1.1.1
30176  * Copyright(c) 2006-2007, Ext JS, LLC.
30177  *
30178  * Originally Released Under LGPL - original licence link has changed is not relivant.
30179  *
30180  * Fork - LGPL
30181  * <script type="text/javascript">
30182  */
30183  
30184 /**
30185  * @class Roo.BasicDialog
30186  * @extends Roo.util.Observable
30187  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
30188  * <pre><code>
30189 var dlg = new Roo.BasicDialog("my-dlg", {
30190     height: 200,
30191     width: 300,
30192     minHeight: 100,
30193     minWidth: 150,
30194     modal: true,
30195     proxyDrag: true,
30196     shadow: true
30197 });
30198 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
30199 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
30200 dlg.addButton('Cancel', dlg.hide, dlg);
30201 dlg.show();
30202 </code></pre>
30203   <b>A Dialog should always be a direct child of the body element.</b>
30204  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30205  * @cfg {String} title Default text to display in the title bar (defaults to null)
30206  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30207  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30208  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30209  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30210  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30211  * (defaults to null with no animation)
30212  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30213  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30214  * property for valid values (defaults to 'all')
30215  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30216  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30217  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30218  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30219  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30220  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30221  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30222  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30223  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30224  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30225  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30226  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30227  * draggable = true (defaults to false)
30228  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30229  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30230  * shadow (defaults to false)
30231  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30232  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30233  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30234  * @cfg {Array} buttons Array of buttons
30235  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30236  * @constructor
30237  * Create a new BasicDialog.
30238  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30239  * @param {Object} config Configuration options
30240  */
30241 Roo.BasicDialog = function(el, config){
30242     this.el = Roo.get(el);
30243     var dh = Roo.DomHelper;
30244     if(!this.el && config && config.autoCreate){
30245         if(typeof config.autoCreate == "object"){
30246             if(!config.autoCreate.id){
30247                 config.autoCreate.id = el;
30248             }
30249             this.el = dh.append(document.body,
30250                         config.autoCreate, true);
30251         }else{
30252             this.el = dh.append(document.body,
30253                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30254         }
30255     }
30256     el = this.el;
30257     el.setDisplayed(true);
30258     el.hide = this.hideAction;
30259     this.id = el.id;
30260     el.addClass("x-dlg");
30261
30262     Roo.apply(this, config);
30263
30264     this.proxy = el.createProxy("x-dlg-proxy");
30265     this.proxy.hide = this.hideAction;
30266     this.proxy.setOpacity(.5);
30267     this.proxy.hide();
30268
30269     if(config.width){
30270         el.setWidth(config.width);
30271     }
30272     if(config.height){
30273         el.setHeight(config.height);
30274     }
30275     this.size = el.getSize();
30276     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30277         this.xy = [config.x,config.y];
30278     }else{
30279         this.xy = el.getCenterXY(true);
30280     }
30281     /** The header element @type Roo.Element */
30282     this.header = el.child("> .x-dlg-hd");
30283     /** The body element @type Roo.Element */
30284     this.body = el.child("> .x-dlg-bd");
30285     /** The footer element @type Roo.Element */
30286     this.footer = el.child("> .x-dlg-ft");
30287
30288     if(!this.header){
30289         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30290     }
30291     if(!this.body){
30292         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30293     }
30294
30295     this.header.unselectable();
30296     if(this.title){
30297         this.header.update(this.title);
30298     }
30299     // this element allows the dialog to be focused for keyboard event
30300     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30301     this.focusEl.swallowEvent("click", true);
30302
30303     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30304
30305     // wrap the body and footer for special rendering
30306     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30307     if(this.footer){
30308         this.bwrap.dom.appendChild(this.footer.dom);
30309     }
30310
30311     this.bg = this.el.createChild({
30312         tag: "div", cls:"x-dlg-bg",
30313         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30314     });
30315     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30316
30317
30318     if(this.autoScroll !== false && !this.autoTabs){
30319         this.body.setStyle("overflow", "auto");
30320     }
30321
30322     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30323
30324     if(this.closable !== false){
30325         this.el.addClass("x-dlg-closable");
30326         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30327         this.close.on("click", this.closeClick, this);
30328         this.close.addClassOnOver("x-dlg-close-over");
30329     }
30330     if(this.collapsible !== false){
30331         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30332         this.collapseBtn.on("click", this.collapseClick, this);
30333         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30334         this.header.on("dblclick", this.collapseClick, this);
30335     }
30336     if(this.resizable !== false){
30337         this.el.addClass("x-dlg-resizable");
30338         this.resizer = new Roo.Resizable(el, {
30339             minWidth: this.minWidth || 80,
30340             minHeight:this.minHeight || 80,
30341             handles: this.resizeHandles || "all",
30342             pinned: true
30343         });
30344         this.resizer.on("beforeresize", this.beforeResize, this);
30345         this.resizer.on("resize", this.onResize, this);
30346     }
30347     if(this.draggable !== false){
30348         el.addClass("x-dlg-draggable");
30349         if (!this.proxyDrag) {
30350             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30351         }
30352         else {
30353             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30354         }
30355         dd.setHandleElId(this.header.id);
30356         dd.endDrag = this.endMove.createDelegate(this);
30357         dd.startDrag = this.startMove.createDelegate(this);
30358         dd.onDrag = this.onDrag.createDelegate(this);
30359         dd.scroll = false;
30360         this.dd = dd;
30361     }
30362     if(this.modal){
30363         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30364         this.mask.enableDisplayMode("block");
30365         this.mask.hide();
30366         this.el.addClass("x-dlg-modal");
30367     }
30368     if(this.shadow){
30369         this.shadow = new Roo.Shadow({
30370             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30371             offset : this.shadowOffset
30372         });
30373     }else{
30374         this.shadowOffset = 0;
30375     }
30376     if(Roo.useShims && this.shim !== false){
30377         this.shim = this.el.createShim();
30378         this.shim.hide = this.hideAction;
30379         this.shim.hide();
30380     }else{
30381         this.shim = false;
30382     }
30383     if(this.autoTabs){
30384         this.initTabs();
30385     }
30386     if (this.buttons) { 
30387         var bts= this.buttons;
30388         this.buttons = [];
30389         Roo.each(bts, function(b) {
30390             this.addButton(b);
30391         }, this);
30392     }
30393     
30394     
30395     this.addEvents({
30396         /**
30397          * @event keydown
30398          * Fires when a key is pressed
30399          * @param {Roo.BasicDialog} this
30400          * @param {Roo.EventObject} e
30401          */
30402         "keydown" : true,
30403         /**
30404          * @event move
30405          * Fires when this dialog is moved by the user.
30406          * @param {Roo.BasicDialog} this
30407          * @param {Number} x The new page X
30408          * @param {Number} y The new page Y
30409          */
30410         "move" : true,
30411         /**
30412          * @event resize
30413          * Fires when this dialog is resized by the user.
30414          * @param {Roo.BasicDialog} this
30415          * @param {Number} width The new width
30416          * @param {Number} height The new height
30417          */
30418         "resize" : true,
30419         /**
30420          * @event beforehide
30421          * Fires before this dialog is hidden.
30422          * @param {Roo.BasicDialog} this
30423          */
30424         "beforehide" : true,
30425         /**
30426          * @event hide
30427          * Fires when this dialog is hidden.
30428          * @param {Roo.BasicDialog} this
30429          */
30430         "hide" : true,
30431         /**
30432          * @event beforeshow
30433          * Fires before this dialog is shown.
30434          * @param {Roo.BasicDialog} this
30435          */
30436         "beforeshow" : true,
30437         /**
30438          * @event show
30439          * Fires when this dialog is shown.
30440          * @param {Roo.BasicDialog} this
30441          */
30442         "show" : true
30443     });
30444     el.on("keydown", this.onKeyDown, this);
30445     el.on("mousedown", this.toFront, this);
30446     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30447     this.el.hide();
30448     Roo.DialogManager.register(this);
30449     Roo.BasicDialog.superclass.constructor.call(this);
30450 };
30451
30452 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30453     shadowOffset: Roo.isIE ? 6 : 5,
30454     minHeight: 80,
30455     minWidth: 200,
30456     minButtonWidth: 75,
30457     defaultButton: null,
30458     buttonAlign: "right",
30459     tabTag: 'div',
30460     firstShow: true,
30461
30462     /**
30463      * Sets the dialog title text
30464      * @param {String} text The title text to display
30465      * @return {Roo.BasicDialog} this
30466      */
30467     setTitle : function(text){
30468         this.header.update(text);
30469         return this;
30470     },
30471
30472     // private
30473     closeClick : function(){
30474         this.hide();
30475     },
30476
30477     // private
30478     collapseClick : function(){
30479         this[this.collapsed ? "expand" : "collapse"]();
30480     },
30481
30482     /**
30483      * Collapses the dialog to its minimized state (only the title bar is visible).
30484      * Equivalent to the user clicking the collapse dialog button.
30485      */
30486     collapse : function(){
30487         if(!this.collapsed){
30488             this.collapsed = true;
30489             this.el.addClass("x-dlg-collapsed");
30490             this.restoreHeight = this.el.getHeight();
30491             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30492         }
30493     },
30494
30495     /**
30496      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30497      * clicking the expand dialog button.
30498      */
30499     expand : function(){
30500         if(this.collapsed){
30501             this.collapsed = false;
30502             this.el.removeClass("x-dlg-collapsed");
30503             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30504         }
30505     },
30506
30507     /**
30508      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30509      * @return {Roo.TabPanel} The tabs component
30510      */
30511     initTabs : function(){
30512         var tabs = this.getTabs();
30513         while(tabs.getTab(0)){
30514             tabs.removeTab(0);
30515         }
30516         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30517             var dom = el.dom;
30518             tabs.addTab(Roo.id(dom), dom.title);
30519             dom.title = "";
30520         });
30521         tabs.activate(0);
30522         return tabs;
30523     },
30524
30525     // private
30526     beforeResize : function(){
30527         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30528     },
30529
30530     // private
30531     onResize : function(){
30532         this.refreshSize();
30533         this.syncBodyHeight();
30534         this.adjustAssets();
30535         this.focus();
30536         this.fireEvent("resize", this, this.size.width, this.size.height);
30537     },
30538
30539     // private
30540     onKeyDown : function(e){
30541         if(this.isVisible()){
30542             this.fireEvent("keydown", this, e);
30543         }
30544     },
30545
30546     /**
30547      * Resizes the dialog.
30548      * @param {Number} width
30549      * @param {Number} height
30550      * @return {Roo.BasicDialog} this
30551      */
30552     resizeTo : function(width, height){
30553         this.el.setSize(width, height);
30554         this.size = {width: width, height: height};
30555         this.syncBodyHeight();
30556         if(this.fixedcenter){
30557             this.center();
30558         }
30559         if(this.isVisible()){
30560             this.constrainXY();
30561             this.adjustAssets();
30562         }
30563         this.fireEvent("resize", this, width, height);
30564         return this;
30565     },
30566
30567
30568     /**
30569      * Resizes the dialog to fit the specified content size.
30570      * @param {Number} width
30571      * @param {Number} height
30572      * @return {Roo.BasicDialog} this
30573      */
30574     setContentSize : function(w, h){
30575         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30576         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30577         //if(!this.el.isBorderBox()){
30578             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30579             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30580         //}
30581         if(this.tabs){
30582             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30583             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30584         }
30585         this.resizeTo(w, h);
30586         return this;
30587     },
30588
30589     /**
30590      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30591      * executed in response to a particular key being pressed while the dialog is active.
30592      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30593      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30594      * @param {Function} fn The function to call
30595      * @param {Object} scope (optional) The scope of the function
30596      * @return {Roo.BasicDialog} this
30597      */
30598     addKeyListener : function(key, fn, scope){
30599         var keyCode, shift, ctrl, alt;
30600         if(typeof key == "object" && !(key instanceof Array)){
30601             keyCode = key["key"];
30602             shift = key["shift"];
30603             ctrl = key["ctrl"];
30604             alt = key["alt"];
30605         }else{
30606             keyCode = key;
30607         }
30608         var handler = function(dlg, e){
30609             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30610                 var k = e.getKey();
30611                 if(keyCode instanceof Array){
30612                     for(var i = 0, len = keyCode.length; i < len; i++){
30613                         if(keyCode[i] == k){
30614                           fn.call(scope || window, dlg, k, e);
30615                           return;
30616                         }
30617                     }
30618                 }else{
30619                     if(k == keyCode){
30620                         fn.call(scope || window, dlg, k, e);
30621                     }
30622                 }
30623             }
30624         };
30625         this.on("keydown", handler);
30626         return this;
30627     },
30628
30629     /**
30630      * Returns the TabPanel component (creates it if it doesn't exist).
30631      * Note: If you wish to simply check for the existence of tabs without creating them,
30632      * check for a null 'tabs' property.
30633      * @return {Roo.TabPanel} The tabs component
30634      */
30635     getTabs : function(){
30636         if(!this.tabs){
30637             this.el.addClass("x-dlg-auto-tabs");
30638             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30639             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30640         }
30641         return this.tabs;
30642     },
30643
30644     /**
30645      * Adds a button to the footer section of the dialog.
30646      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30647      * object or a valid Roo.DomHelper element config
30648      * @param {Function} handler The function called when the button is clicked
30649      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30650      * @return {Roo.Button} The new button
30651      */
30652     addButton : function(config, handler, scope){
30653         var dh = Roo.DomHelper;
30654         if(!this.footer){
30655             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30656         }
30657         if(!this.btnContainer){
30658             var tb = this.footer.createChild({
30659
30660                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30661                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30662             }, null, true);
30663             this.btnContainer = tb.firstChild.firstChild.firstChild;
30664         }
30665         var bconfig = {
30666             handler: handler,
30667             scope: scope,
30668             minWidth: this.minButtonWidth,
30669             hideParent:true
30670         };
30671         if(typeof config == "string"){
30672             bconfig.text = config;
30673         }else{
30674             if(config.tag){
30675                 bconfig.dhconfig = config;
30676             }else{
30677                 Roo.apply(bconfig, config);
30678             }
30679         }
30680         var fc = false;
30681         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30682             bconfig.position = Math.max(0, bconfig.position);
30683             fc = this.btnContainer.childNodes[bconfig.position];
30684         }
30685          
30686         var btn = new Roo.Button(
30687             fc ? 
30688                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30689                 : this.btnContainer.appendChild(document.createElement("td")),
30690             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30691             bconfig
30692         );
30693         this.syncBodyHeight();
30694         if(!this.buttons){
30695             /**
30696              * Array of all the buttons that have been added to this dialog via addButton
30697              * @type Array
30698              */
30699             this.buttons = [];
30700         }
30701         this.buttons.push(btn);
30702         return btn;
30703     },
30704
30705     /**
30706      * Sets the default button to be focused when the dialog is displayed.
30707      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30708      * @return {Roo.BasicDialog} this
30709      */
30710     setDefaultButton : function(btn){
30711         this.defaultButton = btn;
30712         return this;
30713     },
30714
30715     // private
30716     getHeaderFooterHeight : function(safe){
30717         var height = 0;
30718         if(this.header){
30719            height += this.header.getHeight();
30720         }
30721         if(this.footer){
30722            var fm = this.footer.getMargins();
30723             height += (this.footer.getHeight()+fm.top+fm.bottom);
30724         }
30725         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30726         height += this.centerBg.getPadding("tb");
30727         return height;
30728     },
30729
30730     // private
30731     syncBodyHeight : function()
30732     {
30733         var bd = this.body, // the text
30734             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30735             bw = this.bwrap;
30736         var height = this.size.height - this.getHeaderFooterHeight(false);
30737         bd.setHeight(height-bd.getMargins("tb"));
30738         var hh = this.header.getHeight();
30739         var h = this.size.height-hh;
30740         cb.setHeight(h);
30741         
30742         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30743         bw.setHeight(h-cb.getPadding("tb"));
30744         
30745         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30746         bd.setWidth(bw.getWidth(true));
30747         if(this.tabs){
30748             this.tabs.syncHeight();
30749             if(Roo.isIE){
30750                 this.tabs.el.repaint();
30751             }
30752         }
30753     },
30754
30755     /**
30756      * Restores the previous state of the dialog if Roo.state is configured.
30757      * @return {Roo.BasicDialog} this
30758      */
30759     restoreState : function(){
30760         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30761         if(box && box.width){
30762             this.xy = [box.x, box.y];
30763             this.resizeTo(box.width, box.height);
30764         }
30765         return this;
30766     },
30767
30768     // private
30769     beforeShow : function(){
30770         this.expand();
30771         if(this.fixedcenter){
30772             this.xy = this.el.getCenterXY(true);
30773         }
30774         if(this.modal){
30775             Roo.get(document.body).addClass("x-body-masked");
30776             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30777             this.mask.show();
30778         }
30779         this.constrainXY();
30780     },
30781
30782     // private
30783     animShow : function(){
30784         var b = Roo.get(this.animateTarget).getBox();
30785         this.proxy.setSize(b.width, b.height);
30786         this.proxy.setLocation(b.x, b.y);
30787         this.proxy.show();
30788         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30789                     true, .35, this.showEl.createDelegate(this));
30790     },
30791
30792     /**
30793      * Shows the dialog.
30794      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30795      * @return {Roo.BasicDialog} this
30796      */
30797     show : function(animateTarget){
30798         if (this.fireEvent("beforeshow", this) === false){
30799             return;
30800         }
30801         if(this.syncHeightBeforeShow){
30802             this.syncBodyHeight();
30803         }else if(this.firstShow){
30804             this.firstShow = false;
30805             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30806         }
30807         this.animateTarget = animateTarget || this.animateTarget;
30808         if(!this.el.isVisible()){
30809             this.beforeShow();
30810             if(this.animateTarget && Roo.get(this.animateTarget)){
30811                 this.animShow();
30812             }else{
30813                 this.showEl();
30814             }
30815         }
30816         return this;
30817     },
30818
30819     // private
30820     showEl : function(){
30821         this.proxy.hide();
30822         this.el.setXY(this.xy);
30823         this.el.show();
30824         this.adjustAssets(true);
30825         this.toFront();
30826         this.focus();
30827         // IE peekaboo bug - fix found by Dave Fenwick
30828         if(Roo.isIE){
30829             this.el.repaint();
30830         }
30831         this.fireEvent("show", this);
30832     },
30833
30834     /**
30835      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30836      * dialog itself will receive focus.
30837      */
30838     focus : function(){
30839         if(this.defaultButton){
30840             this.defaultButton.focus();
30841         }else{
30842             this.focusEl.focus();
30843         }
30844     },
30845
30846     // private
30847     constrainXY : function(){
30848         if(this.constraintoviewport !== false){
30849             if(!this.viewSize){
30850                 if(this.container){
30851                     var s = this.container.getSize();
30852                     this.viewSize = [s.width, s.height];
30853                 }else{
30854                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30855                 }
30856             }
30857             var s = Roo.get(this.container||document).getScroll();
30858
30859             var x = this.xy[0], y = this.xy[1];
30860             var w = this.size.width, h = this.size.height;
30861             var vw = this.viewSize[0], vh = this.viewSize[1];
30862             // only move it if it needs it
30863             var moved = false;
30864             // first validate right/bottom
30865             if(x + w > vw+s.left){
30866                 x = vw - w;
30867                 moved = true;
30868             }
30869             if(y + h > vh+s.top){
30870                 y = vh - h;
30871                 moved = true;
30872             }
30873             // then make sure top/left isn't negative
30874             if(x < s.left){
30875                 x = s.left;
30876                 moved = true;
30877             }
30878             if(y < s.top){
30879                 y = s.top;
30880                 moved = true;
30881             }
30882             if(moved){
30883                 // cache xy
30884                 this.xy = [x, y];
30885                 if(this.isVisible()){
30886                     this.el.setLocation(x, y);
30887                     this.adjustAssets();
30888                 }
30889             }
30890         }
30891     },
30892
30893     // private
30894     onDrag : function(){
30895         if(!this.proxyDrag){
30896             this.xy = this.el.getXY();
30897             this.adjustAssets();
30898         }
30899     },
30900
30901     // private
30902     adjustAssets : function(doShow){
30903         var x = this.xy[0], y = this.xy[1];
30904         var w = this.size.width, h = this.size.height;
30905         if(doShow === true){
30906             if(this.shadow){
30907                 this.shadow.show(this.el);
30908             }
30909             if(this.shim){
30910                 this.shim.show();
30911             }
30912         }
30913         if(this.shadow && this.shadow.isVisible()){
30914             this.shadow.show(this.el);
30915         }
30916         if(this.shim && this.shim.isVisible()){
30917             this.shim.setBounds(x, y, w, h);
30918         }
30919     },
30920
30921     // private
30922     adjustViewport : function(w, h){
30923         if(!w || !h){
30924             w = Roo.lib.Dom.getViewWidth();
30925             h = Roo.lib.Dom.getViewHeight();
30926         }
30927         // cache the size
30928         this.viewSize = [w, h];
30929         if(this.modal && this.mask.isVisible()){
30930             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30931             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30932         }
30933         if(this.isVisible()){
30934             this.constrainXY();
30935         }
30936     },
30937
30938     /**
30939      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30940      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30941      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30942      */
30943     destroy : function(removeEl){
30944         if(this.isVisible()){
30945             this.animateTarget = null;
30946             this.hide();
30947         }
30948         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30949         if(this.tabs){
30950             this.tabs.destroy(removeEl);
30951         }
30952         Roo.destroy(
30953              this.shim,
30954              this.proxy,
30955              this.resizer,
30956              this.close,
30957              this.mask
30958         );
30959         if(this.dd){
30960             this.dd.unreg();
30961         }
30962         if(this.buttons){
30963            for(var i = 0, len = this.buttons.length; i < len; i++){
30964                this.buttons[i].destroy();
30965            }
30966         }
30967         this.el.removeAllListeners();
30968         if(removeEl === true){
30969             this.el.update("");
30970             this.el.remove();
30971         }
30972         Roo.DialogManager.unregister(this);
30973     },
30974
30975     // private
30976     startMove : function(){
30977         if(this.proxyDrag){
30978             this.proxy.show();
30979         }
30980         if(this.constraintoviewport !== false){
30981             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30982         }
30983     },
30984
30985     // private
30986     endMove : function(){
30987         if(!this.proxyDrag){
30988             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30989         }else{
30990             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30991             this.proxy.hide();
30992         }
30993         this.refreshSize();
30994         this.adjustAssets();
30995         this.focus();
30996         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30997     },
30998
30999     /**
31000      * Brings this dialog to the front of any other visible dialogs
31001      * @return {Roo.BasicDialog} this
31002      */
31003     toFront : function(){
31004         Roo.DialogManager.bringToFront(this);
31005         return this;
31006     },
31007
31008     /**
31009      * Sends this dialog to the back (under) of any other visible dialogs
31010      * @return {Roo.BasicDialog} this
31011      */
31012     toBack : function(){
31013         Roo.DialogManager.sendToBack(this);
31014         return this;
31015     },
31016
31017     /**
31018      * Centers this dialog in the viewport
31019      * @return {Roo.BasicDialog} this
31020      */
31021     center : function(){
31022         var xy = this.el.getCenterXY(true);
31023         this.moveTo(xy[0], xy[1]);
31024         return this;
31025     },
31026
31027     /**
31028      * Moves the dialog's top-left corner to the specified point
31029      * @param {Number} x
31030      * @param {Number} y
31031      * @return {Roo.BasicDialog} this
31032      */
31033     moveTo : function(x, y){
31034         this.xy = [x,y];
31035         if(this.isVisible()){
31036             this.el.setXY(this.xy);
31037             this.adjustAssets();
31038         }
31039         return this;
31040     },
31041
31042     /**
31043      * Aligns the dialog to the specified element
31044      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31045      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
31046      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31047      * @return {Roo.BasicDialog} this
31048      */
31049     alignTo : function(element, position, offsets){
31050         this.xy = this.el.getAlignToXY(element, position, offsets);
31051         if(this.isVisible()){
31052             this.el.setXY(this.xy);
31053             this.adjustAssets();
31054         }
31055         return this;
31056     },
31057
31058     /**
31059      * Anchors an element to another element and realigns it when the window is resized.
31060      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31061      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
31062      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31063      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
31064      * is a number, it is used as the buffer delay (defaults to 50ms).
31065      * @return {Roo.BasicDialog} this
31066      */
31067     anchorTo : function(el, alignment, offsets, monitorScroll){
31068         var action = function(){
31069             this.alignTo(el, alignment, offsets);
31070         };
31071         Roo.EventManager.onWindowResize(action, this);
31072         var tm = typeof monitorScroll;
31073         if(tm != 'undefined'){
31074             Roo.EventManager.on(window, 'scroll', action, this,
31075                 {buffer: tm == 'number' ? monitorScroll : 50});
31076         }
31077         action.call(this);
31078         return this;
31079     },
31080
31081     /**
31082      * Returns true if the dialog is visible
31083      * @return {Boolean}
31084      */
31085     isVisible : function(){
31086         return this.el.isVisible();
31087     },
31088
31089     // private
31090     animHide : function(callback){
31091         var b = Roo.get(this.animateTarget).getBox();
31092         this.proxy.show();
31093         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
31094         this.el.hide();
31095         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
31096                     this.hideEl.createDelegate(this, [callback]));
31097     },
31098
31099     /**
31100      * Hides the dialog.
31101      * @param {Function} callback (optional) Function to call when the dialog is hidden
31102      * @return {Roo.BasicDialog} this
31103      */
31104     hide : function(callback){
31105         if (this.fireEvent("beforehide", this) === false){
31106             return;
31107         }
31108         if(this.shadow){
31109             this.shadow.hide();
31110         }
31111         if(this.shim) {
31112           this.shim.hide();
31113         }
31114         // sometimes animateTarget seems to get set.. causing problems...
31115         // this just double checks..
31116         if(this.animateTarget && Roo.get(this.animateTarget)) {
31117            this.animHide(callback);
31118         }else{
31119             this.el.hide();
31120             this.hideEl(callback);
31121         }
31122         return this;
31123     },
31124
31125     // private
31126     hideEl : function(callback){
31127         this.proxy.hide();
31128         if(this.modal){
31129             this.mask.hide();
31130             Roo.get(document.body).removeClass("x-body-masked");
31131         }
31132         this.fireEvent("hide", this);
31133         if(typeof callback == "function"){
31134             callback();
31135         }
31136     },
31137
31138     // private
31139     hideAction : function(){
31140         this.setLeft("-10000px");
31141         this.setTop("-10000px");
31142         this.setStyle("visibility", "hidden");
31143     },
31144
31145     // private
31146     refreshSize : function(){
31147         this.size = this.el.getSize();
31148         this.xy = this.el.getXY();
31149         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
31150     },
31151
31152     // private
31153     // z-index is managed by the DialogManager and may be overwritten at any time
31154     setZIndex : function(index){
31155         if(this.modal){
31156             this.mask.setStyle("z-index", index);
31157         }
31158         if(this.shim){
31159             this.shim.setStyle("z-index", ++index);
31160         }
31161         if(this.shadow){
31162             this.shadow.setZIndex(++index);
31163         }
31164         this.el.setStyle("z-index", ++index);
31165         if(this.proxy){
31166             this.proxy.setStyle("z-index", ++index);
31167         }
31168         if(this.resizer){
31169             this.resizer.proxy.setStyle("z-index", ++index);
31170         }
31171
31172         this.lastZIndex = index;
31173     },
31174
31175     /**
31176      * Returns the element for this dialog
31177      * @return {Roo.Element} The underlying dialog Element
31178      */
31179     getEl : function(){
31180         return this.el;
31181     }
31182 });
31183
31184 /**
31185  * @class Roo.DialogManager
31186  * Provides global access to BasicDialogs that have been created and
31187  * support for z-indexing (layering) multiple open dialogs.
31188  */
31189 Roo.DialogManager = function(){
31190     var list = {};
31191     var accessList = [];
31192     var front = null;
31193
31194     // private
31195     var sortDialogs = function(d1, d2){
31196         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
31197     };
31198
31199     // private
31200     var orderDialogs = function(){
31201         accessList.sort(sortDialogs);
31202         var seed = Roo.DialogManager.zseed;
31203         for(var i = 0, len = accessList.length; i < len; i++){
31204             var dlg = accessList[i];
31205             if(dlg){
31206                 dlg.setZIndex(seed + (i*10));
31207             }
31208         }
31209     };
31210
31211     return {
31212         /**
31213          * The starting z-index for BasicDialogs (defaults to 9000)
31214          * @type Number The z-index value
31215          */
31216         zseed : 9000,
31217
31218         // private
31219         register : function(dlg){
31220             list[dlg.id] = dlg;
31221             accessList.push(dlg);
31222         },
31223
31224         // private
31225         unregister : function(dlg){
31226             delete list[dlg.id];
31227             var i=0;
31228             var len=0;
31229             if(!accessList.indexOf){
31230                 for(  i = 0, len = accessList.length; i < len; i++){
31231                     if(accessList[i] == dlg){
31232                         accessList.splice(i, 1);
31233                         return;
31234                     }
31235                 }
31236             }else{
31237                  i = accessList.indexOf(dlg);
31238                 if(i != -1){
31239                     accessList.splice(i, 1);
31240                 }
31241             }
31242         },
31243
31244         /**
31245          * Gets a registered dialog by id
31246          * @param {String/Object} id The id of the dialog or a dialog
31247          * @return {Roo.BasicDialog} this
31248          */
31249         get : function(id){
31250             return typeof id == "object" ? id : list[id];
31251         },
31252
31253         /**
31254          * Brings the specified dialog to the front
31255          * @param {String/Object} dlg The id of the dialog or a dialog
31256          * @return {Roo.BasicDialog} this
31257          */
31258         bringToFront : function(dlg){
31259             dlg = this.get(dlg);
31260             if(dlg != front){
31261                 front = dlg;
31262                 dlg._lastAccess = new Date().getTime();
31263                 orderDialogs();
31264             }
31265             return dlg;
31266         },
31267
31268         /**
31269          * Sends the specified dialog to the back
31270          * @param {String/Object} dlg The id of the dialog or a dialog
31271          * @return {Roo.BasicDialog} this
31272          */
31273         sendToBack : function(dlg){
31274             dlg = this.get(dlg);
31275             dlg._lastAccess = -(new Date().getTime());
31276             orderDialogs();
31277             return dlg;
31278         },
31279
31280         /**
31281          * Hides all dialogs
31282          */
31283         hideAll : function(){
31284             for(var id in list){
31285                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31286                     list[id].hide();
31287                 }
31288             }
31289         }
31290     };
31291 }();
31292
31293 /**
31294  * @class Roo.LayoutDialog
31295  * @extends Roo.BasicDialog
31296  * Dialog which provides adjustments for working with a layout in a Dialog.
31297  * Add your necessary layout config options to the dialog's config.<br>
31298  * Example usage (including a nested layout):
31299  * <pre><code>
31300 if(!dialog){
31301     dialog = new Roo.LayoutDialog("download-dlg", {
31302         modal: true,
31303         width:600,
31304         height:450,
31305         shadow:true,
31306         minWidth:500,
31307         minHeight:350,
31308         autoTabs:true,
31309         proxyDrag:true,
31310         // layout config merges with the dialog config
31311         center:{
31312             tabPosition: "top",
31313             alwaysShowTabs: true
31314         }
31315     });
31316     dialog.addKeyListener(27, dialog.hide, dialog);
31317     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31318     dialog.addButton("Build It!", this.getDownload, this);
31319
31320     // we can even add nested layouts
31321     var innerLayout = new Roo.BorderLayout("dl-inner", {
31322         east: {
31323             initialSize: 200,
31324             autoScroll:true,
31325             split:true
31326         },
31327         center: {
31328             autoScroll:true
31329         }
31330     });
31331     innerLayout.beginUpdate();
31332     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31333     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31334     innerLayout.endUpdate(true);
31335
31336     var layout = dialog.getLayout();
31337     layout.beginUpdate();
31338     layout.add("center", new Roo.ContentPanel("standard-panel",
31339                         {title: "Download the Source", fitToFrame:true}));
31340     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31341                {title: "Build your own roo.js"}));
31342     layout.getRegion("center").showPanel(sp);
31343     layout.endUpdate();
31344 }
31345 </code></pre>
31346     * @constructor
31347     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31348     * @param {Object} config configuration options
31349   */
31350 Roo.LayoutDialog = function(el, cfg){
31351     
31352     var config=  cfg;
31353     if (typeof(cfg) == 'undefined') {
31354         config = Roo.apply({}, el);
31355         // not sure why we use documentElement here.. - it should always be body.
31356         // IE7 borks horribly if we use documentElement.
31357         // webkit also does not like documentElement - it creates a body element...
31358         el = Roo.get( document.body || document.documentElement ).createChild();
31359         //config.autoCreate = true;
31360     }
31361     
31362     
31363     config.autoTabs = false;
31364     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31365     this.body.setStyle({overflow:"hidden", position:"relative"});
31366     this.layout = new Roo.BorderLayout(this.body.dom, config);
31367     this.layout.monitorWindowResize = false;
31368     this.el.addClass("x-dlg-auto-layout");
31369     // fix case when center region overwrites center function
31370     this.center = Roo.BasicDialog.prototype.center;
31371     this.on("show", this.layout.layout, this.layout, true);
31372     if (config.items) {
31373         var xitems = config.items;
31374         delete config.items;
31375         Roo.each(xitems, this.addxtype, this);
31376     }
31377     
31378     
31379 };
31380 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31381     /**
31382      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31383      * @deprecated
31384      */
31385     endUpdate : function(){
31386         this.layout.endUpdate();
31387     },
31388
31389     /**
31390      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31391      *  @deprecated
31392      */
31393     beginUpdate : function(){
31394         this.layout.beginUpdate();
31395     },
31396
31397     /**
31398      * Get the BorderLayout for this dialog
31399      * @return {Roo.BorderLayout}
31400      */
31401     getLayout : function(){
31402         return this.layout;
31403     },
31404
31405     showEl : function(){
31406         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31407         if(Roo.isIE7){
31408             this.layout.layout();
31409         }
31410     },
31411
31412     // private
31413     // Use the syncHeightBeforeShow config option to control this automatically
31414     syncBodyHeight : function(){
31415         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31416         if(this.layout){this.layout.layout();}
31417     },
31418     
31419       /**
31420      * Add an xtype element (actually adds to the layout.)
31421      * @return {Object} xdata xtype object data.
31422      */
31423     
31424     addxtype : function(c) {
31425         return this.layout.addxtype(c);
31426     }
31427 });/*
31428  * Based on:
31429  * Ext JS Library 1.1.1
31430  * Copyright(c) 2006-2007, Ext JS, LLC.
31431  *
31432  * Originally Released Under LGPL - original licence link has changed is not relivant.
31433  *
31434  * Fork - LGPL
31435  * <script type="text/javascript">
31436  */
31437  
31438 /**
31439  * @class Roo.MessageBox
31440  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31441  * Example usage:
31442  *<pre><code>
31443 // Basic alert:
31444 Roo.Msg.alert('Status', 'Changes saved successfully.');
31445
31446 // Prompt for user data:
31447 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31448     if (btn == 'ok'){
31449         // process text value...
31450     }
31451 });
31452
31453 // Show a dialog using config options:
31454 Roo.Msg.show({
31455    title:'Save Changes?',
31456    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31457    buttons: Roo.Msg.YESNOCANCEL,
31458    fn: processResult,
31459    animEl: 'elId'
31460 });
31461 </code></pre>
31462  * @singleton
31463  */
31464 Roo.MessageBox = function(){
31465     var dlg, opt, mask, waitTimer;
31466     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31467     var buttons, activeTextEl, bwidth;
31468
31469     // private
31470     var handleButton = function(button){
31471         dlg.hide();
31472         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31473     };
31474
31475     // private
31476     var handleHide = function(){
31477         if(opt && opt.cls){
31478             dlg.el.removeClass(opt.cls);
31479         }
31480         if(waitTimer){
31481             Roo.TaskMgr.stop(waitTimer);
31482             waitTimer = null;
31483         }
31484     };
31485
31486     // private
31487     var updateButtons = function(b){
31488         var width = 0;
31489         if(!b){
31490             buttons["ok"].hide();
31491             buttons["cancel"].hide();
31492             buttons["yes"].hide();
31493             buttons["no"].hide();
31494             dlg.footer.dom.style.display = 'none';
31495             return width;
31496         }
31497         dlg.footer.dom.style.display = '';
31498         for(var k in buttons){
31499             if(typeof buttons[k] != "function"){
31500                 if(b[k]){
31501                     buttons[k].show();
31502                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31503                     width += buttons[k].el.getWidth()+15;
31504                 }else{
31505                     buttons[k].hide();
31506                 }
31507             }
31508         }
31509         return width;
31510     };
31511
31512     // private
31513     var handleEsc = function(d, k, e){
31514         if(opt && opt.closable !== false){
31515             dlg.hide();
31516         }
31517         if(e){
31518             e.stopEvent();
31519         }
31520     };
31521
31522     return {
31523         /**
31524          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31525          * @return {Roo.BasicDialog} The BasicDialog element
31526          */
31527         getDialog : function(){
31528            if(!dlg){
31529                 dlg = new Roo.BasicDialog("x-msg-box", {
31530                     autoCreate : true,
31531                     shadow: true,
31532                     draggable: true,
31533                     resizable:false,
31534                     constraintoviewport:false,
31535                     fixedcenter:true,
31536                     collapsible : false,
31537                     shim:true,
31538                     modal: true,
31539                     width:400, height:100,
31540                     buttonAlign:"center",
31541                     closeClick : function(){
31542                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31543                             handleButton("no");
31544                         }else{
31545                             handleButton("cancel");
31546                         }
31547                     }
31548                 });
31549                 dlg.on("hide", handleHide);
31550                 mask = dlg.mask;
31551                 dlg.addKeyListener(27, handleEsc);
31552                 buttons = {};
31553                 var bt = this.buttonText;
31554                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31555                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31556                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31557                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31558                 bodyEl = dlg.body.createChild({
31559
31560                     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>'
31561                 });
31562                 msgEl = bodyEl.dom.firstChild;
31563                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31564                 textboxEl.enableDisplayMode();
31565                 textboxEl.addKeyListener([10,13], function(){
31566                     if(dlg.isVisible() && opt && opt.buttons){
31567                         if(opt.buttons.ok){
31568                             handleButton("ok");
31569                         }else if(opt.buttons.yes){
31570                             handleButton("yes");
31571                         }
31572                     }
31573                 });
31574                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31575                 textareaEl.enableDisplayMode();
31576                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31577                 progressEl.enableDisplayMode();
31578                 var pf = progressEl.dom.firstChild;
31579                 if (pf) {
31580                     pp = Roo.get(pf.firstChild);
31581                     pp.setHeight(pf.offsetHeight);
31582                 }
31583                 
31584             }
31585             return dlg;
31586         },
31587
31588         /**
31589          * Updates the message box body text
31590          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31591          * the XHTML-compliant non-breaking space character '&amp;#160;')
31592          * @return {Roo.MessageBox} This message box
31593          */
31594         updateText : function(text){
31595             if(!dlg.isVisible() && !opt.width){
31596                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31597             }
31598             msgEl.innerHTML = text || '&#160;';
31599       
31600             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31601             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31602             var w = Math.max(
31603                     Math.min(opt.width || cw , this.maxWidth), 
31604                     Math.max(opt.minWidth || this.minWidth, bwidth)
31605             );
31606             if(opt.prompt){
31607                 activeTextEl.setWidth(w);
31608             }
31609             if(dlg.isVisible()){
31610                 dlg.fixedcenter = false;
31611             }
31612             // to big, make it scroll. = But as usual stupid IE does not support
31613             // !important..
31614             
31615             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31616                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31617                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31618             } else {
31619                 bodyEl.dom.style.height = '';
31620                 bodyEl.dom.style.overflowY = '';
31621             }
31622             if (cw > w) {
31623                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31624             } else {
31625                 bodyEl.dom.style.overflowX = '';
31626             }
31627             
31628             dlg.setContentSize(w, bodyEl.getHeight());
31629             if(dlg.isVisible()){
31630                 dlg.fixedcenter = true;
31631             }
31632             return this;
31633         },
31634
31635         /**
31636          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31637          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31638          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31639          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31640          * @return {Roo.MessageBox} This message box
31641          */
31642         updateProgress : function(value, text){
31643             if(text){
31644                 this.updateText(text);
31645             }
31646             if (pp) { // weird bug on my firefox - for some reason this is not defined
31647                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31648             }
31649             return this;
31650         },        
31651
31652         /**
31653          * Returns true if the message box is currently displayed
31654          * @return {Boolean} True if the message box is visible, else false
31655          */
31656         isVisible : function(){
31657             return dlg && dlg.isVisible();  
31658         },
31659
31660         /**
31661          * Hides the message box if it is displayed
31662          */
31663         hide : function(){
31664             if(this.isVisible()){
31665                 dlg.hide();
31666             }  
31667         },
31668
31669         /**
31670          * Displays a new message box, or reinitializes an existing message box, based on the config options
31671          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31672          * The following config object properties are supported:
31673          * <pre>
31674 Property    Type             Description
31675 ----------  ---------------  ------------------------------------------------------------------------------------
31676 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31677                                    closes (defaults to undefined)
31678 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31679                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31680 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31681                                    progress and wait dialogs will ignore this property and always hide the
31682                                    close button as they can only be closed programmatically.
31683 cls               String           A custom CSS class to apply to the message box element
31684 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31685                                    displayed (defaults to 75)
31686 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31687                                    function will be btn (the name of the button that was clicked, if applicable,
31688                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31689                                    Progress and wait dialogs will ignore this option since they do not respond to
31690                                    user actions and can only be closed programmatically, so any required function
31691                                    should be called by the same code after it closes the dialog.
31692 icon              String           A CSS class that provides a background image to be used as an icon for
31693                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31694 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31695 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31696 modal             Boolean          False to allow user interaction with the page while the message box is
31697                                    displayed (defaults to true)
31698 msg               String           A string that will replace the existing message box body text (defaults
31699                                    to the XHTML-compliant non-breaking space character '&#160;')
31700 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31701 progress          Boolean          True to display a progress bar (defaults to false)
31702 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31703 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31704 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31705 title             String           The title text
31706 value             String           The string value to set into the active textbox element if displayed
31707 wait              Boolean          True to display a progress bar (defaults to false)
31708 width             Number           The width of the dialog in pixels
31709 </pre>
31710          *
31711          * Example usage:
31712          * <pre><code>
31713 Roo.Msg.show({
31714    title: 'Address',
31715    msg: 'Please enter your address:',
31716    width: 300,
31717    buttons: Roo.MessageBox.OKCANCEL,
31718    multiline: true,
31719    fn: saveAddress,
31720    animEl: 'addAddressBtn'
31721 });
31722 </code></pre>
31723          * @param {Object} config Configuration options
31724          * @return {Roo.MessageBox} This message box
31725          */
31726         show : function(options)
31727         {
31728             
31729             // this causes nightmares if you show one dialog after another
31730             // especially on callbacks..
31731              
31732             if(this.isVisible()){
31733                 
31734                 this.hide();
31735                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31736                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31737                 Roo.log("New Dialog Message:" +  options.msg )
31738                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31739                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31740                 
31741             }
31742             var d = this.getDialog();
31743             opt = options;
31744             d.setTitle(opt.title || "&#160;");
31745             d.close.setDisplayed(opt.closable !== false);
31746             activeTextEl = textboxEl;
31747             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31748             if(opt.prompt){
31749                 if(opt.multiline){
31750                     textboxEl.hide();
31751                     textareaEl.show();
31752                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31753                         opt.multiline : this.defaultTextHeight);
31754                     activeTextEl = textareaEl;
31755                 }else{
31756                     textboxEl.show();
31757                     textareaEl.hide();
31758                 }
31759             }else{
31760                 textboxEl.hide();
31761                 textareaEl.hide();
31762             }
31763             progressEl.setDisplayed(opt.progress === true);
31764             this.updateProgress(0);
31765             activeTextEl.dom.value = opt.value || "";
31766             if(opt.prompt){
31767                 dlg.setDefaultButton(activeTextEl);
31768             }else{
31769                 var bs = opt.buttons;
31770                 var db = null;
31771                 if(bs && bs.ok){
31772                     db = buttons["ok"];
31773                 }else if(bs && bs.yes){
31774                     db = buttons["yes"];
31775                 }
31776                 dlg.setDefaultButton(db);
31777             }
31778             bwidth = updateButtons(opt.buttons);
31779             this.updateText(opt.msg);
31780             if(opt.cls){
31781                 d.el.addClass(opt.cls);
31782             }
31783             d.proxyDrag = opt.proxyDrag === true;
31784             d.modal = opt.modal !== false;
31785             d.mask = opt.modal !== false ? mask : false;
31786             if(!d.isVisible()){
31787                 // force it to the end of the z-index stack so it gets a cursor in FF
31788                 document.body.appendChild(dlg.el.dom);
31789                 d.animateTarget = null;
31790                 d.show(options.animEl);
31791             }
31792             return this;
31793         },
31794
31795         /**
31796          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31797          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31798          * and closing the message box when the process is complete.
31799          * @param {String} title The title bar text
31800          * @param {String} msg The message box body text
31801          * @return {Roo.MessageBox} This message box
31802          */
31803         progress : function(title, msg){
31804             this.show({
31805                 title : title,
31806                 msg : msg,
31807                 buttons: false,
31808                 progress:true,
31809                 closable:false,
31810                 minWidth: this.minProgressWidth,
31811                 modal : true
31812             });
31813             return this;
31814         },
31815
31816         /**
31817          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31818          * If a callback function is passed it will be called after the user clicks the button, and the
31819          * id of the button that was clicked will be passed as the only parameter to the callback
31820          * (could also be the top-right close button).
31821          * @param {String} title The title bar text
31822          * @param {String} msg The message box body text
31823          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31824          * @param {Object} scope (optional) The scope of the callback function
31825          * @return {Roo.MessageBox} This message box
31826          */
31827         alert : function(title, msg, fn, scope){
31828             this.show({
31829                 title : title,
31830                 msg : msg,
31831                 buttons: this.OK,
31832                 fn: fn,
31833                 scope : scope,
31834                 modal : true
31835             });
31836             return this;
31837         },
31838
31839         /**
31840          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31841          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31842          * You are responsible for closing the message box when the process is complete.
31843          * @param {String} msg The message box body text
31844          * @param {String} title (optional) The title bar text
31845          * @return {Roo.MessageBox} This message box
31846          */
31847         wait : function(msg, title){
31848             this.show({
31849                 title : title,
31850                 msg : msg,
31851                 buttons: false,
31852                 closable:false,
31853                 progress:true,
31854                 modal:true,
31855                 width:300,
31856                 wait:true
31857             });
31858             waitTimer = Roo.TaskMgr.start({
31859                 run: function(i){
31860                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31861                 },
31862                 interval: 1000
31863             });
31864             return this;
31865         },
31866
31867         /**
31868          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31869          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31870          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31871          * @param {String} title The title bar text
31872          * @param {String} msg The message box body text
31873          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31874          * @param {Object} scope (optional) The scope of the callback function
31875          * @return {Roo.MessageBox} This message box
31876          */
31877         confirm : function(title, msg, fn, scope){
31878             this.show({
31879                 title : title,
31880                 msg : msg,
31881                 buttons: this.YESNO,
31882                 fn: fn,
31883                 scope : scope,
31884                 modal : true
31885             });
31886             return this;
31887         },
31888
31889         /**
31890          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31891          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31892          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31893          * (could also be the top-right close button) and the text that was entered will be passed as the two
31894          * parameters to the callback.
31895          * @param {String} title The title bar text
31896          * @param {String} msg The message box body text
31897          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31898          * @param {Object} scope (optional) The scope of the callback function
31899          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31900          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31901          * @return {Roo.MessageBox} This message box
31902          */
31903         prompt : function(title, msg, fn, scope, multiline){
31904             this.show({
31905                 title : title,
31906                 msg : msg,
31907                 buttons: this.OKCANCEL,
31908                 fn: fn,
31909                 minWidth:250,
31910                 scope : scope,
31911                 prompt:true,
31912                 multiline: multiline,
31913                 modal : true
31914             });
31915             return this;
31916         },
31917
31918         /**
31919          * Button config that displays a single OK button
31920          * @type Object
31921          */
31922         OK : {ok:true},
31923         /**
31924          * Button config that displays Yes and No buttons
31925          * @type Object
31926          */
31927         YESNO : {yes:true, no:true},
31928         /**
31929          * Button config that displays OK and Cancel buttons
31930          * @type Object
31931          */
31932         OKCANCEL : {ok:true, cancel:true},
31933         /**
31934          * Button config that displays Yes, No and Cancel buttons
31935          * @type Object
31936          */
31937         YESNOCANCEL : {yes:true, no:true, cancel:true},
31938
31939         /**
31940          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31941          * @type Number
31942          */
31943         defaultTextHeight : 75,
31944         /**
31945          * The maximum width in pixels of the message box (defaults to 600)
31946          * @type Number
31947          */
31948         maxWidth : 600,
31949         /**
31950          * The minimum width in pixels of the message box (defaults to 100)
31951          * @type Number
31952          */
31953         minWidth : 100,
31954         /**
31955          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31956          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31957          * @type Number
31958          */
31959         minProgressWidth : 250,
31960         /**
31961          * An object containing the default button text strings that can be overriden for localized language support.
31962          * Supported properties are: ok, cancel, yes and no.
31963          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31964          * @type Object
31965          */
31966         buttonText : {
31967             ok : "OK",
31968             cancel : "Cancel",
31969             yes : "Yes",
31970             no : "No"
31971         }
31972     };
31973 }();
31974
31975 /**
31976  * Shorthand for {@link Roo.MessageBox}
31977  */
31978 Roo.Msg = Roo.MessageBox;/*
31979  * Based on:
31980  * Ext JS Library 1.1.1
31981  * Copyright(c) 2006-2007, Ext JS, LLC.
31982  *
31983  * Originally Released Under LGPL - original licence link has changed is not relivant.
31984  *
31985  * Fork - LGPL
31986  * <script type="text/javascript">
31987  */
31988 /**
31989  * @class Roo.QuickTips
31990  * Provides attractive and customizable tooltips for any element.
31991  * @singleton
31992  */
31993 Roo.QuickTips = function(){
31994     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31995     var ce, bd, xy, dd;
31996     var visible = false, disabled = true, inited = false;
31997     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31998     
31999     var onOver = function(e){
32000         if(disabled){
32001             return;
32002         }
32003         var t = e.getTarget();
32004         if(!t || t.nodeType !== 1 || t == document || t == document.body){
32005             return;
32006         }
32007         if(ce && t == ce.el){
32008             clearTimeout(hideProc);
32009             return;
32010         }
32011         if(t && tagEls[t.id]){
32012             tagEls[t.id].el = t;
32013             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
32014             return;
32015         }
32016         var ttp, et = Roo.fly(t);
32017         var ns = cfg.namespace;
32018         if(tm.interceptTitles && t.title){
32019             ttp = t.title;
32020             t.qtip = ttp;
32021             t.removeAttribute("title");
32022             e.preventDefault();
32023         }else{
32024             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
32025         }
32026         if(ttp){
32027             showProc = show.defer(tm.showDelay, tm, [{
32028                 el: t, 
32029                 text: ttp, 
32030                 width: et.getAttributeNS(ns, cfg.width),
32031                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
32032                 title: et.getAttributeNS(ns, cfg.title),
32033                     cls: et.getAttributeNS(ns, cfg.cls)
32034             }]);
32035         }
32036     };
32037     
32038     var onOut = function(e){
32039         clearTimeout(showProc);
32040         var t = e.getTarget();
32041         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
32042             hideProc = setTimeout(hide, tm.hideDelay);
32043         }
32044     };
32045     
32046     var onMove = function(e){
32047         if(disabled){
32048             return;
32049         }
32050         xy = e.getXY();
32051         xy[1] += 18;
32052         if(tm.trackMouse && ce){
32053             el.setXY(xy);
32054         }
32055     };
32056     
32057     var onDown = function(e){
32058         clearTimeout(showProc);
32059         clearTimeout(hideProc);
32060         if(!e.within(el)){
32061             if(tm.hideOnClick){
32062                 hide();
32063                 tm.disable();
32064                 tm.enable.defer(100, tm);
32065             }
32066         }
32067     };
32068     
32069     var getPad = function(){
32070         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
32071     };
32072
32073     var show = function(o){
32074         if(disabled){
32075             return;
32076         }
32077         clearTimeout(dismissProc);
32078         ce = o;
32079         if(removeCls){ // in case manually hidden
32080             el.removeClass(removeCls);
32081             removeCls = null;
32082         }
32083         if(ce.cls){
32084             el.addClass(ce.cls);
32085             removeCls = ce.cls;
32086         }
32087         if(ce.title){
32088             tipTitle.update(ce.title);
32089             tipTitle.show();
32090         }else{
32091             tipTitle.update('');
32092             tipTitle.hide();
32093         }
32094         el.dom.style.width  = tm.maxWidth+'px';
32095         //tipBody.dom.style.width = '';
32096         tipBodyText.update(o.text);
32097         var p = getPad(), w = ce.width;
32098         if(!w){
32099             var td = tipBodyText.dom;
32100             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
32101             if(aw > tm.maxWidth){
32102                 w = tm.maxWidth;
32103             }else if(aw < tm.minWidth){
32104                 w = tm.minWidth;
32105             }else{
32106                 w = aw;
32107             }
32108         }
32109         //tipBody.setWidth(w);
32110         el.setWidth(parseInt(w, 10) + p);
32111         if(ce.autoHide === false){
32112             close.setDisplayed(true);
32113             if(dd){
32114                 dd.unlock();
32115             }
32116         }else{
32117             close.setDisplayed(false);
32118             if(dd){
32119                 dd.lock();
32120             }
32121         }
32122         if(xy){
32123             el.avoidY = xy[1]-18;
32124             el.setXY(xy);
32125         }
32126         if(tm.animate){
32127             el.setOpacity(.1);
32128             el.setStyle("visibility", "visible");
32129             el.fadeIn({callback: afterShow});
32130         }else{
32131             afterShow();
32132         }
32133     };
32134     
32135     var afterShow = function(){
32136         if(ce){
32137             el.show();
32138             esc.enable();
32139             if(tm.autoDismiss && ce.autoHide !== false){
32140                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
32141             }
32142         }
32143     };
32144     
32145     var hide = function(noanim){
32146         clearTimeout(dismissProc);
32147         clearTimeout(hideProc);
32148         ce = null;
32149         if(el.isVisible()){
32150             esc.disable();
32151             if(noanim !== true && tm.animate){
32152                 el.fadeOut({callback: afterHide});
32153             }else{
32154                 afterHide();
32155             } 
32156         }
32157     };
32158     
32159     var afterHide = function(){
32160         el.hide();
32161         if(removeCls){
32162             el.removeClass(removeCls);
32163             removeCls = null;
32164         }
32165     };
32166     
32167     return {
32168         /**
32169         * @cfg {Number} minWidth
32170         * The minimum width of the quick tip (defaults to 40)
32171         */
32172        minWidth : 40,
32173         /**
32174         * @cfg {Number} maxWidth
32175         * The maximum width of the quick tip (defaults to 300)
32176         */
32177        maxWidth : 300,
32178         /**
32179         * @cfg {Boolean} interceptTitles
32180         * True to automatically use the element's DOM title value if available (defaults to false)
32181         */
32182        interceptTitles : false,
32183         /**
32184         * @cfg {Boolean} trackMouse
32185         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
32186         */
32187        trackMouse : false,
32188         /**
32189         * @cfg {Boolean} hideOnClick
32190         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
32191         */
32192        hideOnClick : true,
32193         /**
32194         * @cfg {Number} showDelay
32195         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
32196         */
32197        showDelay : 500,
32198         /**
32199         * @cfg {Number} hideDelay
32200         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
32201         */
32202        hideDelay : 200,
32203         /**
32204         * @cfg {Boolean} autoHide
32205         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32206         * Used in conjunction with hideDelay.
32207         */
32208        autoHide : true,
32209         /**
32210         * @cfg {Boolean}
32211         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32212         * (defaults to true).  Used in conjunction with autoDismissDelay.
32213         */
32214        autoDismiss : true,
32215         /**
32216         * @cfg {Number}
32217         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32218         */
32219        autoDismissDelay : 5000,
32220        /**
32221         * @cfg {Boolean} animate
32222         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32223         */
32224        animate : false,
32225
32226        /**
32227         * @cfg {String} title
32228         * Title text to display (defaults to '').  This can be any valid HTML markup.
32229         */
32230         title: '',
32231        /**
32232         * @cfg {String} text
32233         * Body text to display (defaults to '').  This can be any valid HTML markup.
32234         */
32235         text : '',
32236        /**
32237         * @cfg {String} cls
32238         * A CSS class to apply to the base quick tip element (defaults to '').
32239         */
32240         cls : '',
32241        /**
32242         * @cfg {Number} width
32243         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32244         * minWidth or maxWidth.
32245         */
32246         width : null,
32247
32248     /**
32249      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32250      * or display QuickTips in a page.
32251      */
32252        init : function(){
32253           tm = Roo.QuickTips;
32254           cfg = tm.tagConfig;
32255           if(!inited){
32256               if(!Roo.isReady){ // allow calling of init() before onReady
32257                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32258                   return;
32259               }
32260               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32261               el.fxDefaults = {stopFx: true};
32262               // maximum custom styling
32263               //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>');
32264               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>');              
32265               tipTitle = el.child('h3');
32266               tipTitle.enableDisplayMode("block");
32267               tipBody = el.child('div.x-tip-bd');
32268               tipBodyText = el.child('div.x-tip-bd-inner');
32269               //bdLeft = el.child('div.x-tip-bd-left');
32270               //bdRight = el.child('div.x-tip-bd-right');
32271               close = el.child('div.x-tip-close');
32272               close.enableDisplayMode("block");
32273               close.on("click", hide);
32274               var d = Roo.get(document);
32275               d.on("mousedown", onDown);
32276               d.on("mouseover", onOver);
32277               d.on("mouseout", onOut);
32278               d.on("mousemove", onMove);
32279               esc = d.addKeyListener(27, hide);
32280               esc.disable();
32281               if(Roo.dd.DD){
32282                   dd = el.initDD("default", null, {
32283                       onDrag : function(){
32284                           el.sync();  
32285                       }
32286                   });
32287                   dd.setHandleElId(tipTitle.id);
32288                   dd.lock();
32289               }
32290               inited = true;
32291           }
32292           this.enable(); 
32293        },
32294
32295     /**
32296      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32297      * are supported:
32298      * <pre>
32299 Property    Type                   Description
32300 ----------  ---------------------  ------------------------------------------------------------------------
32301 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32302      * </ul>
32303      * @param {Object} config The config object
32304      */
32305        register : function(config){
32306            var cs = config instanceof Array ? config : arguments;
32307            for(var i = 0, len = cs.length; i < len; i++) {
32308                var c = cs[i];
32309                var target = c.target;
32310                if(target){
32311                    if(target instanceof Array){
32312                        for(var j = 0, jlen = target.length; j < jlen; j++){
32313                            tagEls[target[j]] = c;
32314                        }
32315                    }else{
32316                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32317                    }
32318                }
32319            }
32320        },
32321
32322     /**
32323      * Removes this quick tip from its element and destroys it.
32324      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32325      */
32326        unregister : function(el){
32327            delete tagEls[Roo.id(el)];
32328        },
32329
32330     /**
32331      * Enable this quick tip.
32332      */
32333        enable : function(){
32334            if(inited && disabled){
32335                locks.pop();
32336                if(locks.length < 1){
32337                    disabled = false;
32338                }
32339            }
32340        },
32341
32342     /**
32343      * Disable this quick tip.
32344      */
32345        disable : function(){
32346           disabled = true;
32347           clearTimeout(showProc);
32348           clearTimeout(hideProc);
32349           clearTimeout(dismissProc);
32350           if(ce){
32351               hide(true);
32352           }
32353           locks.push(1);
32354        },
32355
32356     /**
32357      * Returns true if the quick tip is enabled, else false.
32358      */
32359        isEnabled : function(){
32360             return !disabled;
32361        },
32362
32363         // private
32364        tagConfig : {
32365            namespace : "ext",
32366            attribute : "qtip",
32367            width : "width",
32368            target : "target",
32369            title : "qtitle",
32370            hide : "hide",
32371            cls : "qclass"
32372        }
32373    };
32374 }();
32375
32376 // backwards compat
32377 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32378  * Based on:
32379  * Ext JS Library 1.1.1
32380  * Copyright(c) 2006-2007, Ext JS, LLC.
32381  *
32382  * Originally Released Under LGPL - original licence link has changed is not relivant.
32383  *
32384  * Fork - LGPL
32385  * <script type="text/javascript">
32386  */
32387  
32388
32389 /**
32390  * @class Roo.tree.TreePanel
32391  * @extends Roo.data.Tree
32392
32393  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32394  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32395  * @cfg {Boolean} enableDD true to enable drag and drop
32396  * @cfg {Boolean} enableDrag true to enable just drag
32397  * @cfg {Boolean} enableDrop true to enable just drop
32398  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32399  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32400  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32401  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32402  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32403  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32404  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32405  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32406  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32407  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32408  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32409  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32410  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32411  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32412  * @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>
32413  * @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>
32414  * 
32415  * @constructor
32416  * @param {String/HTMLElement/Element} el The container element
32417  * @param {Object} config
32418  */
32419 Roo.tree.TreePanel = function(el, config){
32420     var root = false;
32421     var loader = false;
32422     if (config.root) {
32423         root = config.root;
32424         delete config.root;
32425     }
32426     if (config.loader) {
32427         loader = config.loader;
32428         delete config.loader;
32429     }
32430     
32431     Roo.apply(this, config);
32432     Roo.tree.TreePanel.superclass.constructor.call(this);
32433     this.el = Roo.get(el);
32434     this.el.addClass('x-tree');
32435     //console.log(root);
32436     if (root) {
32437         this.setRootNode( Roo.factory(root, Roo.tree));
32438     }
32439     if (loader) {
32440         this.loader = Roo.factory(loader, Roo.tree);
32441     }
32442    /**
32443     * Read-only. The id of the container element becomes this TreePanel's id.
32444     */
32445     this.id = this.el.id;
32446     this.addEvents({
32447         /**
32448         * @event beforeload
32449         * Fires before a node is loaded, return false to cancel
32450         * @param {Node} node The node being loaded
32451         */
32452         "beforeload" : true,
32453         /**
32454         * @event load
32455         * Fires when a node is loaded
32456         * @param {Node} node The node that was loaded
32457         */
32458         "load" : true,
32459         /**
32460         * @event textchange
32461         * Fires when the text for a node is changed
32462         * @param {Node} node The node
32463         * @param {String} text The new text
32464         * @param {String} oldText The old text
32465         */
32466         "textchange" : true,
32467         /**
32468         * @event beforeexpand
32469         * Fires before a node is expanded, return false to cancel.
32470         * @param {Node} node The node
32471         * @param {Boolean} deep
32472         * @param {Boolean} anim
32473         */
32474         "beforeexpand" : true,
32475         /**
32476         * @event beforecollapse
32477         * Fires before a node is collapsed, return false to cancel.
32478         * @param {Node} node The node
32479         * @param {Boolean} deep
32480         * @param {Boolean} anim
32481         */
32482         "beforecollapse" : true,
32483         /**
32484         * @event expand
32485         * Fires when a node is expanded
32486         * @param {Node} node The node
32487         */
32488         "expand" : true,
32489         /**
32490         * @event disabledchange
32491         * Fires when the disabled status of a node changes
32492         * @param {Node} node The node
32493         * @param {Boolean} disabled
32494         */
32495         "disabledchange" : true,
32496         /**
32497         * @event collapse
32498         * Fires when a node is collapsed
32499         * @param {Node} node The node
32500         */
32501         "collapse" : true,
32502         /**
32503         * @event beforeclick
32504         * Fires before click processing on a node. Return false to cancel the default action.
32505         * @param {Node} node The node
32506         * @param {Roo.EventObject} e The event object
32507         */
32508         "beforeclick":true,
32509         /**
32510         * @event checkchange
32511         * Fires when a node with a checkbox's checked property changes
32512         * @param {Node} this This node
32513         * @param {Boolean} checked
32514         */
32515         "checkchange":true,
32516         /**
32517         * @event click
32518         * Fires when a node is clicked
32519         * @param {Node} node The node
32520         * @param {Roo.EventObject} e The event object
32521         */
32522         "click":true,
32523         /**
32524         * @event dblclick
32525         * Fires when a node is double clicked
32526         * @param {Node} node The node
32527         * @param {Roo.EventObject} e The event object
32528         */
32529         "dblclick":true,
32530         /**
32531         * @event contextmenu
32532         * Fires when a node is right clicked
32533         * @param {Node} node The node
32534         * @param {Roo.EventObject} e The event object
32535         */
32536         "contextmenu":true,
32537         /**
32538         * @event beforechildrenrendered
32539         * Fires right before the child nodes for a node are rendered
32540         * @param {Node} node The node
32541         */
32542         "beforechildrenrendered":true,
32543         /**
32544         * @event startdrag
32545         * Fires when a node starts being dragged
32546         * @param {Roo.tree.TreePanel} this
32547         * @param {Roo.tree.TreeNode} node
32548         * @param {event} e The raw browser event
32549         */ 
32550        "startdrag" : true,
32551        /**
32552         * @event enddrag
32553         * Fires when a drag operation is complete
32554         * @param {Roo.tree.TreePanel} this
32555         * @param {Roo.tree.TreeNode} node
32556         * @param {event} e The raw browser event
32557         */
32558        "enddrag" : true,
32559        /**
32560         * @event dragdrop
32561         * Fires when a dragged node is dropped on a valid DD target
32562         * @param {Roo.tree.TreePanel} this
32563         * @param {Roo.tree.TreeNode} node
32564         * @param {DD} dd The dd it was dropped on
32565         * @param {event} e The raw browser event
32566         */
32567        "dragdrop" : true,
32568        /**
32569         * @event beforenodedrop
32570         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32571         * passed to handlers has the following properties:<br />
32572         * <ul style="padding:5px;padding-left:16px;">
32573         * <li>tree - The TreePanel</li>
32574         * <li>target - The node being targeted for the drop</li>
32575         * <li>data - The drag data from the drag source</li>
32576         * <li>point - The point of the drop - append, above or below</li>
32577         * <li>source - The drag source</li>
32578         * <li>rawEvent - Raw mouse event</li>
32579         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32580         * to be inserted by setting them on this object.</li>
32581         * <li>cancel - Set this to true to cancel the drop.</li>
32582         * </ul>
32583         * @param {Object} dropEvent
32584         */
32585        "beforenodedrop" : true,
32586        /**
32587         * @event nodedrop
32588         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32589         * passed to handlers has the following properties:<br />
32590         * <ul style="padding:5px;padding-left:16px;">
32591         * <li>tree - The TreePanel</li>
32592         * <li>target - The node being targeted for the drop</li>
32593         * <li>data - The drag data from the drag source</li>
32594         * <li>point - The point of the drop - append, above or below</li>
32595         * <li>source - The drag source</li>
32596         * <li>rawEvent - Raw mouse event</li>
32597         * <li>dropNode - Dropped node(s).</li>
32598         * </ul>
32599         * @param {Object} dropEvent
32600         */
32601        "nodedrop" : true,
32602         /**
32603         * @event nodedragover
32604         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32605         * passed to handlers has the following properties:<br />
32606         * <ul style="padding:5px;padding-left:16px;">
32607         * <li>tree - The TreePanel</li>
32608         * <li>target - The node being targeted for the drop</li>
32609         * <li>data - The drag data from the drag source</li>
32610         * <li>point - The point of the drop - append, above or below</li>
32611         * <li>source - The drag source</li>
32612         * <li>rawEvent - Raw mouse event</li>
32613         * <li>dropNode - Drop node(s) provided by the source.</li>
32614         * <li>cancel - Set this to true to signal drop not allowed.</li>
32615         * </ul>
32616         * @param {Object} dragOverEvent
32617         */
32618        "nodedragover" : true
32619         
32620     });
32621     if(this.singleExpand){
32622        this.on("beforeexpand", this.restrictExpand, this);
32623     }
32624     if (this.editor) {
32625         this.editor.tree = this;
32626         this.editor = Roo.factory(this.editor, Roo.tree);
32627     }
32628     
32629     if (this.selModel) {
32630         this.selModel = Roo.factory(this.selModel, Roo.tree);
32631     }
32632    
32633 };
32634 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32635     rootVisible : true,
32636     animate: Roo.enableFx,
32637     lines : true,
32638     enableDD : false,
32639     hlDrop : Roo.enableFx,
32640   
32641     renderer: false,
32642     
32643     rendererTip: false,
32644     // private
32645     restrictExpand : function(node){
32646         var p = node.parentNode;
32647         if(p){
32648             if(p.expandedChild && p.expandedChild.parentNode == p){
32649                 p.expandedChild.collapse();
32650             }
32651             p.expandedChild = node;
32652         }
32653     },
32654
32655     // private override
32656     setRootNode : function(node){
32657         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32658         if(!this.rootVisible){
32659             node.ui = new Roo.tree.RootTreeNodeUI(node);
32660         }
32661         return node;
32662     },
32663
32664     /**
32665      * Returns the container element for this TreePanel
32666      */
32667     getEl : function(){
32668         return this.el;
32669     },
32670
32671     /**
32672      * Returns the default TreeLoader for this TreePanel
32673      */
32674     getLoader : function(){
32675         return this.loader;
32676     },
32677
32678     /**
32679      * Expand all nodes
32680      */
32681     expandAll : function(){
32682         this.root.expand(true);
32683     },
32684
32685     /**
32686      * Collapse all nodes
32687      */
32688     collapseAll : function(){
32689         this.root.collapse(true);
32690     },
32691
32692     /**
32693      * Returns the selection model used by this TreePanel
32694      */
32695     getSelectionModel : function(){
32696         if(!this.selModel){
32697             this.selModel = new Roo.tree.DefaultSelectionModel();
32698         }
32699         return this.selModel;
32700     },
32701
32702     /**
32703      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32704      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32705      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32706      * @return {Array}
32707      */
32708     getChecked : function(a, startNode){
32709         startNode = startNode || this.root;
32710         var r = [];
32711         var f = function(){
32712             if(this.attributes.checked){
32713                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32714             }
32715         }
32716         startNode.cascade(f);
32717         return r;
32718     },
32719
32720     /**
32721      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32722      * @param {String} path
32723      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32724      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32725      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32726      */
32727     expandPath : function(path, attr, callback){
32728         attr = attr || "id";
32729         var keys = path.split(this.pathSeparator);
32730         var curNode = this.root;
32731         if(curNode.attributes[attr] != keys[1]){ // invalid root
32732             if(callback){
32733                 callback(false, null);
32734             }
32735             return;
32736         }
32737         var index = 1;
32738         var f = function(){
32739             if(++index == keys.length){
32740                 if(callback){
32741                     callback(true, curNode);
32742                 }
32743                 return;
32744             }
32745             var c = curNode.findChild(attr, keys[index]);
32746             if(!c){
32747                 if(callback){
32748                     callback(false, curNode);
32749                 }
32750                 return;
32751             }
32752             curNode = c;
32753             c.expand(false, false, f);
32754         };
32755         curNode.expand(false, false, f);
32756     },
32757
32758     /**
32759      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32760      * @param {String} path
32761      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32762      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32763      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32764      */
32765     selectPath : function(path, attr, callback){
32766         attr = attr || "id";
32767         var keys = path.split(this.pathSeparator);
32768         var v = keys.pop();
32769         if(keys.length > 0){
32770             var f = function(success, node){
32771                 if(success && node){
32772                     var n = node.findChild(attr, v);
32773                     if(n){
32774                         n.select();
32775                         if(callback){
32776                             callback(true, n);
32777                         }
32778                     }else if(callback){
32779                         callback(false, n);
32780                     }
32781                 }else{
32782                     if(callback){
32783                         callback(false, n);
32784                     }
32785                 }
32786             };
32787             this.expandPath(keys.join(this.pathSeparator), attr, f);
32788         }else{
32789             this.root.select();
32790             if(callback){
32791                 callback(true, this.root);
32792             }
32793         }
32794     },
32795
32796     getTreeEl : function(){
32797         return this.el;
32798     },
32799
32800     /**
32801      * Trigger rendering of this TreePanel
32802      */
32803     render : function(){
32804         if (this.innerCt) {
32805             return this; // stop it rendering more than once!!
32806         }
32807         
32808         this.innerCt = this.el.createChild({tag:"ul",
32809                cls:"x-tree-root-ct " +
32810                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32811
32812         if(this.containerScroll){
32813             Roo.dd.ScrollManager.register(this.el);
32814         }
32815         if((this.enableDD || this.enableDrop) && !this.dropZone){
32816            /**
32817             * The dropZone used by this tree if drop is enabled
32818             * @type Roo.tree.TreeDropZone
32819             */
32820              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32821                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32822            });
32823         }
32824         if((this.enableDD || this.enableDrag) && !this.dragZone){
32825            /**
32826             * The dragZone used by this tree if drag is enabled
32827             * @type Roo.tree.TreeDragZone
32828             */
32829             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32830                ddGroup: this.ddGroup || "TreeDD",
32831                scroll: this.ddScroll
32832            });
32833         }
32834         this.getSelectionModel().init(this);
32835         if (!this.root) {
32836             Roo.log("ROOT not set in tree");
32837             return this;
32838         }
32839         this.root.render();
32840         if(!this.rootVisible){
32841             this.root.renderChildren();
32842         }
32843         return this;
32844     }
32845 });/*
32846  * Based on:
32847  * Ext JS Library 1.1.1
32848  * Copyright(c) 2006-2007, Ext JS, LLC.
32849  *
32850  * Originally Released Under LGPL - original licence link has changed is not relivant.
32851  *
32852  * Fork - LGPL
32853  * <script type="text/javascript">
32854  */
32855  
32856
32857 /**
32858  * @class Roo.tree.DefaultSelectionModel
32859  * @extends Roo.util.Observable
32860  * The default single selection for a TreePanel.
32861  * @param {Object} cfg Configuration
32862  */
32863 Roo.tree.DefaultSelectionModel = function(cfg){
32864    this.selNode = null;
32865    
32866    
32867    
32868    this.addEvents({
32869        /**
32870         * @event selectionchange
32871         * Fires when the selected node changes
32872         * @param {DefaultSelectionModel} this
32873         * @param {TreeNode} node the new selection
32874         */
32875        "selectionchange" : true,
32876
32877        /**
32878         * @event beforeselect
32879         * Fires before the selected node changes, return false to cancel the change
32880         * @param {DefaultSelectionModel} this
32881         * @param {TreeNode} node the new selection
32882         * @param {TreeNode} node the old selection
32883         */
32884        "beforeselect" : true
32885    });
32886    
32887     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32888 };
32889
32890 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32891     init : function(tree){
32892         this.tree = tree;
32893         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32894         tree.on("click", this.onNodeClick, this);
32895     },
32896     
32897     onNodeClick : function(node, e){
32898         if (e.ctrlKey && this.selNode == node)  {
32899             this.unselect(node);
32900             return;
32901         }
32902         this.select(node);
32903     },
32904     
32905     /**
32906      * Select a node.
32907      * @param {TreeNode} node The node to select
32908      * @return {TreeNode} The selected node
32909      */
32910     select : function(node){
32911         var last = this.selNode;
32912         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32913             if(last){
32914                 last.ui.onSelectedChange(false);
32915             }
32916             this.selNode = node;
32917             node.ui.onSelectedChange(true);
32918             this.fireEvent("selectionchange", this, node, last);
32919         }
32920         return node;
32921     },
32922     
32923     /**
32924      * Deselect a node.
32925      * @param {TreeNode} node The node to unselect
32926      */
32927     unselect : function(node){
32928         if(this.selNode == node){
32929             this.clearSelections();
32930         }    
32931     },
32932     
32933     /**
32934      * Clear all selections
32935      */
32936     clearSelections : function(){
32937         var n = this.selNode;
32938         if(n){
32939             n.ui.onSelectedChange(false);
32940             this.selNode = null;
32941             this.fireEvent("selectionchange", this, null);
32942         }
32943         return n;
32944     },
32945     
32946     /**
32947      * Get the selected node
32948      * @return {TreeNode} The selected node
32949      */
32950     getSelectedNode : function(){
32951         return this.selNode;    
32952     },
32953     
32954     /**
32955      * Returns true if the node is selected
32956      * @param {TreeNode} node The node to check
32957      * @return {Boolean}
32958      */
32959     isSelected : function(node){
32960         return this.selNode == node;  
32961     },
32962
32963     /**
32964      * Selects the node above the selected node in the tree, intelligently walking the nodes
32965      * @return TreeNode The new selection
32966      */
32967     selectPrevious : function(){
32968         var s = this.selNode || this.lastSelNode;
32969         if(!s){
32970             return null;
32971         }
32972         var ps = s.previousSibling;
32973         if(ps){
32974             if(!ps.isExpanded() || ps.childNodes.length < 1){
32975                 return this.select(ps);
32976             } else{
32977                 var lc = ps.lastChild;
32978                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32979                     lc = lc.lastChild;
32980                 }
32981                 return this.select(lc);
32982             }
32983         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32984             return this.select(s.parentNode);
32985         }
32986         return null;
32987     },
32988
32989     /**
32990      * Selects the node above the selected node in the tree, intelligently walking the nodes
32991      * @return TreeNode The new selection
32992      */
32993     selectNext : function(){
32994         var s = this.selNode || this.lastSelNode;
32995         if(!s){
32996             return null;
32997         }
32998         if(s.firstChild && s.isExpanded()){
32999              return this.select(s.firstChild);
33000          }else if(s.nextSibling){
33001              return this.select(s.nextSibling);
33002          }else if(s.parentNode){
33003             var newS = null;
33004             s.parentNode.bubble(function(){
33005                 if(this.nextSibling){
33006                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
33007                     return false;
33008                 }
33009             });
33010             return newS;
33011          }
33012         return null;
33013     },
33014
33015     onKeyDown : function(e){
33016         var s = this.selNode || this.lastSelNode;
33017         // undesirable, but required
33018         var sm = this;
33019         if(!s){
33020             return;
33021         }
33022         var k = e.getKey();
33023         switch(k){
33024              case e.DOWN:
33025                  e.stopEvent();
33026                  this.selectNext();
33027              break;
33028              case e.UP:
33029                  e.stopEvent();
33030                  this.selectPrevious();
33031              break;
33032              case e.RIGHT:
33033                  e.preventDefault();
33034                  if(s.hasChildNodes()){
33035                      if(!s.isExpanded()){
33036                          s.expand();
33037                      }else if(s.firstChild){
33038                          this.select(s.firstChild, e);
33039                      }
33040                  }
33041              break;
33042              case e.LEFT:
33043                  e.preventDefault();
33044                  if(s.hasChildNodes() && s.isExpanded()){
33045                      s.collapse();
33046                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
33047                      this.select(s.parentNode, e);
33048                  }
33049              break;
33050         };
33051     }
33052 });
33053
33054 /**
33055  * @class Roo.tree.MultiSelectionModel
33056  * @extends Roo.util.Observable
33057  * Multi selection for a TreePanel.
33058  * @param {Object} cfg Configuration
33059  */
33060 Roo.tree.MultiSelectionModel = function(){
33061    this.selNodes = [];
33062    this.selMap = {};
33063    this.addEvents({
33064        /**
33065         * @event selectionchange
33066         * Fires when the selected nodes change
33067         * @param {MultiSelectionModel} this
33068         * @param {Array} nodes Array of the selected nodes
33069         */
33070        "selectionchange" : true
33071    });
33072    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
33073    
33074 };
33075
33076 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
33077     init : function(tree){
33078         this.tree = tree;
33079         tree.getTreeEl().on("keydown", this.onKeyDown, this);
33080         tree.on("click", this.onNodeClick, this);
33081     },
33082     
33083     onNodeClick : function(node, e){
33084         this.select(node, e, e.ctrlKey);
33085     },
33086     
33087     /**
33088      * Select a node.
33089      * @param {TreeNode} node The node to select
33090      * @param {EventObject} e (optional) An event associated with the selection
33091      * @param {Boolean} keepExisting True to retain existing selections
33092      * @return {TreeNode} The selected node
33093      */
33094     select : function(node, e, keepExisting){
33095         if(keepExisting !== true){
33096             this.clearSelections(true);
33097         }
33098         if(this.isSelected(node)){
33099             this.lastSelNode = node;
33100             return node;
33101         }
33102         this.selNodes.push(node);
33103         this.selMap[node.id] = node;
33104         this.lastSelNode = node;
33105         node.ui.onSelectedChange(true);
33106         this.fireEvent("selectionchange", this, this.selNodes);
33107         return node;
33108     },
33109     
33110     /**
33111      * Deselect a node.
33112      * @param {TreeNode} node The node to unselect
33113      */
33114     unselect : function(node){
33115         if(this.selMap[node.id]){
33116             node.ui.onSelectedChange(false);
33117             var sn = this.selNodes;
33118             var index = -1;
33119             if(sn.indexOf){
33120                 index = sn.indexOf(node);
33121             }else{
33122                 for(var i = 0, len = sn.length; i < len; i++){
33123                     if(sn[i] == node){
33124                         index = i;
33125                         break;
33126                     }
33127                 }
33128             }
33129             if(index != -1){
33130                 this.selNodes.splice(index, 1);
33131             }
33132             delete this.selMap[node.id];
33133             this.fireEvent("selectionchange", this, this.selNodes);
33134         }
33135     },
33136     
33137     /**
33138      * Clear all selections
33139      */
33140     clearSelections : function(suppressEvent){
33141         var sn = this.selNodes;
33142         if(sn.length > 0){
33143             for(var i = 0, len = sn.length; i < len; i++){
33144                 sn[i].ui.onSelectedChange(false);
33145             }
33146             this.selNodes = [];
33147             this.selMap = {};
33148             if(suppressEvent !== true){
33149                 this.fireEvent("selectionchange", this, this.selNodes);
33150             }
33151         }
33152     },
33153     
33154     /**
33155      * Returns true if the node is selected
33156      * @param {TreeNode} node The node to check
33157      * @return {Boolean}
33158      */
33159     isSelected : function(node){
33160         return this.selMap[node.id] ? true : false;  
33161     },
33162     
33163     /**
33164      * Returns an array of the selected nodes
33165      * @return {Array}
33166      */
33167     getSelectedNodes : function(){
33168         return this.selNodes;    
33169     },
33170
33171     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
33172
33173     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
33174
33175     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
33176 });/*
33177  * Based on:
33178  * Ext JS Library 1.1.1
33179  * Copyright(c) 2006-2007, Ext JS, LLC.
33180  *
33181  * Originally Released Under LGPL - original licence link has changed is not relivant.
33182  *
33183  * Fork - LGPL
33184  * <script type="text/javascript">
33185  */
33186  
33187 /**
33188  * @class Roo.tree.TreeNode
33189  * @extends Roo.data.Node
33190  * @cfg {String} text The text for this node
33191  * @cfg {Boolean} expanded true to start the node expanded
33192  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
33193  * @cfg {Boolean} allowDrop false if this node cannot be drop on
33194  * @cfg {Boolean} disabled true to start the node disabled
33195  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
33196  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
33197  * @cfg {String} cls A css class to be added to the node
33198  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
33199  * @cfg {String} href URL of the link used for the node (defaults to #)
33200  * @cfg {String} hrefTarget target frame for the link
33201  * @cfg {String} qtip An Ext QuickTip for the node
33202  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33203  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33204  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33205  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33206  * (defaults to undefined with no checkbox rendered)
33207  * @constructor
33208  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33209  */
33210 Roo.tree.TreeNode = function(attributes){
33211     attributes = attributes || {};
33212     if(typeof attributes == "string"){
33213         attributes = {text: attributes};
33214     }
33215     this.childrenRendered = false;
33216     this.rendered = false;
33217     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33218     this.expanded = attributes.expanded === true;
33219     this.isTarget = attributes.isTarget !== false;
33220     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33221     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33222
33223     /**
33224      * Read-only. The text for this node. To change it use setText().
33225      * @type String
33226      */
33227     this.text = attributes.text;
33228     /**
33229      * True if this node is disabled.
33230      * @type Boolean
33231      */
33232     this.disabled = attributes.disabled === true;
33233
33234     this.addEvents({
33235         /**
33236         * @event textchange
33237         * Fires when the text for this node is changed
33238         * @param {Node} this This node
33239         * @param {String} text The new text
33240         * @param {String} oldText The old text
33241         */
33242         "textchange" : true,
33243         /**
33244         * @event beforeexpand
33245         * Fires before this node is expanded, return false to cancel.
33246         * @param {Node} this This node
33247         * @param {Boolean} deep
33248         * @param {Boolean} anim
33249         */
33250         "beforeexpand" : true,
33251         /**
33252         * @event beforecollapse
33253         * Fires before this node is collapsed, return false to cancel.
33254         * @param {Node} this This node
33255         * @param {Boolean} deep
33256         * @param {Boolean} anim
33257         */
33258         "beforecollapse" : true,
33259         /**
33260         * @event expand
33261         * Fires when this node is expanded
33262         * @param {Node} this This node
33263         */
33264         "expand" : true,
33265         /**
33266         * @event disabledchange
33267         * Fires when the disabled status of this node changes
33268         * @param {Node} this This node
33269         * @param {Boolean} disabled
33270         */
33271         "disabledchange" : true,
33272         /**
33273         * @event collapse
33274         * Fires when this node is collapsed
33275         * @param {Node} this This node
33276         */
33277         "collapse" : true,
33278         /**
33279         * @event beforeclick
33280         * Fires before click processing. Return false to cancel the default action.
33281         * @param {Node} this This node
33282         * @param {Roo.EventObject} e The event object
33283         */
33284         "beforeclick":true,
33285         /**
33286         * @event checkchange
33287         * Fires when a node with a checkbox's checked property changes
33288         * @param {Node} this This node
33289         * @param {Boolean} checked
33290         */
33291         "checkchange":true,
33292         /**
33293         * @event click
33294         * Fires when this node is clicked
33295         * @param {Node} this This node
33296         * @param {Roo.EventObject} e The event object
33297         */
33298         "click":true,
33299         /**
33300         * @event dblclick
33301         * Fires when this node is double clicked
33302         * @param {Node} this This node
33303         * @param {Roo.EventObject} e The event object
33304         */
33305         "dblclick":true,
33306         /**
33307         * @event contextmenu
33308         * Fires when this node is right clicked
33309         * @param {Node} this This node
33310         * @param {Roo.EventObject} e The event object
33311         */
33312         "contextmenu":true,
33313         /**
33314         * @event beforechildrenrendered
33315         * Fires right before the child nodes for this node are rendered
33316         * @param {Node} this This node
33317         */
33318         "beforechildrenrendered":true
33319     });
33320
33321     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33322
33323     /**
33324      * Read-only. The UI for this node
33325      * @type TreeNodeUI
33326      */
33327     this.ui = new uiClass(this);
33328     
33329     // finally support items[]
33330     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33331         return;
33332     }
33333     
33334     
33335     Roo.each(this.attributes.items, function(c) {
33336         this.appendChild(Roo.factory(c,Roo.Tree));
33337     }, this);
33338     delete this.attributes.items;
33339     
33340     
33341     
33342 };
33343 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33344     preventHScroll: true,
33345     /**
33346      * Returns true if this node is expanded
33347      * @return {Boolean}
33348      */
33349     isExpanded : function(){
33350         return this.expanded;
33351     },
33352
33353     /**
33354      * Returns the UI object for this node
33355      * @return {TreeNodeUI}
33356      */
33357     getUI : function(){
33358         return this.ui;
33359     },
33360
33361     // private override
33362     setFirstChild : function(node){
33363         var of = this.firstChild;
33364         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33365         if(this.childrenRendered && of && node != of){
33366             of.renderIndent(true, true);
33367         }
33368         if(this.rendered){
33369             this.renderIndent(true, true);
33370         }
33371     },
33372
33373     // private override
33374     setLastChild : function(node){
33375         var ol = this.lastChild;
33376         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33377         if(this.childrenRendered && ol && node != ol){
33378             ol.renderIndent(true, true);
33379         }
33380         if(this.rendered){
33381             this.renderIndent(true, true);
33382         }
33383     },
33384
33385     // these methods are overridden to provide lazy rendering support
33386     // private override
33387     appendChild : function()
33388     {
33389         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33390         if(node && this.childrenRendered){
33391             node.render();
33392         }
33393         this.ui.updateExpandIcon();
33394         return node;
33395     },
33396
33397     // private override
33398     removeChild : function(node){
33399         this.ownerTree.getSelectionModel().unselect(node);
33400         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33401         // if it's been rendered remove dom node
33402         if(this.childrenRendered){
33403             node.ui.remove();
33404         }
33405         if(this.childNodes.length < 1){
33406             this.collapse(false, false);
33407         }else{
33408             this.ui.updateExpandIcon();
33409         }
33410         if(!this.firstChild) {
33411             this.childrenRendered = false;
33412         }
33413         return node;
33414     },
33415
33416     // private override
33417     insertBefore : function(node, refNode){
33418         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33419         if(newNode && refNode && this.childrenRendered){
33420             node.render();
33421         }
33422         this.ui.updateExpandIcon();
33423         return newNode;
33424     },
33425
33426     /**
33427      * Sets the text for this node
33428      * @param {String} text
33429      */
33430     setText : function(text){
33431         var oldText = this.text;
33432         this.text = text;
33433         this.attributes.text = text;
33434         if(this.rendered){ // event without subscribing
33435             this.ui.onTextChange(this, text, oldText);
33436         }
33437         this.fireEvent("textchange", this, text, oldText);
33438     },
33439
33440     /**
33441      * Triggers selection of this node
33442      */
33443     select : function(){
33444         this.getOwnerTree().getSelectionModel().select(this);
33445     },
33446
33447     /**
33448      * Triggers deselection of this node
33449      */
33450     unselect : function(){
33451         this.getOwnerTree().getSelectionModel().unselect(this);
33452     },
33453
33454     /**
33455      * Returns true if this node is selected
33456      * @return {Boolean}
33457      */
33458     isSelected : function(){
33459         return this.getOwnerTree().getSelectionModel().isSelected(this);
33460     },
33461
33462     /**
33463      * Expand this node.
33464      * @param {Boolean} deep (optional) True to expand all children as well
33465      * @param {Boolean} anim (optional) false to cancel the default animation
33466      * @param {Function} callback (optional) A callback to be called when
33467      * expanding this node completes (does not wait for deep expand to complete).
33468      * Called with 1 parameter, this node.
33469      */
33470     expand : function(deep, anim, callback){
33471         if(!this.expanded){
33472             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33473                 return;
33474             }
33475             if(!this.childrenRendered){
33476                 this.renderChildren();
33477             }
33478             this.expanded = true;
33479             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33480                 this.ui.animExpand(function(){
33481                     this.fireEvent("expand", this);
33482                     if(typeof callback == "function"){
33483                         callback(this);
33484                     }
33485                     if(deep === true){
33486                         this.expandChildNodes(true);
33487                     }
33488                 }.createDelegate(this));
33489                 return;
33490             }else{
33491                 this.ui.expand();
33492                 this.fireEvent("expand", this);
33493                 if(typeof callback == "function"){
33494                     callback(this);
33495                 }
33496             }
33497         }else{
33498            if(typeof callback == "function"){
33499                callback(this);
33500            }
33501         }
33502         if(deep === true){
33503             this.expandChildNodes(true);
33504         }
33505     },
33506
33507     isHiddenRoot : function(){
33508         return this.isRoot && !this.getOwnerTree().rootVisible;
33509     },
33510
33511     /**
33512      * Collapse this node.
33513      * @param {Boolean} deep (optional) True to collapse all children as well
33514      * @param {Boolean} anim (optional) false to cancel the default animation
33515      */
33516     collapse : function(deep, anim){
33517         if(this.expanded && !this.isHiddenRoot()){
33518             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33519                 return;
33520             }
33521             this.expanded = false;
33522             if((this.getOwnerTree().animate && anim !== false) || anim){
33523                 this.ui.animCollapse(function(){
33524                     this.fireEvent("collapse", this);
33525                     if(deep === true){
33526                         this.collapseChildNodes(true);
33527                     }
33528                 }.createDelegate(this));
33529                 return;
33530             }else{
33531                 this.ui.collapse();
33532                 this.fireEvent("collapse", this);
33533             }
33534         }
33535         if(deep === true){
33536             var cs = this.childNodes;
33537             for(var i = 0, len = cs.length; i < len; i++) {
33538                 cs[i].collapse(true, false);
33539             }
33540         }
33541     },
33542
33543     // private
33544     delayedExpand : function(delay){
33545         if(!this.expandProcId){
33546             this.expandProcId = this.expand.defer(delay, this);
33547         }
33548     },
33549
33550     // private
33551     cancelExpand : function(){
33552         if(this.expandProcId){
33553             clearTimeout(this.expandProcId);
33554         }
33555         this.expandProcId = false;
33556     },
33557
33558     /**
33559      * Toggles expanded/collapsed state of the node
33560      */
33561     toggle : function(){
33562         if(this.expanded){
33563             this.collapse();
33564         }else{
33565             this.expand();
33566         }
33567     },
33568
33569     /**
33570      * Ensures all parent nodes are expanded
33571      */
33572     ensureVisible : function(callback){
33573         var tree = this.getOwnerTree();
33574         tree.expandPath(this.parentNode.getPath(), false, function(){
33575             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33576             Roo.callback(callback);
33577         }.createDelegate(this));
33578     },
33579
33580     /**
33581      * Expand all child nodes
33582      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33583      */
33584     expandChildNodes : function(deep){
33585         var cs = this.childNodes;
33586         for(var i = 0, len = cs.length; i < len; i++) {
33587                 cs[i].expand(deep);
33588         }
33589     },
33590
33591     /**
33592      * Collapse all child nodes
33593      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33594      */
33595     collapseChildNodes : function(deep){
33596         var cs = this.childNodes;
33597         for(var i = 0, len = cs.length; i < len; i++) {
33598                 cs[i].collapse(deep);
33599         }
33600     },
33601
33602     /**
33603      * Disables this node
33604      */
33605     disable : function(){
33606         this.disabled = true;
33607         this.unselect();
33608         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33609             this.ui.onDisableChange(this, true);
33610         }
33611         this.fireEvent("disabledchange", this, true);
33612     },
33613
33614     /**
33615      * Enables this node
33616      */
33617     enable : function(){
33618         this.disabled = false;
33619         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33620             this.ui.onDisableChange(this, false);
33621         }
33622         this.fireEvent("disabledchange", this, false);
33623     },
33624
33625     // private
33626     renderChildren : function(suppressEvent){
33627         if(suppressEvent !== false){
33628             this.fireEvent("beforechildrenrendered", this);
33629         }
33630         var cs = this.childNodes;
33631         for(var i = 0, len = cs.length; i < len; i++){
33632             cs[i].render(true);
33633         }
33634         this.childrenRendered = true;
33635     },
33636
33637     // private
33638     sort : function(fn, scope){
33639         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33640         if(this.childrenRendered){
33641             var cs = this.childNodes;
33642             for(var i = 0, len = cs.length; i < len; i++){
33643                 cs[i].render(true);
33644             }
33645         }
33646     },
33647
33648     // private
33649     render : function(bulkRender){
33650         this.ui.render(bulkRender);
33651         if(!this.rendered){
33652             this.rendered = true;
33653             if(this.expanded){
33654                 this.expanded = false;
33655                 this.expand(false, false);
33656             }
33657         }
33658     },
33659
33660     // private
33661     renderIndent : function(deep, refresh){
33662         if(refresh){
33663             this.ui.childIndent = null;
33664         }
33665         this.ui.renderIndent();
33666         if(deep === true && this.childrenRendered){
33667             var cs = this.childNodes;
33668             for(var i = 0, len = cs.length; i < len; i++){
33669                 cs[i].renderIndent(true, refresh);
33670             }
33671         }
33672     }
33673 });/*
33674  * Based on:
33675  * Ext JS Library 1.1.1
33676  * Copyright(c) 2006-2007, Ext JS, LLC.
33677  *
33678  * Originally Released Under LGPL - original licence link has changed is not relivant.
33679  *
33680  * Fork - LGPL
33681  * <script type="text/javascript">
33682  */
33683  
33684 /**
33685  * @class Roo.tree.AsyncTreeNode
33686  * @extends Roo.tree.TreeNode
33687  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33688  * @constructor
33689  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33690  */
33691  Roo.tree.AsyncTreeNode = function(config){
33692     this.loaded = false;
33693     this.loading = false;
33694     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33695     /**
33696     * @event beforeload
33697     * Fires before this node is loaded, return false to cancel
33698     * @param {Node} this This node
33699     */
33700     this.addEvents({'beforeload':true, 'load': true});
33701     /**
33702     * @event load
33703     * Fires when this node is loaded
33704     * @param {Node} this This node
33705     */
33706     /**
33707      * The loader used by this node (defaults to using the tree's defined loader)
33708      * @type TreeLoader
33709      * @property loader
33710      */
33711 };
33712 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33713     expand : function(deep, anim, callback){
33714         if(this.loading){ // if an async load is already running, waiting til it's done
33715             var timer;
33716             var f = function(){
33717                 if(!this.loading){ // done loading
33718                     clearInterval(timer);
33719                     this.expand(deep, anim, callback);
33720                 }
33721             }.createDelegate(this);
33722             timer = setInterval(f, 200);
33723             return;
33724         }
33725         if(!this.loaded){
33726             if(this.fireEvent("beforeload", this) === false){
33727                 return;
33728             }
33729             this.loading = true;
33730             this.ui.beforeLoad(this);
33731             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33732             if(loader){
33733                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33734                 return;
33735             }
33736         }
33737         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33738     },
33739     
33740     /**
33741      * Returns true if this node is currently loading
33742      * @return {Boolean}
33743      */
33744     isLoading : function(){
33745         return this.loading;  
33746     },
33747     
33748     loadComplete : function(deep, anim, callback){
33749         this.loading = false;
33750         this.loaded = true;
33751         this.ui.afterLoad(this);
33752         this.fireEvent("load", this);
33753         this.expand(deep, anim, callback);
33754     },
33755     
33756     /**
33757      * Returns true if this node has been loaded
33758      * @return {Boolean}
33759      */
33760     isLoaded : function(){
33761         return this.loaded;
33762     },
33763     
33764     hasChildNodes : function(){
33765         if(!this.isLeaf() && !this.loaded){
33766             return true;
33767         }else{
33768             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33769         }
33770     },
33771
33772     /**
33773      * Trigger a reload for this node
33774      * @param {Function} callback
33775      */
33776     reload : function(callback){
33777         this.collapse(false, false);
33778         while(this.firstChild){
33779             this.removeChild(this.firstChild);
33780         }
33781         this.childrenRendered = false;
33782         this.loaded = false;
33783         if(this.isHiddenRoot()){
33784             this.expanded = false;
33785         }
33786         this.expand(false, false, callback);
33787     }
33788 });/*
33789  * Based on:
33790  * Ext JS Library 1.1.1
33791  * Copyright(c) 2006-2007, Ext JS, LLC.
33792  *
33793  * Originally Released Under LGPL - original licence link has changed is not relivant.
33794  *
33795  * Fork - LGPL
33796  * <script type="text/javascript">
33797  */
33798  
33799 /**
33800  * @class Roo.tree.TreeNodeUI
33801  * @constructor
33802  * @param {Object} node The node to render
33803  * The TreeNode UI implementation is separate from the
33804  * tree implementation. Unless you are customizing the tree UI,
33805  * you should never have to use this directly.
33806  */
33807 Roo.tree.TreeNodeUI = function(node){
33808     this.node = node;
33809     this.rendered = false;
33810     this.animating = false;
33811     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33812 };
33813
33814 Roo.tree.TreeNodeUI.prototype = {
33815     removeChild : function(node){
33816         if(this.rendered){
33817             this.ctNode.removeChild(node.ui.getEl());
33818         }
33819     },
33820
33821     beforeLoad : function(){
33822          this.addClass("x-tree-node-loading");
33823     },
33824
33825     afterLoad : function(){
33826          this.removeClass("x-tree-node-loading");
33827     },
33828
33829     onTextChange : function(node, text, oldText){
33830         if(this.rendered){
33831             this.textNode.innerHTML = text;
33832         }
33833     },
33834
33835     onDisableChange : function(node, state){
33836         this.disabled = state;
33837         if(state){
33838             this.addClass("x-tree-node-disabled");
33839         }else{
33840             this.removeClass("x-tree-node-disabled");
33841         }
33842     },
33843
33844     onSelectedChange : function(state){
33845         if(state){
33846             this.focus();
33847             this.addClass("x-tree-selected");
33848         }else{
33849             //this.blur();
33850             this.removeClass("x-tree-selected");
33851         }
33852     },
33853
33854     onMove : function(tree, node, oldParent, newParent, index, refNode){
33855         this.childIndent = null;
33856         if(this.rendered){
33857             var targetNode = newParent.ui.getContainer();
33858             if(!targetNode){//target not rendered
33859                 this.holder = document.createElement("div");
33860                 this.holder.appendChild(this.wrap);
33861                 return;
33862             }
33863             var insertBefore = refNode ? refNode.ui.getEl() : null;
33864             if(insertBefore){
33865                 targetNode.insertBefore(this.wrap, insertBefore);
33866             }else{
33867                 targetNode.appendChild(this.wrap);
33868             }
33869             this.node.renderIndent(true);
33870         }
33871     },
33872
33873     addClass : function(cls){
33874         if(this.elNode){
33875             Roo.fly(this.elNode).addClass(cls);
33876         }
33877     },
33878
33879     removeClass : function(cls){
33880         if(this.elNode){
33881             Roo.fly(this.elNode).removeClass(cls);
33882         }
33883     },
33884
33885     remove : function(){
33886         if(this.rendered){
33887             this.holder = document.createElement("div");
33888             this.holder.appendChild(this.wrap);
33889         }
33890     },
33891
33892     fireEvent : function(){
33893         return this.node.fireEvent.apply(this.node, arguments);
33894     },
33895
33896     initEvents : function(){
33897         this.node.on("move", this.onMove, this);
33898         var E = Roo.EventManager;
33899         var a = this.anchor;
33900
33901         var el = Roo.fly(a, '_treeui');
33902
33903         if(Roo.isOpera){ // opera render bug ignores the CSS
33904             el.setStyle("text-decoration", "none");
33905         }
33906
33907         el.on("click", this.onClick, this);
33908         el.on("dblclick", this.onDblClick, this);
33909
33910         if(this.checkbox){
33911             Roo.EventManager.on(this.checkbox,
33912                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33913         }
33914
33915         el.on("contextmenu", this.onContextMenu, this);
33916
33917         var icon = Roo.fly(this.iconNode);
33918         icon.on("click", this.onClick, this);
33919         icon.on("dblclick", this.onDblClick, this);
33920         icon.on("contextmenu", this.onContextMenu, this);
33921         E.on(this.ecNode, "click", this.ecClick, this, true);
33922
33923         if(this.node.disabled){
33924             this.addClass("x-tree-node-disabled");
33925         }
33926         if(this.node.hidden){
33927             this.addClass("x-tree-node-disabled");
33928         }
33929         var ot = this.node.getOwnerTree();
33930         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33931         if(dd && (!this.node.isRoot || ot.rootVisible)){
33932             Roo.dd.Registry.register(this.elNode, {
33933                 node: this.node,
33934                 handles: this.getDDHandles(),
33935                 isHandle: false
33936             });
33937         }
33938     },
33939
33940     getDDHandles : function(){
33941         return [this.iconNode, this.textNode];
33942     },
33943
33944     hide : function(){
33945         if(this.rendered){
33946             this.wrap.style.display = "none";
33947         }
33948     },
33949
33950     show : function(){
33951         if(this.rendered){
33952             this.wrap.style.display = "";
33953         }
33954     },
33955
33956     onContextMenu : function(e){
33957         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33958             e.preventDefault();
33959             this.focus();
33960             this.fireEvent("contextmenu", this.node, e);
33961         }
33962     },
33963
33964     onClick : function(e){
33965         if(this.dropping){
33966             e.stopEvent();
33967             return;
33968         }
33969         if(this.fireEvent("beforeclick", this.node, e) !== false){
33970             if(!this.disabled && this.node.attributes.href){
33971                 this.fireEvent("click", this.node, e);
33972                 return;
33973             }
33974             e.preventDefault();
33975             if(this.disabled){
33976                 return;
33977             }
33978
33979             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33980                 this.node.toggle();
33981             }
33982
33983             this.fireEvent("click", this.node, e);
33984         }else{
33985             e.stopEvent();
33986         }
33987     },
33988
33989     onDblClick : function(e){
33990         e.preventDefault();
33991         if(this.disabled){
33992             return;
33993         }
33994         if(this.checkbox){
33995             this.toggleCheck();
33996         }
33997         if(!this.animating && this.node.hasChildNodes()){
33998             this.node.toggle();
33999         }
34000         this.fireEvent("dblclick", this.node, e);
34001     },
34002
34003     onCheckChange : function(){
34004         var checked = this.checkbox.checked;
34005         this.node.attributes.checked = checked;
34006         this.fireEvent('checkchange', this.node, checked);
34007     },
34008
34009     ecClick : function(e){
34010         if(!this.animating && this.node.hasChildNodes()){
34011             this.node.toggle();
34012         }
34013     },
34014
34015     startDrop : function(){
34016         this.dropping = true;
34017     },
34018
34019     // delayed drop so the click event doesn't get fired on a drop
34020     endDrop : function(){
34021        setTimeout(function(){
34022            this.dropping = false;
34023        }.createDelegate(this), 50);
34024     },
34025
34026     expand : function(){
34027         this.updateExpandIcon();
34028         this.ctNode.style.display = "";
34029     },
34030
34031     focus : function(){
34032         if(!this.node.preventHScroll){
34033             try{this.anchor.focus();
34034             }catch(e){}
34035         }else if(!Roo.isIE){
34036             try{
34037                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
34038                 var l = noscroll.scrollLeft;
34039                 this.anchor.focus();
34040                 noscroll.scrollLeft = l;
34041             }catch(e){}
34042         }
34043     },
34044
34045     toggleCheck : function(value){
34046         var cb = this.checkbox;
34047         if(cb){
34048             cb.checked = (value === undefined ? !cb.checked : value);
34049         }
34050     },
34051
34052     blur : function(){
34053         try{
34054             this.anchor.blur();
34055         }catch(e){}
34056     },
34057
34058     animExpand : function(callback){
34059         var ct = Roo.get(this.ctNode);
34060         ct.stopFx();
34061         if(!this.node.hasChildNodes()){
34062             this.updateExpandIcon();
34063             this.ctNode.style.display = "";
34064             Roo.callback(callback);
34065             return;
34066         }
34067         this.animating = true;
34068         this.updateExpandIcon();
34069
34070         ct.slideIn('t', {
34071            callback : function(){
34072                this.animating = false;
34073                Roo.callback(callback);
34074             },
34075             scope: this,
34076             duration: this.node.ownerTree.duration || .25
34077         });
34078     },
34079
34080     highlight : function(){
34081         var tree = this.node.getOwnerTree();
34082         Roo.fly(this.wrap).highlight(
34083             tree.hlColor || "C3DAF9",
34084             {endColor: tree.hlBaseColor}
34085         );
34086     },
34087
34088     collapse : function(){
34089         this.updateExpandIcon();
34090         this.ctNode.style.display = "none";
34091     },
34092
34093     animCollapse : function(callback){
34094         var ct = Roo.get(this.ctNode);
34095         ct.enableDisplayMode('block');
34096         ct.stopFx();
34097
34098         this.animating = true;
34099         this.updateExpandIcon();
34100
34101         ct.slideOut('t', {
34102             callback : function(){
34103                this.animating = false;
34104                Roo.callback(callback);
34105             },
34106             scope: this,
34107             duration: this.node.ownerTree.duration || .25
34108         });
34109     },
34110
34111     getContainer : function(){
34112         return this.ctNode;
34113     },
34114
34115     getEl : function(){
34116         return this.wrap;
34117     },
34118
34119     appendDDGhost : function(ghostNode){
34120         ghostNode.appendChild(this.elNode.cloneNode(true));
34121     },
34122
34123     getDDRepairXY : function(){
34124         return Roo.lib.Dom.getXY(this.iconNode);
34125     },
34126
34127     onRender : function(){
34128         this.render();
34129     },
34130
34131     render : function(bulkRender){
34132         var n = this.node, a = n.attributes;
34133         var targetNode = n.parentNode ?
34134               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
34135
34136         if(!this.rendered){
34137             this.rendered = true;
34138
34139             this.renderElements(n, a, targetNode, bulkRender);
34140
34141             if(a.qtip){
34142                if(this.textNode.setAttributeNS){
34143                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
34144                    if(a.qtipTitle){
34145                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
34146                    }
34147                }else{
34148                    this.textNode.setAttribute("ext:qtip", a.qtip);
34149                    if(a.qtipTitle){
34150                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
34151                    }
34152                }
34153             }else if(a.qtipCfg){
34154                 a.qtipCfg.target = Roo.id(this.textNode);
34155                 Roo.QuickTips.register(a.qtipCfg);
34156             }
34157             this.initEvents();
34158             if(!this.node.expanded){
34159                 this.updateExpandIcon();
34160             }
34161         }else{
34162             if(bulkRender === true) {
34163                 targetNode.appendChild(this.wrap);
34164             }
34165         }
34166     },
34167
34168     renderElements : function(n, a, targetNode, bulkRender)
34169     {
34170         // add some indent caching, this helps performance when rendering a large tree
34171         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34172         var t = n.getOwnerTree();
34173         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
34174         if (typeof(n.attributes.html) != 'undefined') {
34175             txt = n.attributes.html;
34176         }
34177         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
34178         var cb = typeof a.checked == 'boolean';
34179         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34180         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
34181             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
34182             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
34183             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
34184             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
34185             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
34186              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
34187                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
34188             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34189             "</li>"];
34190
34191         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34192             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34193                                 n.nextSibling.ui.getEl(), buf.join(""));
34194         }else{
34195             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34196         }
34197
34198         this.elNode = this.wrap.childNodes[0];
34199         this.ctNode = this.wrap.childNodes[1];
34200         var cs = this.elNode.childNodes;
34201         this.indentNode = cs[0];
34202         this.ecNode = cs[1];
34203         this.iconNode = cs[2];
34204         var index = 3;
34205         if(cb){
34206             this.checkbox = cs[3];
34207             index++;
34208         }
34209         this.anchor = cs[index];
34210         this.textNode = cs[index].firstChild;
34211     },
34212
34213     getAnchor : function(){
34214         return this.anchor;
34215     },
34216
34217     getTextEl : function(){
34218         return this.textNode;
34219     },
34220
34221     getIconEl : function(){
34222         return this.iconNode;
34223     },
34224
34225     isChecked : function(){
34226         return this.checkbox ? this.checkbox.checked : false;
34227     },
34228
34229     updateExpandIcon : function(){
34230         if(this.rendered){
34231             var n = this.node, c1, c2;
34232             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34233             var hasChild = n.hasChildNodes();
34234             if(hasChild){
34235                 if(n.expanded){
34236                     cls += "-minus";
34237                     c1 = "x-tree-node-collapsed";
34238                     c2 = "x-tree-node-expanded";
34239                 }else{
34240                     cls += "-plus";
34241                     c1 = "x-tree-node-expanded";
34242                     c2 = "x-tree-node-collapsed";
34243                 }
34244                 if(this.wasLeaf){
34245                     this.removeClass("x-tree-node-leaf");
34246                     this.wasLeaf = false;
34247                 }
34248                 if(this.c1 != c1 || this.c2 != c2){
34249                     Roo.fly(this.elNode).replaceClass(c1, c2);
34250                     this.c1 = c1; this.c2 = c2;
34251                 }
34252             }else{
34253                 // this changes non-leafs into leafs if they have no children.
34254                 // it's not very rational behaviour..
34255                 
34256                 if(!this.wasLeaf && this.node.leaf){
34257                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34258                     delete this.c1;
34259                     delete this.c2;
34260                     this.wasLeaf = true;
34261                 }
34262             }
34263             var ecc = "x-tree-ec-icon "+cls;
34264             if(this.ecc != ecc){
34265                 this.ecNode.className = ecc;
34266                 this.ecc = ecc;
34267             }
34268         }
34269     },
34270
34271     getChildIndent : function(){
34272         if(!this.childIndent){
34273             var buf = [];
34274             var p = this.node;
34275             while(p){
34276                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34277                     if(!p.isLast()) {
34278                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34279                     } else {
34280                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34281                     }
34282                 }
34283                 p = p.parentNode;
34284             }
34285             this.childIndent = buf.join("");
34286         }
34287         return this.childIndent;
34288     },
34289
34290     renderIndent : function(){
34291         if(this.rendered){
34292             var indent = "";
34293             var p = this.node.parentNode;
34294             if(p){
34295                 indent = p.ui.getChildIndent();
34296             }
34297             if(this.indentMarkup != indent){ // don't rerender if not required
34298                 this.indentNode.innerHTML = indent;
34299                 this.indentMarkup = indent;
34300             }
34301             this.updateExpandIcon();
34302         }
34303     }
34304 };
34305
34306 Roo.tree.RootTreeNodeUI = function(){
34307     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34308 };
34309 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34310     render : function(){
34311         if(!this.rendered){
34312             var targetNode = this.node.ownerTree.innerCt.dom;
34313             this.node.expanded = true;
34314             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34315             this.wrap = this.ctNode = targetNode.firstChild;
34316         }
34317     },
34318     collapse : function(){
34319     },
34320     expand : function(){
34321     }
34322 });/*
34323  * Based on:
34324  * Ext JS Library 1.1.1
34325  * Copyright(c) 2006-2007, Ext JS, LLC.
34326  *
34327  * Originally Released Under LGPL - original licence link has changed is not relivant.
34328  *
34329  * Fork - LGPL
34330  * <script type="text/javascript">
34331  */
34332 /**
34333  * @class Roo.tree.TreeLoader
34334  * @extends Roo.util.Observable
34335  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34336  * nodes from a specified URL. The response must be a javascript Array definition
34337  * who's elements are node definition objects. eg:
34338  * <pre><code>
34339 {  success : true,
34340    data :      [
34341    
34342     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34343     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34344     ]
34345 }
34346
34347
34348 </code></pre>
34349  * <br><br>
34350  * The old style respose with just an array is still supported, but not recommended.
34351  * <br><br>
34352  *
34353  * A server request is sent, and child nodes are loaded only when a node is expanded.
34354  * The loading node's id is passed to the server under the parameter name "node" to
34355  * enable the server to produce the correct child nodes.
34356  * <br><br>
34357  * To pass extra parameters, an event handler may be attached to the "beforeload"
34358  * event, and the parameters specified in the TreeLoader's baseParams property:
34359  * <pre><code>
34360     myTreeLoader.on("beforeload", function(treeLoader, node) {
34361         this.baseParams.category = node.attributes.category;
34362     }, this);
34363 </code></pre><
34364  * This would pass an HTTP parameter called "category" to the server containing
34365  * the value of the Node's "category" attribute.
34366  * @constructor
34367  * Creates a new Treeloader.
34368  * @param {Object} config A config object containing config properties.
34369  */
34370 Roo.tree.TreeLoader = function(config){
34371     this.baseParams = {};
34372     this.requestMethod = "POST";
34373     Roo.apply(this, config);
34374
34375     this.addEvents({
34376     
34377         /**
34378          * @event beforeload
34379          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34380          * @param {Object} This TreeLoader object.
34381          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34382          * @param {Object} callback The callback function specified in the {@link #load} call.
34383          */
34384         beforeload : true,
34385         /**
34386          * @event load
34387          * Fires when the node has been successfuly loaded.
34388          * @param {Object} This TreeLoader object.
34389          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34390          * @param {Object} response The response object containing the data from the server.
34391          */
34392         load : true,
34393         /**
34394          * @event loadexception
34395          * Fires if the network request failed.
34396          * @param {Object} This TreeLoader object.
34397          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34398          * @param {Object} response The response object containing the data from the server.
34399          */
34400         loadexception : true,
34401         /**
34402          * @event create
34403          * Fires before a node is created, enabling you to return custom Node types 
34404          * @param {Object} This TreeLoader object.
34405          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34406          */
34407         create : true
34408     });
34409
34410     Roo.tree.TreeLoader.superclass.constructor.call(this);
34411 };
34412
34413 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34414     /**
34415     * @cfg {String} dataUrl The URL from which to request a Json string which
34416     * specifies an array of node definition object representing the child nodes
34417     * to be loaded.
34418     */
34419     /**
34420     * @cfg {String} requestMethod either GET or POST
34421     * defaults to POST (due to BC)
34422     * to be loaded.
34423     */
34424     /**
34425     * @cfg {Object} baseParams (optional) An object containing properties which
34426     * specify HTTP parameters to be passed to each request for child nodes.
34427     */
34428     /**
34429     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34430     * created by this loader. If the attributes sent by the server have an attribute in this object,
34431     * they take priority.
34432     */
34433     /**
34434     * @cfg {Object} uiProviders (optional) An object containing properties which
34435     * 
34436     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34437     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34438     * <i>uiProvider</i> attribute of a returned child node is a string rather
34439     * than a reference to a TreeNodeUI implementation, this that string value
34440     * is used as a property name in the uiProviders object. You can define the provider named
34441     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34442     */
34443     uiProviders : {},
34444
34445     /**
34446     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34447     * child nodes before loading.
34448     */
34449     clearOnLoad : true,
34450
34451     /**
34452     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34453     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34454     * Grid query { data : [ .....] }
34455     */
34456     
34457     root : false,
34458      /**
34459     * @cfg {String} queryParam (optional) 
34460     * Name of the query as it will be passed on the querystring (defaults to 'node')
34461     * eg. the request will be ?node=[id]
34462     */
34463     
34464     
34465     queryParam: false,
34466     
34467     /**
34468      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34469      * This is called automatically when a node is expanded, but may be used to reload
34470      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34471      * @param {Roo.tree.TreeNode} node
34472      * @param {Function} callback
34473      */
34474     load : function(node, callback){
34475         if(this.clearOnLoad){
34476             while(node.firstChild){
34477                 node.removeChild(node.firstChild);
34478             }
34479         }
34480         if(node.attributes.children){ // preloaded json children
34481             var cs = node.attributes.children;
34482             for(var i = 0, len = cs.length; i < len; i++){
34483                 node.appendChild(this.createNode(cs[i]));
34484             }
34485             if(typeof callback == "function"){
34486                 callback();
34487             }
34488         }else if(this.dataUrl){
34489             this.requestData(node, callback);
34490         }
34491     },
34492
34493     getParams: function(node){
34494         var buf = [], bp = this.baseParams;
34495         for(var key in bp){
34496             if(typeof bp[key] != "function"){
34497                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34498             }
34499         }
34500         var n = this.queryParam === false ? 'node' : this.queryParam;
34501         buf.push(n + "=", encodeURIComponent(node.id));
34502         return buf.join("");
34503     },
34504
34505     requestData : function(node, callback){
34506         if(this.fireEvent("beforeload", this, node, callback) !== false){
34507             this.transId = Roo.Ajax.request({
34508                 method:this.requestMethod,
34509                 url: this.dataUrl||this.url,
34510                 success: this.handleResponse,
34511                 failure: this.handleFailure,
34512                 scope: this,
34513                 argument: {callback: callback, node: node},
34514                 params: this.getParams(node)
34515             });
34516         }else{
34517             // if the load is cancelled, make sure we notify
34518             // the node that we are done
34519             if(typeof callback == "function"){
34520                 callback();
34521             }
34522         }
34523     },
34524
34525     isLoading : function(){
34526         return this.transId ? true : false;
34527     },
34528
34529     abort : function(){
34530         if(this.isLoading()){
34531             Roo.Ajax.abort(this.transId);
34532         }
34533     },
34534
34535     // private
34536     createNode : function(attr)
34537     {
34538         // apply baseAttrs, nice idea Corey!
34539         if(this.baseAttrs){
34540             Roo.applyIf(attr, this.baseAttrs);
34541         }
34542         if(this.applyLoader !== false){
34543             attr.loader = this;
34544         }
34545         // uiProvider = depreciated..
34546         
34547         if(typeof(attr.uiProvider) == 'string'){
34548            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34549                 /**  eval:var:attr */ eval(attr.uiProvider);
34550         }
34551         if(typeof(this.uiProviders['default']) != 'undefined') {
34552             attr.uiProvider = this.uiProviders['default'];
34553         }
34554         
34555         this.fireEvent('create', this, attr);
34556         
34557         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34558         return(attr.leaf ?
34559                         new Roo.tree.TreeNode(attr) :
34560                         new Roo.tree.AsyncTreeNode(attr));
34561     },
34562
34563     processResponse : function(response, node, callback)
34564     {
34565         var json = response.responseText;
34566         try {
34567             
34568             var o = Roo.decode(json);
34569             
34570             if (this.root === false && typeof(o.success) != undefined) {
34571                 this.root = 'data'; // the default behaviour for list like data..
34572                 }
34573                 
34574             if (this.root !== false &&  !o.success) {
34575                 // it's a failure condition.
34576                 var a = response.argument;
34577                 this.fireEvent("loadexception", this, a.node, response);
34578                 Roo.log("Load failed - should have a handler really");
34579                 return;
34580             }
34581             
34582             
34583             
34584             if (this.root !== false) {
34585                  o = o[this.root];
34586             }
34587             
34588             for(var i = 0, len = o.length; i < len; i++){
34589                 var n = this.createNode(o[i]);
34590                 if(n){
34591                     node.appendChild(n);
34592                 }
34593             }
34594             if(typeof callback == "function"){
34595                 callback(this, node);
34596             }
34597         }catch(e){
34598             this.handleFailure(response);
34599         }
34600     },
34601
34602     handleResponse : function(response){
34603         this.transId = false;
34604         var a = response.argument;
34605         this.processResponse(response, a.node, a.callback);
34606         this.fireEvent("load", this, a.node, response);
34607     },
34608
34609     handleFailure : function(response)
34610     {
34611         // should handle failure better..
34612         this.transId = false;
34613         var a = response.argument;
34614         this.fireEvent("loadexception", this, a.node, response);
34615         if(typeof a.callback == "function"){
34616             a.callback(this, a.node);
34617         }
34618     }
34619 });/*
34620  * Based on:
34621  * Ext JS Library 1.1.1
34622  * Copyright(c) 2006-2007, Ext JS, LLC.
34623  *
34624  * Originally Released Under LGPL - original licence link has changed is not relivant.
34625  *
34626  * Fork - LGPL
34627  * <script type="text/javascript">
34628  */
34629
34630 /**
34631 * @class Roo.tree.TreeFilter
34632 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34633 * @param {TreePanel} tree
34634 * @param {Object} config (optional)
34635  */
34636 Roo.tree.TreeFilter = function(tree, config){
34637     this.tree = tree;
34638     this.filtered = {};
34639     Roo.apply(this, config);
34640 };
34641
34642 Roo.tree.TreeFilter.prototype = {
34643     clearBlank:false,
34644     reverse:false,
34645     autoClear:false,
34646     remove:false,
34647
34648      /**
34649      * Filter the data by a specific attribute.
34650      * @param {String/RegExp} value Either string that the attribute value
34651      * should start with or a RegExp to test against the attribute
34652      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34653      * @param {TreeNode} startNode (optional) The node to start the filter at.
34654      */
34655     filter : function(value, attr, startNode){
34656         attr = attr || "text";
34657         var f;
34658         if(typeof value == "string"){
34659             var vlen = value.length;
34660             // auto clear empty filter
34661             if(vlen == 0 && this.clearBlank){
34662                 this.clear();
34663                 return;
34664             }
34665             value = value.toLowerCase();
34666             f = function(n){
34667                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34668             };
34669         }else if(value.exec){ // regex?
34670             f = function(n){
34671                 return value.test(n.attributes[attr]);
34672             };
34673         }else{
34674             throw 'Illegal filter type, must be string or regex';
34675         }
34676         this.filterBy(f, null, startNode);
34677         },
34678
34679     /**
34680      * Filter by a function. The passed function will be called with each
34681      * node in the tree (or from the startNode). If the function returns true, the node is kept
34682      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34683      * @param {Function} fn The filter function
34684      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34685      */
34686     filterBy : function(fn, scope, startNode){
34687         startNode = startNode || this.tree.root;
34688         if(this.autoClear){
34689             this.clear();
34690         }
34691         var af = this.filtered, rv = this.reverse;
34692         var f = function(n){
34693             if(n == startNode){
34694                 return true;
34695             }
34696             if(af[n.id]){
34697                 return false;
34698             }
34699             var m = fn.call(scope || n, n);
34700             if(!m || rv){
34701                 af[n.id] = n;
34702                 n.ui.hide();
34703                 return false;
34704             }
34705             return true;
34706         };
34707         startNode.cascade(f);
34708         if(this.remove){
34709            for(var id in af){
34710                if(typeof id != "function"){
34711                    var n = af[id];
34712                    if(n && n.parentNode){
34713                        n.parentNode.removeChild(n);
34714                    }
34715                }
34716            }
34717         }
34718     },
34719
34720     /**
34721      * Clears the current filter. Note: with the "remove" option
34722      * set a filter cannot be cleared.
34723      */
34724     clear : function(){
34725         var t = this.tree;
34726         var af = this.filtered;
34727         for(var id in af){
34728             if(typeof id != "function"){
34729                 var n = af[id];
34730                 if(n){
34731                     n.ui.show();
34732                 }
34733             }
34734         }
34735         this.filtered = {};
34736     }
34737 };
34738 /*
34739  * Based on:
34740  * Ext JS Library 1.1.1
34741  * Copyright(c) 2006-2007, Ext JS, LLC.
34742  *
34743  * Originally Released Under LGPL - original licence link has changed is not relivant.
34744  *
34745  * Fork - LGPL
34746  * <script type="text/javascript">
34747  */
34748  
34749
34750 /**
34751  * @class Roo.tree.TreeSorter
34752  * Provides sorting of nodes in a TreePanel
34753  * 
34754  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34755  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34756  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34757  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34758  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34759  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34760  * @constructor
34761  * @param {TreePanel} tree
34762  * @param {Object} config
34763  */
34764 Roo.tree.TreeSorter = function(tree, config){
34765     Roo.apply(this, config);
34766     tree.on("beforechildrenrendered", this.doSort, this);
34767     tree.on("append", this.updateSort, this);
34768     tree.on("insert", this.updateSort, this);
34769     
34770     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34771     var p = this.property || "text";
34772     var sortType = this.sortType;
34773     var fs = this.folderSort;
34774     var cs = this.caseSensitive === true;
34775     var leafAttr = this.leafAttr || 'leaf';
34776
34777     this.sortFn = function(n1, n2){
34778         if(fs){
34779             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34780                 return 1;
34781             }
34782             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34783                 return -1;
34784             }
34785         }
34786         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34787         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34788         if(v1 < v2){
34789                         return dsc ? +1 : -1;
34790                 }else if(v1 > v2){
34791                         return dsc ? -1 : +1;
34792         }else{
34793                 return 0;
34794         }
34795     };
34796 };
34797
34798 Roo.tree.TreeSorter.prototype = {
34799     doSort : function(node){
34800         node.sort(this.sortFn);
34801     },
34802     
34803     compareNodes : function(n1, n2){
34804         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34805     },
34806     
34807     updateSort : function(tree, node){
34808         if(node.childrenRendered){
34809             this.doSort.defer(1, this, [node]);
34810         }
34811     }
34812 };/*
34813  * Based on:
34814  * Ext JS Library 1.1.1
34815  * Copyright(c) 2006-2007, Ext JS, LLC.
34816  *
34817  * Originally Released Under LGPL - original licence link has changed is not relivant.
34818  *
34819  * Fork - LGPL
34820  * <script type="text/javascript">
34821  */
34822
34823 if(Roo.dd.DropZone){
34824     
34825 Roo.tree.TreeDropZone = function(tree, config){
34826     this.allowParentInsert = false;
34827     this.allowContainerDrop = false;
34828     this.appendOnly = false;
34829     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34830     this.tree = tree;
34831     this.lastInsertClass = "x-tree-no-status";
34832     this.dragOverData = {};
34833 };
34834
34835 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34836     ddGroup : "TreeDD",
34837     scroll:  true,
34838     
34839     expandDelay : 1000,
34840     
34841     expandNode : function(node){
34842         if(node.hasChildNodes() && !node.isExpanded()){
34843             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34844         }
34845     },
34846     
34847     queueExpand : function(node){
34848         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34849     },
34850     
34851     cancelExpand : function(){
34852         if(this.expandProcId){
34853             clearTimeout(this.expandProcId);
34854             this.expandProcId = false;
34855         }
34856     },
34857     
34858     isValidDropPoint : function(n, pt, dd, e, data){
34859         if(!n || !data){ return false; }
34860         var targetNode = n.node;
34861         var dropNode = data.node;
34862         // default drop rules
34863         if(!(targetNode && targetNode.isTarget && pt)){
34864             return false;
34865         }
34866         if(pt == "append" && targetNode.allowChildren === false){
34867             return false;
34868         }
34869         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34870             return false;
34871         }
34872         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34873             return false;
34874         }
34875         // reuse the object
34876         var overEvent = this.dragOverData;
34877         overEvent.tree = this.tree;
34878         overEvent.target = targetNode;
34879         overEvent.data = data;
34880         overEvent.point = pt;
34881         overEvent.source = dd;
34882         overEvent.rawEvent = e;
34883         overEvent.dropNode = dropNode;
34884         overEvent.cancel = false;  
34885         var result = this.tree.fireEvent("nodedragover", overEvent);
34886         return overEvent.cancel === false && result !== false;
34887     },
34888     
34889     getDropPoint : function(e, n, dd)
34890     {
34891         var tn = n.node;
34892         if(tn.isRoot){
34893             return tn.allowChildren !== false ? "append" : false; // always append for root
34894         }
34895         var dragEl = n.ddel;
34896         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34897         var y = Roo.lib.Event.getPageY(e);
34898         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34899         
34900         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34901         var noAppend = tn.allowChildren === false;
34902         if(this.appendOnly || tn.parentNode.allowChildren === false){
34903             return noAppend ? false : "append";
34904         }
34905         var noBelow = false;
34906         if(!this.allowParentInsert){
34907             noBelow = tn.hasChildNodes() && tn.isExpanded();
34908         }
34909         var q = (b - t) / (noAppend ? 2 : 3);
34910         if(y >= t && y < (t + q)){
34911             return "above";
34912         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34913             return "below";
34914         }else{
34915             return "append";
34916         }
34917     },
34918     
34919     onNodeEnter : function(n, dd, e, data)
34920     {
34921         this.cancelExpand();
34922     },
34923     
34924     onNodeOver : function(n, dd, e, data)
34925     {
34926        
34927         var pt = this.getDropPoint(e, n, dd);
34928         var node = n.node;
34929         
34930         // auto node expand check
34931         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34932             this.queueExpand(node);
34933         }else if(pt != "append"){
34934             this.cancelExpand();
34935         }
34936         
34937         // set the insert point style on the target node
34938         var returnCls = this.dropNotAllowed;
34939         if(this.isValidDropPoint(n, pt, dd, e, data)){
34940            if(pt){
34941                var el = n.ddel;
34942                var cls;
34943                if(pt == "above"){
34944                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34945                    cls = "x-tree-drag-insert-above";
34946                }else if(pt == "below"){
34947                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34948                    cls = "x-tree-drag-insert-below";
34949                }else{
34950                    returnCls = "x-tree-drop-ok-append";
34951                    cls = "x-tree-drag-append";
34952                }
34953                if(this.lastInsertClass != cls){
34954                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34955                    this.lastInsertClass = cls;
34956                }
34957            }
34958        }
34959        return returnCls;
34960     },
34961     
34962     onNodeOut : function(n, dd, e, data){
34963         
34964         this.cancelExpand();
34965         this.removeDropIndicators(n);
34966     },
34967     
34968     onNodeDrop : function(n, dd, e, data){
34969         var point = this.getDropPoint(e, n, dd);
34970         var targetNode = n.node;
34971         targetNode.ui.startDrop();
34972         if(!this.isValidDropPoint(n, point, dd, e, data)){
34973             targetNode.ui.endDrop();
34974             return false;
34975         }
34976         // first try to find the drop node
34977         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34978         var dropEvent = {
34979             tree : this.tree,
34980             target: targetNode,
34981             data: data,
34982             point: point,
34983             source: dd,
34984             rawEvent: e,
34985             dropNode: dropNode,
34986             cancel: !dropNode   
34987         };
34988         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34989         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34990             targetNode.ui.endDrop();
34991             return false;
34992         }
34993         // allow target changing
34994         targetNode = dropEvent.target;
34995         if(point == "append" && !targetNode.isExpanded()){
34996             targetNode.expand(false, null, function(){
34997                 this.completeDrop(dropEvent);
34998             }.createDelegate(this));
34999         }else{
35000             this.completeDrop(dropEvent);
35001         }
35002         return true;
35003     },
35004     
35005     completeDrop : function(de){
35006         var ns = de.dropNode, p = de.point, t = de.target;
35007         if(!(ns instanceof Array)){
35008             ns = [ns];
35009         }
35010         var n;
35011         for(var i = 0, len = ns.length; i < len; i++){
35012             n = ns[i];
35013             if(p == "above"){
35014                 t.parentNode.insertBefore(n, t);
35015             }else if(p == "below"){
35016                 t.parentNode.insertBefore(n, t.nextSibling);
35017             }else{
35018                 t.appendChild(n);
35019             }
35020         }
35021         n.ui.focus();
35022         if(this.tree.hlDrop){
35023             n.ui.highlight();
35024         }
35025         t.ui.endDrop();
35026         this.tree.fireEvent("nodedrop", de);
35027     },
35028     
35029     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
35030         if(this.tree.hlDrop){
35031             dropNode.ui.focus();
35032             dropNode.ui.highlight();
35033         }
35034         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
35035     },
35036     
35037     getTree : function(){
35038         return this.tree;
35039     },
35040     
35041     removeDropIndicators : function(n){
35042         if(n && n.ddel){
35043             var el = n.ddel;
35044             Roo.fly(el).removeClass([
35045                     "x-tree-drag-insert-above",
35046                     "x-tree-drag-insert-below",
35047                     "x-tree-drag-append"]);
35048             this.lastInsertClass = "_noclass";
35049         }
35050     },
35051     
35052     beforeDragDrop : function(target, e, id){
35053         this.cancelExpand();
35054         return true;
35055     },
35056     
35057     afterRepair : function(data){
35058         if(data && Roo.enableFx){
35059             data.node.ui.highlight();
35060         }
35061         this.hideProxy();
35062     } 
35063     
35064 });
35065
35066 }
35067 /*
35068  * Based on:
35069  * Ext JS Library 1.1.1
35070  * Copyright(c) 2006-2007, Ext JS, LLC.
35071  *
35072  * Originally Released Under LGPL - original licence link has changed is not relivant.
35073  *
35074  * Fork - LGPL
35075  * <script type="text/javascript">
35076  */
35077  
35078
35079 if(Roo.dd.DragZone){
35080 Roo.tree.TreeDragZone = function(tree, config){
35081     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
35082     this.tree = tree;
35083 };
35084
35085 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
35086     ddGroup : "TreeDD",
35087    
35088     onBeforeDrag : function(data, e){
35089         var n = data.node;
35090         return n && n.draggable && !n.disabled;
35091     },
35092      
35093     
35094     onInitDrag : function(e){
35095         var data = this.dragData;
35096         this.tree.getSelectionModel().select(data.node);
35097         this.proxy.update("");
35098         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
35099         this.tree.fireEvent("startdrag", this.tree, data.node, e);
35100     },
35101     
35102     getRepairXY : function(e, data){
35103         return data.node.ui.getDDRepairXY();
35104     },
35105     
35106     onEndDrag : function(data, e){
35107         this.tree.fireEvent("enddrag", this.tree, data.node, e);
35108         
35109         
35110     },
35111     
35112     onValidDrop : function(dd, e, id){
35113         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
35114         this.hideProxy();
35115     },
35116     
35117     beforeInvalidDrop : function(e, id){
35118         // this scrolls the original position back into view
35119         var sm = this.tree.getSelectionModel();
35120         sm.clearSelections();
35121         sm.select(this.dragData.node);
35122     }
35123 });
35124 }/*
35125  * Based on:
35126  * Ext JS Library 1.1.1
35127  * Copyright(c) 2006-2007, Ext JS, LLC.
35128  *
35129  * Originally Released Under LGPL - original licence link has changed is not relivant.
35130  *
35131  * Fork - LGPL
35132  * <script type="text/javascript">
35133  */
35134 /**
35135  * @class Roo.tree.TreeEditor
35136  * @extends Roo.Editor
35137  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
35138  * as the editor field.
35139  * @constructor
35140  * @param {Object} config (used to be the tree panel.)
35141  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
35142  * 
35143  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
35144  * @cfg {Roo.form.TextField|Object} field The field configuration
35145  *
35146  * 
35147  */
35148 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
35149     var tree = config;
35150     var field;
35151     if (oldconfig) { // old style..
35152         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
35153     } else {
35154         // new style..
35155         tree = config.tree;
35156         config.field = config.field  || {};
35157         config.field.xtype = 'TextField';
35158         field = Roo.factory(config.field, Roo.form);
35159     }
35160     config = config || {};
35161     
35162     
35163     this.addEvents({
35164         /**
35165          * @event beforenodeedit
35166          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35167          * false from the handler of this event.
35168          * @param {Editor} this
35169          * @param {Roo.tree.Node} node 
35170          */
35171         "beforenodeedit" : true
35172     });
35173     
35174     //Roo.log(config);
35175     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
35176
35177     this.tree = tree;
35178
35179     tree.on('beforeclick', this.beforeNodeClick, this);
35180     tree.getTreeEl().on('mousedown', this.hide, this);
35181     this.on('complete', this.updateNode, this);
35182     this.on('beforestartedit', this.fitToTree, this);
35183     this.on('startedit', this.bindScroll, this, {delay:10});
35184     this.on('specialkey', this.onSpecialKey, this);
35185 };
35186
35187 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
35188     /**
35189      * @cfg {String} alignment
35190      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
35191      */
35192     alignment: "l-l",
35193     // inherit
35194     autoSize: false,
35195     /**
35196      * @cfg {Boolean} hideEl
35197      * True to hide the bound element while the editor is displayed (defaults to false)
35198      */
35199     hideEl : false,
35200     /**
35201      * @cfg {String} cls
35202      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35203      */
35204     cls: "x-small-editor x-tree-editor",
35205     /**
35206      * @cfg {Boolean} shim
35207      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35208      */
35209     shim:false,
35210     // inherit
35211     shadow:"frame",
35212     /**
35213      * @cfg {Number} maxWidth
35214      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35215      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35216      * scroll and client offsets into account prior to each edit.
35217      */
35218     maxWidth: 250,
35219
35220     editDelay : 350,
35221
35222     // private
35223     fitToTree : function(ed, el){
35224         var td = this.tree.getTreeEl().dom, nd = el.dom;
35225         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35226             td.scrollLeft = nd.offsetLeft;
35227         }
35228         var w = Math.min(
35229                 this.maxWidth,
35230                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35231         this.setSize(w, '');
35232         
35233         return this.fireEvent('beforenodeedit', this, this.editNode);
35234         
35235     },
35236
35237     // private
35238     triggerEdit : function(node){
35239         this.completeEdit();
35240         this.editNode = node;
35241         this.startEdit(node.ui.textNode, node.text);
35242     },
35243
35244     // private
35245     bindScroll : function(){
35246         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35247     },
35248
35249     // private
35250     beforeNodeClick : function(node, e){
35251         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35252         this.lastClick = new Date();
35253         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35254             e.stopEvent();
35255             this.triggerEdit(node);
35256             return false;
35257         }
35258         return true;
35259     },
35260
35261     // private
35262     updateNode : function(ed, value){
35263         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35264         this.editNode.setText(value);
35265     },
35266
35267     // private
35268     onHide : function(){
35269         Roo.tree.TreeEditor.superclass.onHide.call(this);
35270         if(this.editNode){
35271             this.editNode.ui.focus();
35272         }
35273     },
35274
35275     // private
35276     onSpecialKey : function(field, e){
35277         var k = e.getKey();
35278         if(k == e.ESC){
35279             e.stopEvent();
35280             this.cancelEdit();
35281         }else if(k == e.ENTER && !e.hasModifier()){
35282             e.stopEvent();
35283             this.completeEdit();
35284         }
35285     }
35286 });//<Script type="text/javascript">
35287 /*
35288  * Based on:
35289  * Ext JS Library 1.1.1
35290  * Copyright(c) 2006-2007, Ext JS, LLC.
35291  *
35292  * Originally Released Under LGPL - original licence link has changed is not relivant.
35293  *
35294  * Fork - LGPL
35295  * <script type="text/javascript">
35296  */
35297  
35298 /**
35299  * Not documented??? - probably should be...
35300  */
35301
35302 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35303     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35304     
35305     renderElements : function(n, a, targetNode, bulkRender){
35306         //consel.log("renderElements?");
35307         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35308
35309         var t = n.getOwnerTree();
35310         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35311         
35312         var cols = t.columns;
35313         var bw = t.borderWidth;
35314         var c = cols[0];
35315         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35316          var cb = typeof a.checked == "boolean";
35317         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35318         var colcls = 'x-t-' + tid + '-c0';
35319         var buf = [
35320             '<li class="x-tree-node">',
35321             
35322                 
35323                 '<div class="x-tree-node-el ', a.cls,'">',
35324                     // extran...
35325                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35326                 
35327                 
35328                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35329                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35330                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35331                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35332                            (a.iconCls ? ' '+a.iconCls : ''),
35333                            '" unselectable="on" />',
35334                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35335                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35336                              
35337                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35338                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35339                             '<span unselectable="on" qtip="' + tx + '">',
35340                              tx,
35341                              '</span></a>' ,
35342                     '</div>',
35343                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35344                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35345                  ];
35346         for(var i = 1, len = cols.length; i < len; i++){
35347             c = cols[i];
35348             colcls = 'x-t-' + tid + '-c' +i;
35349             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35350             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35351                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35352                       "</div>");
35353          }
35354          
35355          buf.push(
35356             '</a>',
35357             '<div class="x-clear"></div></div>',
35358             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35359             "</li>");
35360         
35361         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35362             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35363                                 n.nextSibling.ui.getEl(), buf.join(""));
35364         }else{
35365             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35366         }
35367         var el = this.wrap.firstChild;
35368         this.elRow = el;
35369         this.elNode = el.firstChild;
35370         this.ranchor = el.childNodes[1];
35371         this.ctNode = this.wrap.childNodes[1];
35372         var cs = el.firstChild.childNodes;
35373         this.indentNode = cs[0];
35374         this.ecNode = cs[1];
35375         this.iconNode = cs[2];
35376         var index = 3;
35377         if(cb){
35378             this.checkbox = cs[3];
35379             index++;
35380         }
35381         this.anchor = cs[index];
35382         
35383         this.textNode = cs[index].firstChild;
35384         
35385         //el.on("click", this.onClick, this);
35386         //el.on("dblclick", this.onDblClick, this);
35387         
35388         
35389        // console.log(this);
35390     },
35391     initEvents : function(){
35392         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35393         
35394             
35395         var a = this.ranchor;
35396
35397         var el = Roo.get(a);
35398
35399         if(Roo.isOpera){ // opera render bug ignores the CSS
35400             el.setStyle("text-decoration", "none");
35401         }
35402
35403         el.on("click", this.onClick, this);
35404         el.on("dblclick", this.onDblClick, this);
35405         el.on("contextmenu", this.onContextMenu, this);
35406         
35407     },
35408     
35409     /*onSelectedChange : function(state){
35410         if(state){
35411             this.focus();
35412             this.addClass("x-tree-selected");
35413         }else{
35414             //this.blur();
35415             this.removeClass("x-tree-selected");
35416         }
35417     },*/
35418     addClass : function(cls){
35419         if(this.elRow){
35420             Roo.fly(this.elRow).addClass(cls);
35421         }
35422         
35423     },
35424     
35425     
35426     removeClass : function(cls){
35427         if(this.elRow){
35428             Roo.fly(this.elRow).removeClass(cls);
35429         }
35430     }
35431
35432     
35433     
35434 });//<Script type="text/javascript">
35435
35436 /*
35437  * Based on:
35438  * Ext JS Library 1.1.1
35439  * Copyright(c) 2006-2007, Ext JS, LLC.
35440  *
35441  * Originally Released Under LGPL - original licence link has changed is not relivant.
35442  *
35443  * Fork - LGPL
35444  * <script type="text/javascript">
35445  */
35446  
35447
35448 /**
35449  * @class Roo.tree.ColumnTree
35450  * @extends Roo.data.TreePanel
35451  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35452  * @cfg {int} borderWidth  compined right/left border allowance
35453  * @constructor
35454  * @param {String/HTMLElement/Element} el The container element
35455  * @param {Object} config
35456  */
35457 Roo.tree.ColumnTree =  function(el, config)
35458 {
35459    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35460    this.addEvents({
35461         /**
35462         * @event resize
35463         * Fire this event on a container when it resizes
35464         * @param {int} w Width
35465         * @param {int} h Height
35466         */
35467        "resize" : true
35468     });
35469     this.on('resize', this.onResize, this);
35470 };
35471
35472 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35473     //lines:false,
35474     
35475     
35476     borderWidth: Roo.isBorderBox ? 0 : 2, 
35477     headEls : false,
35478     
35479     render : function(){
35480         // add the header.....
35481        
35482         Roo.tree.ColumnTree.superclass.render.apply(this);
35483         
35484         this.el.addClass('x-column-tree');
35485         
35486         this.headers = this.el.createChild(
35487             {cls:'x-tree-headers'},this.innerCt.dom);
35488    
35489         var cols = this.columns, c;
35490         var totalWidth = 0;
35491         this.headEls = [];
35492         var  len = cols.length;
35493         for(var i = 0; i < len; i++){
35494              c = cols[i];
35495              totalWidth += c.width;
35496             this.headEls.push(this.headers.createChild({
35497                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35498                  cn: {
35499                      cls:'x-tree-hd-text',
35500                      html: c.header
35501                  },
35502                  style:'width:'+(c.width-this.borderWidth)+'px;'
35503              }));
35504         }
35505         this.headers.createChild({cls:'x-clear'});
35506         // prevent floats from wrapping when clipped
35507         this.headers.setWidth(totalWidth);
35508         //this.innerCt.setWidth(totalWidth);
35509         this.innerCt.setStyle({ overflow: 'auto' });
35510         this.onResize(this.width, this.height);
35511              
35512         
35513     },
35514     onResize : function(w,h)
35515     {
35516         this.height = h;
35517         this.width = w;
35518         // resize cols..
35519         this.innerCt.setWidth(this.width);
35520         this.innerCt.setHeight(this.height-20);
35521         
35522         // headers...
35523         var cols = this.columns, c;
35524         var totalWidth = 0;
35525         var expEl = false;
35526         var len = cols.length;
35527         for(var i = 0; i < len; i++){
35528             c = cols[i];
35529             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35530                 // it's the expander..
35531                 expEl  = this.headEls[i];
35532                 continue;
35533             }
35534             totalWidth += c.width;
35535             
35536         }
35537         if (expEl) {
35538             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35539         }
35540         this.headers.setWidth(w-20);
35541
35542         
35543         
35544         
35545     }
35546 });
35547 /*
35548  * Based on:
35549  * Ext JS Library 1.1.1
35550  * Copyright(c) 2006-2007, Ext JS, LLC.
35551  *
35552  * Originally Released Under LGPL - original licence link has changed is not relivant.
35553  *
35554  * Fork - LGPL
35555  * <script type="text/javascript">
35556  */
35557  
35558 /**
35559  * @class Roo.menu.Menu
35560  * @extends Roo.util.Observable
35561  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35562  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35563  * @constructor
35564  * Creates a new Menu
35565  * @param {Object} config Configuration options
35566  */
35567 Roo.menu.Menu = function(config){
35568     Roo.apply(this, config);
35569     this.id = this.id || Roo.id();
35570     this.addEvents({
35571         /**
35572          * @event beforeshow
35573          * Fires before this menu is displayed
35574          * @param {Roo.menu.Menu} this
35575          */
35576         beforeshow : true,
35577         /**
35578          * @event beforehide
35579          * Fires before this menu is hidden
35580          * @param {Roo.menu.Menu} this
35581          */
35582         beforehide : true,
35583         /**
35584          * @event show
35585          * Fires after this menu is displayed
35586          * @param {Roo.menu.Menu} this
35587          */
35588         show : true,
35589         /**
35590          * @event hide
35591          * Fires after this menu is hidden
35592          * @param {Roo.menu.Menu} this
35593          */
35594         hide : true,
35595         /**
35596          * @event click
35597          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35598          * @param {Roo.menu.Menu} this
35599          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35600          * @param {Roo.EventObject} e
35601          */
35602         click : true,
35603         /**
35604          * @event mouseover
35605          * Fires when the mouse is hovering over this menu
35606          * @param {Roo.menu.Menu} this
35607          * @param {Roo.EventObject} e
35608          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35609          */
35610         mouseover : true,
35611         /**
35612          * @event mouseout
35613          * Fires when the mouse exits this menu
35614          * @param {Roo.menu.Menu} this
35615          * @param {Roo.EventObject} e
35616          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35617          */
35618         mouseout : true,
35619         /**
35620          * @event itemclick
35621          * Fires when a menu item contained in this menu is clicked
35622          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35623          * @param {Roo.EventObject} e
35624          */
35625         itemclick: true
35626     });
35627     if (this.registerMenu) {
35628         Roo.menu.MenuMgr.register(this);
35629     }
35630     
35631     var mis = this.items;
35632     this.items = new Roo.util.MixedCollection();
35633     if(mis){
35634         this.add.apply(this, mis);
35635     }
35636 };
35637
35638 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35639     /**
35640      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35641      */
35642     minWidth : 120,
35643     /**
35644      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35645      * for bottom-right shadow (defaults to "sides")
35646      */
35647     shadow : "sides",
35648     /**
35649      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35650      * this menu (defaults to "tl-tr?")
35651      */
35652     subMenuAlign : "tl-tr?",
35653     /**
35654      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35655      * relative to its element of origin (defaults to "tl-bl?")
35656      */
35657     defaultAlign : "tl-bl?",
35658     /**
35659      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35660      */
35661     allowOtherMenus : false,
35662     /**
35663      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35664      */
35665     registerMenu : true,
35666
35667     hidden:true,
35668
35669     // private
35670     render : function(){
35671         if(this.el){
35672             return;
35673         }
35674         var el = this.el = new Roo.Layer({
35675             cls: "x-menu",
35676             shadow:this.shadow,
35677             constrain: false,
35678             parentEl: this.parentEl || document.body,
35679             zindex:15000
35680         });
35681
35682         this.keyNav = new Roo.menu.MenuNav(this);
35683
35684         if(this.plain){
35685             el.addClass("x-menu-plain");
35686         }
35687         if(this.cls){
35688             el.addClass(this.cls);
35689         }
35690         // generic focus element
35691         this.focusEl = el.createChild({
35692             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35693         });
35694         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35695         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35696         
35697         ul.on("mouseover", this.onMouseOver, this);
35698         ul.on("mouseout", this.onMouseOut, this);
35699         this.items.each(function(item){
35700             if (item.hidden) {
35701                 return;
35702             }
35703             
35704             var li = document.createElement("li");
35705             li.className = "x-menu-list-item";
35706             ul.dom.appendChild(li);
35707             item.render(li, this);
35708         }, this);
35709         this.ul = ul;
35710         this.autoWidth();
35711     },
35712
35713     // private
35714     autoWidth : function(){
35715         var el = this.el, ul = this.ul;
35716         if(!el){
35717             return;
35718         }
35719         var w = this.width;
35720         if(w){
35721             el.setWidth(w);
35722         }else if(Roo.isIE){
35723             el.setWidth(this.minWidth);
35724             var t = el.dom.offsetWidth; // force recalc
35725             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35726         }
35727     },
35728
35729     // private
35730     delayAutoWidth : function(){
35731         if(this.rendered){
35732             if(!this.awTask){
35733                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35734             }
35735             this.awTask.delay(20);
35736         }
35737     },
35738
35739     // private
35740     findTargetItem : function(e){
35741         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35742         if(t && t.menuItemId){
35743             return this.items.get(t.menuItemId);
35744         }
35745     },
35746
35747     // private
35748     onClick : function(e){
35749         Roo.log("menu.onClick");
35750         var t = this.findTargetItem(e);
35751         if(!t){
35752             return;
35753         }
35754         Roo.log(e);
35755         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35756             if(t == this.activeItem && t.shouldDeactivate(e)){
35757                 this.activeItem.deactivate();
35758                 delete this.activeItem;
35759                 return;
35760             }
35761             if(t.canActivate){
35762                 this.setActiveItem(t, true);
35763             }
35764             return;
35765             
35766             
35767         }
35768         
35769         t.onClick(e);
35770         this.fireEvent("click", this, t, e);
35771     },
35772
35773     // private
35774     setActiveItem : function(item, autoExpand){
35775         if(item != this.activeItem){
35776             if(this.activeItem){
35777                 this.activeItem.deactivate();
35778             }
35779             this.activeItem = item;
35780             item.activate(autoExpand);
35781         }else if(autoExpand){
35782             item.expandMenu();
35783         }
35784     },
35785
35786     // private
35787     tryActivate : function(start, step){
35788         var items = this.items;
35789         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35790             var item = items.get(i);
35791             if(!item.disabled && item.canActivate){
35792                 this.setActiveItem(item, false);
35793                 return item;
35794             }
35795         }
35796         return false;
35797     },
35798
35799     // private
35800     onMouseOver : function(e){
35801         var t;
35802         if(t = this.findTargetItem(e)){
35803             if(t.canActivate && !t.disabled){
35804                 this.setActiveItem(t, true);
35805             }
35806         }
35807         this.fireEvent("mouseover", this, e, t);
35808     },
35809
35810     // private
35811     onMouseOut : function(e){
35812         var t;
35813         if(t = this.findTargetItem(e)){
35814             if(t == this.activeItem && t.shouldDeactivate(e)){
35815                 this.activeItem.deactivate();
35816                 delete this.activeItem;
35817             }
35818         }
35819         this.fireEvent("mouseout", this, e, t);
35820     },
35821
35822     /**
35823      * Read-only.  Returns true if the menu is currently displayed, else false.
35824      * @type Boolean
35825      */
35826     isVisible : function(){
35827         return this.el && !this.hidden;
35828     },
35829
35830     /**
35831      * Displays this menu relative to another element
35832      * @param {String/HTMLElement/Roo.Element} element The element to align to
35833      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35834      * the element (defaults to this.defaultAlign)
35835      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35836      */
35837     show : function(el, pos, parentMenu){
35838         this.parentMenu = parentMenu;
35839         if(!this.el){
35840             this.render();
35841         }
35842         this.fireEvent("beforeshow", this);
35843         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35844     },
35845
35846     /**
35847      * Displays this menu at a specific xy position
35848      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35849      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35850      */
35851     showAt : function(xy, parentMenu, /* private: */_e){
35852         this.parentMenu = parentMenu;
35853         if(!this.el){
35854             this.render();
35855         }
35856         if(_e !== false){
35857             this.fireEvent("beforeshow", this);
35858             xy = this.el.adjustForConstraints(xy);
35859         }
35860         this.el.setXY(xy);
35861         this.el.show();
35862         this.hidden = false;
35863         this.focus();
35864         this.fireEvent("show", this);
35865     },
35866
35867     focus : function(){
35868         if(!this.hidden){
35869             this.doFocus.defer(50, this);
35870         }
35871     },
35872
35873     doFocus : function(){
35874         if(!this.hidden){
35875             this.focusEl.focus();
35876         }
35877     },
35878
35879     /**
35880      * Hides this menu and optionally all parent menus
35881      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35882      */
35883     hide : function(deep){
35884         if(this.el && this.isVisible()){
35885             this.fireEvent("beforehide", this);
35886             if(this.activeItem){
35887                 this.activeItem.deactivate();
35888                 this.activeItem = null;
35889             }
35890             this.el.hide();
35891             this.hidden = true;
35892             this.fireEvent("hide", this);
35893         }
35894         if(deep === true && this.parentMenu){
35895             this.parentMenu.hide(true);
35896         }
35897     },
35898
35899     /**
35900      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35901      * Any of the following are valid:
35902      * <ul>
35903      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35904      * <li>An HTMLElement object which will be converted to a menu item</li>
35905      * <li>A menu item config object that will be created as a new menu item</li>
35906      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35907      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35908      * </ul>
35909      * Usage:
35910      * <pre><code>
35911 // Create the menu
35912 var menu = new Roo.menu.Menu();
35913
35914 // Create a menu item to add by reference
35915 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35916
35917 // Add a bunch of items at once using different methods.
35918 // Only the last item added will be returned.
35919 var item = menu.add(
35920     menuItem,                // add existing item by ref
35921     'Dynamic Item',          // new TextItem
35922     '-',                     // new separator
35923     { text: 'Config Item' }  // new item by config
35924 );
35925 </code></pre>
35926      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35927      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35928      */
35929     add : function(){
35930         var a = arguments, l = a.length, item;
35931         for(var i = 0; i < l; i++){
35932             var el = a[i];
35933             if ((typeof(el) == "object") && el.xtype && el.xns) {
35934                 el = Roo.factory(el, Roo.menu);
35935             }
35936             
35937             if(el.render){ // some kind of Item
35938                 item = this.addItem(el);
35939             }else if(typeof el == "string"){ // string
35940                 if(el == "separator" || el == "-"){
35941                     item = this.addSeparator();
35942                 }else{
35943                     item = this.addText(el);
35944                 }
35945             }else if(el.tagName || el.el){ // element
35946                 item = this.addElement(el);
35947             }else if(typeof el == "object"){ // must be menu item config?
35948                 item = this.addMenuItem(el);
35949             }
35950         }
35951         return item;
35952     },
35953
35954     /**
35955      * Returns this menu's underlying {@link Roo.Element} object
35956      * @return {Roo.Element} The element
35957      */
35958     getEl : function(){
35959         if(!this.el){
35960             this.render();
35961         }
35962         return this.el;
35963     },
35964
35965     /**
35966      * Adds a separator bar to the menu
35967      * @return {Roo.menu.Item} The menu item that was added
35968      */
35969     addSeparator : function(){
35970         return this.addItem(new Roo.menu.Separator());
35971     },
35972
35973     /**
35974      * Adds an {@link Roo.Element} object to the menu
35975      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35976      * @return {Roo.menu.Item} The menu item that was added
35977      */
35978     addElement : function(el){
35979         return this.addItem(new Roo.menu.BaseItem(el));
35980     },
35981
35982     /**
35983      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35984      * @param {Roo.menu.Item} item The menu item to add
35985      * @return {Roo.menu.Item} The menu item that was added
35986      */
35987     addItem : function(item){
35988         this.items.add(item);
35989         if(this.ul){
35990             var li = document.createElement("li");
35991             li.className = "x-menu-list-item";
35992             this.ul.dom.appendChild(li);
35993             item.render(li, this);
35994             this.delayAutoWidth();
35995         }
35996         return item;
35997     },
35998
35999     /**
36000      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
36001      * @param {Object} config A MenuItem config object
36002      * @return {Roo.menu.Item} The menu item that was added
36003      */
36004     addMenuItem : function(config){
36005         if(!(config instanceof Roo.menu.Item)){
36006             if(typeof config.checked == "boolean"){ // must be check menu item config?
36007                 config = new Roo.menu.CheckItem(config);
36008             }else{
36009                 config = new Roo.menu.Item(config);
36010             }
36011         }
36012         return this.addItem(config);
36013     },
36014
36015     /**
36016      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
36017      * @param {String} text The text to display in the menu item
36018      * @return {Roo.menu.Item} The menu item that was added
36019      */
36020     addText : function(text){
36021         return this.addItem(new Roo.menu.TextItem({ text : text }));
36022     },
36023
36024     /**
36025      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
36026      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
36027      * @param {Roo.menu.Item} item The menu item to add
36028      * @return {Roo.menu.Item} The menu item that was added
36029      */
36030     insert : function(index, item){
36031         this.items.insert(index, item);
36032         if(this.ul){
36033             var li = document.createElement("li");
36034             li.className = "x-menu-list-item";
36035             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
36036             item.render(li, this);
36037             this.delayAutoWidth();
36038         }
36039         return item;
36040     },
36041
36042     /**
36043      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
36044      * @param {Roo.menu.Item} item The menu item to remove
36045      */
36046     remove : function(item){
36047         this.items.removeKey(item.id);
36048         item.destroy();
36049     },
36050
36051     /**
36052      * Removes and destroys all items in the menu
36053      */
36054     removeAll : function(){
36055         var f;
36056         while(f = this.items.first()){
36057             this.remove(f);
36058         }
36059     }
36060 });
36061
36062 // MenuNav is a private utility class used internally by the Menu
36063 Roo.menu.MenuNav = function(menu){
36064     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
36065     this.scope = this.menu = menu;
36066 };
36067
36068 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
36069     doRelay : function(e, h){
36070         var k = e.getKey();
36071         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
36072             this.menu.tryActivate(0, 1);
36073             return false;
36074         }
36075         return h.call(this.scope || this, e, this.menu);
36076     },
36077
36078     up : function(e, m){
36079         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
36080             m.tryActivate(m.items.length-1, -1);
36081         }
36082     },
36083
36084     down : function(e, m){
36085         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
36086             m.tryActivate(0, 1);
36087         }
36088     },
36089
36090     right : function(e, m){
36091         if(m.activeItem){
36092             m.activeItem.expandMenu(true);
36093         }
36094     },
36095
36096     left : function(e, m){
36097         m.hide();
36098         if(m.parentMenu && m.parentMenu.activeItem){
36099             m.parentMenu.activeItem.activate();
36100         }
36101     },
36102
36103     enter : function(e, m){
36104         if(m.activeItem){
36105             e.stopPropagation();
36106             m.activeItem.onClick(e);
36107             m.fireEvent("click", this, m.activeItem);
36108             return true;
36109         }
36110     }
36111 });/*
36112  * Based on:
36113  * Ext JS Library 1.1.1
36114  * Copyright(c) 2006-2007, Ext JS, LLC.
36115  *
36116  * Originally Released Under LGPL - original licence link has changed is not relivant.
36117  *
36118  * Fork - LGPL
36119  * <script type="text/javascript">
36120  */
36121  
36122 /**
36123  * @class Roo.menu.MenuMgr
36124  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
36125  * @singleton
36126  */
36127 Roo.menu.MenuMgr = function(){
36128    var menus, active, groups = {}, attached = false, lastShow = new Date();
36129
36130    // private - called when first menu is created
36131    function init(){
36132        menus = {};
36133        active = new Roo.util.MixedCollection();
36134        Roo.get(document).addKeyListener(27, function(){
36135            if(active.length > 0){
36136                hideAll();
36137            }
36138        });
36139    }
36140
36141    // private
36142    function hideAll(){
36143        if(active && active.length > 0){
36144            var c = active.clone();
36145            c.each(function(m){
36146                m.hide();
36147            });
36148        }
36149    }
36150
36151    // private
36152    function onHide(m){
36153        active.remove(m);
36154        if(active.length < 1){
36155            Roo.get(document).un("mousedown", onMouseDown);
36156            attached = false;
36157        }
36158    }
36159
36160    // private
36161    function onShow(m){
36162        var last = active.last();
36163        lastShow = new Date();
36164        active.add(m);
36165        if(!attached){
36166            Roo.get(document).on("mousedown", onMouseDown);
36167            attached = true;
36168        }
36169        if(m.parentMenu){
36170           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
36171           m.parentMenu.activeChild = m;
36172        }else if(last && last.isVisible()){
36173           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
36174        }
36175    }
36176
36177    // private
36178    function onBeforeHide(m){
36179        if(m.activeChild){
36180            m.activeChild.hide();
36181        }
36182        if(m.autoHideTimer){
36183            clearTimeout(m.autoHideTimer);
36184            delete m.autoHideTimer;
36185        }
36186    }
36187
36188    // private
36189    function onBeforeShow(m){
36190        var pm = m.parentMenu;
36191        if(!pm && !m.allowOtherMenus){
36192            hideAll();
36193        }else if(pm && pm.activeChild && active != m){
36194            pm.activeChild.hide();
36195        }
36196    }
36197
36198    // private
36199    function onMouseDown(e){
36200        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36201            hideAll();
36202        }
36203    }
36204
36205    // private
36206    function onBeforeCheck(mi, state){
36207        if(state){
36208            var g = groups[mi.group];
36209            for(var i = 0, l = g.length; i < l; i++){
36210                if(g[i] != mi){
36211                    g[i].setChecked(false);
36212                }
36213            }
36214        }
36215    }
36216
36217    return {
36218
36219        /**
36220         * Hides all menus that are currently visible
36221         */
36222        hideAll : function(){
36223             hideAll();  
36224        },
36225
36226        // private
36227        register : function(menu){
36228            if(!menus){
36229                init();
36230            }
36231            menus[menu.id] = menu;
36232            menu.on("beforehide", onBeforeHide);
36233            menu.on("hide", onHide);
36234            menu.on("beforeshow", onBeforeShow);
36235            menu.on("show", onShow);
36236            var g = menu.group;
36237            if(g && menu.events["checkchange"]){
36238                if(!groups[g]){
36239                    groups[g] = [];
36240                }
36241                groups[g].push(menu);
36242                menu.on("checkchange", onCheck);
36243            }
36244        },
36245
36246         /**
36247          * Returns a {@link Roo.menu.Menu} object
36248          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36249          * be used to generate and return a new Menu instance.
36250          */
36251        get : function(menu){
36252            if(typeof menu == "string"){ // menu id
36253                return menus[menu];
36254            }else if(menu.events){  // menu instance
36255                return menu;
36256            }else if(typeof menu.length == 'number'){ // array of menu items?
36257                return new Roo.menu.Menu({items:menu});
36258            }else{ // otherwise, must be a config
36259                return new Roo.menu.Menu(menu);
36260            }
36261        },
36262
36263        // private
36264        unregister : function(menu){
36265            delete menus[menu.id];
36266            menu.un("beforehide", onBeforeHide);
36267            menu.un("hide", onHide);
36268            menu.un("beforeshow", onBeforeShow);
36269            menu.un("show", onShow);
36270            var g = menu.group;
36271            if(g && menu.events["checkchange"]){
36272                groups[g].remove(menu);
36273                menu.un("checkchange", onCheck);
36274            }
36275        },
36276
36277        // private
36278        registerCheckable : function(menuItem){
36279            var g = menuItem.group;
36280            if(g){
36281                if(!groups[g]){
36282                    groups[g] = [];
36283                }
36284                groups[g].push(menuItem);
36285                menuItem.on("beforecheckchange", onBeforeCheck);
36286            }
36287        },
36288
36289        // private
36290        unregisterCheckable : function(menuItem){
36291            var g = menuItem.group;
36292            if(g){
36293                groups[g].remove(menuItem);
36294                menuItem.un("beforecheckchange", onBeforeCheck);
36295            }
36296        }
36297    };
36298 }();/*
36299  * Based on:
36300  * Ext JS Library 1.1.1
36301  * Copyright(c) 2006-2007, Ext JS, LLC.
36302  *
36303  * Originally Released Under LGPL - original licence link has changed is not relivant.
36304  *
36305  * Fork - LGPL
36306  * <script type="text/javascript">
36307  */
36308  
36309
36310 /**
36311  * @class Roo.menu.BaseItem
36312  * @extends Roo.Component
36313  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36314  * management and base configuration options shared by all menu components.
36315  * @constructor
36316  * Creates a new BaseItem
36317  * @param {Object} config Configuration options
36318  */
36319 Roo.menu.BaseItem = function(config){
36320     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36321
36322     this.addEvents({
36323         /**
36324          * @event click
36325          * Fires when this item is clicked
36326          * @param {Roo.menu.BaseItem} this
36327          * @param {Roo.EventObject} e
36328          */
36329         click: true,
36330         /**
36331          * @event activate
36332          * Fires when this item is activated
36333          * @param {Roo.menu.BaseItem} this
36334          */
36335         activate : true,
36336         /**
36337          * @event deactivate
36338          * Fires when this item is deactivated
36339          * @param {Roo.menu.BaseItem} this
36340          */
36341         deactivate : true
36342     });
36343
36344     if(this.handler){
36345         this.on("click", this.handler, this.scope, true);
36346     }
36347 };
36348
36349 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36350     /**
36351      * @cfg {Function} handler
36352      * A function that will handle the click event of this menu item (defaults to undefined)
36353      */
36354     /**
36355      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36356      */
36357     canActivate : false,
36358     
36359      /**
36360      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36361      */
36362     hidden: false,
36363     
36364     /**
36365      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36366      */
36367     activeClass : "x-menu-item-active",
36368     /**
36369      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36370      */
36371     hideOnClick : true,
36372     /**
36373      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36374      */
36375     hideDelay : 100,
36376
36377     // private
36378     ctype: "Roo.menu.BaseItem",
36379
36380     // private
36381     actionMode : "container",
36382
36383     // private
36384     render : function(container, parentMenu){
36385         this.parentMenu = parentMenu;
36386         Roo.menu.BaseItem.superclass.render.call(this, container);
36387         this.container.menuItemId = this.id;
36388     },
36389
36390     // private
36391     onRender : function(container, position){
36392         this.el = Roo.get(this.el);
36393         container.dom.appendChild(this.el.dom);
36394     },
36395
36396     // private
36397     onClick : function(e){
36398         if(!this.disabled && this.fireEvent("click", this, e) !== false
36399                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36400             this.handleClick(e);
36401         }else{
36402             e.stopEvent();
36403         }
36404     },
36405
36406     // private
36407     activate : function(){
36408         if(this.disabled){
36409             return false;
36410         }
36411         var li = this.container;
36412         li.addClass(this.activeClass);
36413         this.region = li.getRegion().adjust(2, 2, -2, -2);
36414         this.fireEvent("activate", this);
36415         return true;
36416     },
36417
36418     // private
36419     deactivate : function(){
36420         this.container.removeClass(this.activeClass);
36421         this.fireEvent("deactivate", this);
36422     },
36423
36424     // private
36425     shouldDeactivate : function(e){
36426         return !this.region || !this.region.contains(e.getPoint());
36427     },
36428
36429     // private
36430     handleClick : function(e){
36431         if(this.hideOnClick){
36432             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36433         }
36434     },
36435
36436     // private
36437     expandMenu : function(autoActivate){
36438         // do nothing
36439     },
36440
36441     // private
36442     hideMenu : function(){
36443         // do nothing
36444     }
36445 });/*
36446  * Based on:
36447  * Ext JS Library 1.1.1
36448  * Copyright(c) 2006-2007, Ext JS, LLC.
36449  *
36450  * Originally Released Under LGPL - original licence link has changed is not relivant.
36451  *
36452  * Fork - LGPL
36453  * <script type="text/javascript">
36454  */
36455  
36456 /**
36457  * @class Roo.menu.Adapter
36458  * @extends Roo.menu.BaseItem
36459  * 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.
36460  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36461  * @constructor
36462  * Creates a new Adapter
36463  * @param {Object} config Configuration options
36464  */
36465 Roo.menu.Adapter = function(component, config){
36466     Roo.menu.Adapter.superclass.constructor.call(this, config);
36467     this.component = component;
36468 };
36469 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36470     // private
36471     canActivate : true,
36472
36473     // private
36474     onRender : function(container, position){
36475         this.component.render(container);
36476         this.el = this.component.getEl();
36477     },
36478
36479     // private
36480     activate : function(){
36481         if(this.disabled){
36482             return false;
36483         }
36484         this.component.focus();
36485         this.fireEvent("activate", this);
36486         return true;
36487     },
36488
36489     // private
36490     deactivate : function(){
36491         this.fireEvent("deactivate", this);
36492     },
36493
36494     // private
36495     disable : function(){
36496         this.component.disable();
36497         Roo.menu.Adapter.superclass.disable.call(this);
36498     },
36499
36500     // private
36501     enable : function(){
36502         this.component.enable();
36503         Roo.menu.Adapter.superclass.enable.call(this);
36504     }
36505 });/*
36506  * Based on:
36507  * Ext JS Library 1.1.1
36508  * Copyright(c) 2006-2007, Ext JS, LLC.
36509  *
36510  * Originally Released Under LGPL - original licence link has changed is not relivant.
36511  *
36512  * Fork - LGPL
36513  * <script type="text/javascript">
36514  */
36515
36516 /**
36517  * @class Roo.menu.TextItem
36518  * @extends Roo.menu.BaseItem
36519  * Adds a static text string to a menu, usually used as either a heading or group separator.
36520  * Note: old style constructor with text is still supported.
36521  * 
36522  * @constructor
36523  * Creates a new TextItem
36524  * @param {Object} cfg Configuration
36525  */
36526 Roo.menu.TextItem = function(cfg){
36527     if (typeof(cfg) == 'string') {
36528         this.text = cfg;
36529     } else {
36530         Roo.apply(this,cfg);
36531     }
36532     
36533     Roo.menu.TextItem.superclass.constructor.call(this);
36534 };
36535
36536 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36537     /**
36538      * @cfg {Boolean} text Text to show on item.
36539      */
36540     text : '',
36541     
36542     /**
36543      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36544      */
36545     hideOnClick : false,
36546     /**
36547      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36548      */
36549     itemCls : "x-menu-text",
36550
36551     // private
36552     onRender : function(){
36553         var s = document.createElement("span");
36554         s.className = this.itemCls;
36555         s.innerHTML = this.text;
36556         this.el = s;
36557         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36558     }
36559 });/*
36560  * Based on:
36561  * Ext JS Library 1.1.1
36562  * Copyright(c) 2006-2007, Ext JS, LLC.
36563  *
36564  * Originally Released Under LGPL - original licence link has changed is not relivant.
36565  *
36566  * Fork - LGPL
36567  * <script type="text/javascript">
36568  */
36569
36570 /**
36571  * @class Roo.menu.Separator
36572  * @extends Roo.menu.BaseItem
36573  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36574  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36575  * @constructor
36576  * @param {Object} config Configuration options
36577  */
36578 Roo.menu.Separator = function(config){
36579     Roo.menu.Separator.superclass.constructor.call(this, config);
36580 };
36581
36582 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36583     /**
36584      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36585      */
36586     itemCls : "x-menu-sep",
36587     /**
36588      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36589      */
36590     hideOnClick : false,
36591
36592     // private
36593     onRender : function(li){
36594         var s = document.createElement("span");
36595         s.className = this.itemCls;
36596         s.innerHTML = "&#160;";
36597         this.el = s;
36598         li.addClass("x-menu-sep-li");
36599         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36600     }
36601 });/*
36602  * Based on:
36603  * Ext JS Library 1.1.1
36604  * Copyright(c) 2006-2007, Ext JS, LLC.
36605  *
36606  * Originally Released Under LGPL - original licence link has changed is not relivant.
36607  *
36608  * Fork - LGPL
36609  * <script type="text/javascript">
36610  */
36611 /**
36612  * @class Roo.menu.Item
36613  * @extends Roo.menu.BaseItem
36614  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36615  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36616  * activation and click handling.
36617  * @constructor
36618  * Creates a new Item
36619  * @param {Object} config Configuration options
36620  */
36621 Roo.menu.Item = function(config){
36622     Roo.menu.Item.superclass.constructor.call(this, config);
36623     if(this.menu){
36624         this.menu = Roo.menu.MenuMgr.get(this.menu);
36625     }
36626 };
36627 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36628     
36629     /**
36630      * @cfg {String} text
36631      * The text to show on the menu item.
36632      */
36633     text: '',
36634      /**
36635      * @cfg {String} HTML to render in menu
36636      * The text to show on the menu item (HTML version).
36637      */
36638     html: '',
36639     /**
36640      * @cfg {String} icon
36641      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36642      */
36643     icon: undefined,
36644     /**
36645      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36646      */
36647     itemCls : "x-menu-item",
36648     /**
36649      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36650      */
36651     canActivate : true,
36652     /**
36653      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36654      */
36655     showDelay: 200,
36656     // doc'd in BaseItem
36657     hideDelay: 200,
36658
36659     // private
36660     ctype: "Roo.menu.Item",
36661     
36662     // private
36663     onRender : function(container, position){
36664         var el = document.createElement("a");
36665         el.hideFocus = true;
36666         el.unselectable = "on";
36667         el.href = this.href || "#";
36668         if(this.hrefTarget){
36669             el.target = this.hrefTarget;
36670         }
36671         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36672         
36673         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36674         
36675         el.innerHTML = String.format(
36676                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36677                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36678         this.el = el;
36679         Roo.menu.Item.superclass.onRender.call(this, container, position);
36680     },
36681
36682     /**
36683      * Sets the text to display in this menu item
36684      * @param {String} text The text to display
36685      * @param {Boolean} isHTML true to indicate text is pure html.
36686      */
36687     setText : function(text, isHTML){
36688         if (isHTML) {
36689             this.html = text;
36690         } else {
36691             this.text = text;
36692             this.html = '';
36693         }
36694         if(this.rendered){
36695             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36696      
36697             this.el.update(String.format(
36698                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36699                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36700             this.parentMenu.autoWidth();
36701         }
36702     },
36703
36704     // private
36705     handleClick : function(e){
36706         if(!this.href){ // if no link defined, stop the event automatically
36707             e.stopEvent();
36708         }
36709         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36710     },
36711
36712     // private
36713     activate : function(autoExpand){
36714         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36715             this.focus();
36716             if(autoExpand){
36717                 this.expandMenu();
36718             }
36719         }
36720         return true;
36721     },
36722
36723     // private
36724     shouldDeactivate : function(e){
36725         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36726             if(this.menu && this.menu.isVisible()){
36727                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36728             }
36729             return true;
36730         }
36731         return false;
36732     },
36733
36734     // private
36735     deactivate : function(){
36736         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36737         this.hideMenu();
36738     },
36739
36740     // private
36741     expandMenu : function(autoActivate){
36742         if(!this.disabled && this.menu){
36743             clearTimeout(this.hideTimer);
36744             delete this.hideTimer;
36745             if(!this.menu.isVisible() && !this.showTimer){
36746                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36747             }else if (this.menu.isVisible() && autoActivate){
36748                 this.menu.tryActivate(0, 1);
36749             }
36750         }
36751     },
36752
36753     // private
36754     deferExpand : function(autoActivate){
36755         delete this.showTimer;
36756         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36757         if(autoActivate){
36758             this.menu.tryActivate(0, 1);
36759         }
36760     },
36761
36762     // private
36763     hideMenu : function(){
36764         clearTimeout(this.showTimer);
36765         delete this.showTimer;
36766         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36767             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36768         }
36769     },
36770
36771     // private
36772     deferHide : function(){
36773         delete this.hideTimer;
36774         this.menu.hide();
36775     }
36776 });/*
36777  * Based on:
36778  * Ext JS Library 1.1.1
36779  * Copyright(c) 2006-2007, Ext JS, LLC.
36780  *
36781  * Originally Released Under LGPL - original licence link has changed is not relivant.
36782  *
36783  * Fork - LGPL
36784  * <script type="text/javascript">
36785  */
36786  
36787 /**
36788  * @class Roo.menu.CheckItem
36789  * @extends Roo.menu.Item
36790  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36791  * @constructor
36792  * Creates a new CheckItem
36793  * @param {Object} config Configuration options
36794  */
36795 Roo.menu.CheckItem = function(config){
36796     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36797     this.addEvents({
36798         /**
36799          * @event beforecheckchange
36800          * Fires before the checked value is set, providing an opportunity to cancel if needed
36801          * @param {Roo.menu.CheckItem} this
36802          * @param {Boolean} checked The new checked value that will be set
36803          */
36804         "beforecheckchange" : true,
36805         /**
36806          * @event checkchange
36807          * Fires after the checked value has been set
36808          * @param {Roo.menu.CheckItem} this
36809          * @param {Boolean} checked The checked value that was set
36810          */
36811         "checkchange" : true
36812     });
36813     if(this.checkHandler){
36814         this.on('checkchange', this.checkHandler, this.scope);
36815     }
36816 };
36817 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36818     /**
36819      * @cfg {String} group
36820      * All check items with the same group name will automatically be grouped into a single-select
36821      * radio button group (defaults to '')
36822      */
36823     /**
36824      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36825      */
36826     itemCls : "x-menu-item x-menu-check-item",
36827     /**
36828      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36829      */
36830     groupClass : "x-menu-group-item",
36831
36832     /**
36833      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36834      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36835      * initialized with checked = true will be rendered as checked.
36836      */
36837     checked: false,
36838
36839     // private
36840     ctype: "Roo.menu.CheckItem",
36841
36842     // private
36843     onRender : function(c){
36844         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36845         if(this.group){
36846             this.el.addClass(this.groupClass);
36847         }
36848         Roo.menu.MenuMgr.registerCheckable(this);
36849         if(this.checked){
36850             this.checked = false;
36851             this.setChecked(true, true);
36852         }
36853     },
36854
36855     // private
36856     destroy : function(){
36857         if(this.rendered){
36858             Roo.menu.MenuMgr.unregisterCheckable(this);
36859         }
36860         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36861     },
36862
36863     /**
36864      * Set the checked state of this item
36865      * @param {Boolean} checked The new checked value
36866      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36867      */
36868     setChecked : function(state, suppressEvent){
36869         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36870             if(this.container){
36871                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36872             }
36873             this.checked = state;
36874             if(suppressEvent !== true){
36875                 this.fireEvent("checkchange", this, state);
36876             }
36877         }
36878     },
36879
36880     // private
36881     handleClick : function(e){
36882        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36883            this.setChecked(!this.checked);
36884        }
36885        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36886     }
36887 });/*
36888  * Based on:
36889  * Ext JS Library 1.1.1
36890  * Copyright(c) 2006-2007, Ext JS, LLC.
36891  *
36892  * Originally Released Under LGPL - original licence link has changed is not relivant.
36893  *
36894  * Fork - LGPL
36895  * <script type="text/javascript">
36896  */
36897  
36898 /**
36899  * @class Roo.menu.DateItem
36900  * @extends Roo.menu.Adapter
36901  * A menu item that wraps the {@link Roo.DatPicker} component.
36902  * @constructor
36903  * Creates a new DateItem
36904  * @param {Object} config Configuration options
36905  */
36906 Roo.menu.DateItem = function(config){
36907     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36908     /** The Roo.DatePicker object @type Roo.DatePicker */
36909     this.picker = this.component;
36910     this.addEvents({select: true});
36911     
36912     this.picker.on("render", function(picker){
36913         picker.getEl().swallowEvent("click");
36914         picker.container.addClass("x-menu-date-item");
36915     });
36916
36917     this.picker.on("select", this.onSelect, this);
36918 };
36919
36920 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36921     // private
36922     onSelect : function(picker, date){
36923         this.fireEvent("select", this, date, picker);
36924         Roo.menu.DateItem.superclass.handleClick.call(this);
36925     }
36926 });/*
36927  * Based on:
36928  * Ext JS Library 1.1.1
36929  * Copyright(c) 2006-2007, Ext JS, LLC.
36930  *
36931  * Originally Released Under LGPL - original licence link has changed is not relivant.
36932  *
36933  * Fork - LGPL
36934  * <script type="text/javascript">
36935  */
36936  
36937 /**
36938  * @class Roo.menu.ColorItem
36939  * @extends Roo.menu.Adapter
36940  * A menu item that wraps the {@link Roo.ColorPalette} component.
36941  * @constructor
36942  * Creates a new ColorItem
36943  * @param {Object} config Configuration options
36944  */
36945 Roo.menu.ColorItem = function(config){
36946     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36947     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36948     this.palette = this.component;
36949     this.relayEvents(this.palette, ["select"]);
36950     if(this.selectHandler){
36951         this.on('select', this.selectHandler, this.scope);
36952     }
36953 };
36954 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36955  * Based on:
36956  * Ext JS Library 1.1.1
36957  * Copyright(c) 2006-2007, Ext JS, LLC.
36958  *
36959  * Originally Released Under LGPL - original licence link has changed is not relivant.
36960  *
36961  * Fork - LGPL
36962  * <script type="text/javascript">
36963  */
36964  
36965
36966 /**
36967  * @class Roo.menu.DateMenu
36968  * @extends Roo.menu.Menu
36969  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36970  * @constructor
36971  * Creates a new DateMenu
36972  * @param {Object} config Configuration options
36973  */
36974 Roo.menu.DateMenu = function(config){
36975     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36976     this.plain = true;
36977     var di = new Roo.menu.DateItem(config);
36978     this.add(di);
36979     /**
36980      * The {@link Roo.DatePicker} instance for this DateMenu
36981      * @type DatePicker
36982      */
36983     this.picker = di.picker;
36984     /**
36985      * @event select
36986      * @param {DatePicker} picker
36987      * @param {Date} date
36988      */
36989     this.relayEvents(di, ["select"]);
36990     this.on('beforeshow', function(){
36991         if(this.picker){
36992             this.picker.hideMonthPicker(false);
36993         }
36994     }, this);
36995 };
36996 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36997     cls:'x-date-menu'
36998 });/*
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.ColorMenu
37012  * @extends Roo.menu.Menu
37013  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
37014  * @constructor
37015  * Creates a new ColorMenu
37016  * @param {Object} config Configuration options
37017  */
37018 Roo.menu.ColorMenu = function(config){
37019     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
37020     this.plain = true;
37021     var ci = new Roo.menu.ColorItem(config);
37022     this.add(ci);
37023     /**
37024      * The {@link Roo.ColorPalette} instance for this ColorMenu
37025      * @type ColorPalette
37026      */
37027     this.palette = ci.palette;
37028     /**
37029      * @event select
37030      * @param {ColorPalette} palette
37031      * @param {String} color
37032      */
37033     this.relayEvents(ci, ["select"]);
37034 };
37035 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
37036  * Based on:
37037  * Ext JS Library 1.1.1
37038  * Copyright(c) 2006-2007, Ext JS, LLC.
37039  *
37040  * Originally Released Under LGPL - original licence link has changed is not relivant.
37041  *
37042  * Fork - LGPL
37043  * <script type="text/javascript">
37044  */
37045  
37046 /**
37047  * @class Roo.form.Field
37048  * @extends Roo.BoxComponent
37049  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
37050  * @constructor
37051  * Creates a new Field
37052  * @param {Object} config Configuration options
37053  */
37054 Roo.form.Field = function(config){
37055     Roo.form.Field.superclass.constructor.call(this, config);
37056 };
37057
37058 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
37059     /**
37060      * @cfg {String} fieldLabel Label to use when rendering a form.
37061      */
37062        /**
37063      * @cfg {String} qtip Mouse over tip
37064      */
37065      
37066     /**
37067      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
37068      */
37069     invalidClass : "x-form-invalid",
37070     /**
37071      * @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")
37072      */
37073     invalidText : "The value in this field is invalid",
37074     /**
37075      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
37076      */
37077     focusClass : "x-form-focus",
37078     /**
37079      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
37080       automatic validation (defaults to "keyup").
37081      */
37082     validationEvent : "keyup",
37083     /**
37084      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
37085      */
37086     validateOnBlur : true,
37087     /**
37088      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
37089      */
37090     validationDelay : 250,
37091     /**
37092      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37093      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
37094      */
37095     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
37096     /**
37097      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
37098      */
37099     fieldClass : "x-form-field",
37100     /**
37101      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
37102      *<pre>
37103 Value         Description
37104 -----------   ----------------------------------------------------------------------
37105 qtip          Display a quick tip when the user hovers over the field
37106 title         Display a default browser title attribute popup
37107 under         Add a block div beneath the field containing the error text
37108 side          Add an error icon to the right of the field with a popup on hover
37109 [element id]  Add the error text directly to the innerHTML of the specified element
37110 </pre>
37111      */
37112     msgTarget : 'qtip',
37113     /**
37114      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
37115      */
37116     msgFx : 'normal',
37117
37118     /**
37119      * @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.
37120      */
37121     readOnly : false,
37122
37123     /**
37124      * @cfg {Boolean} disabled True to disable the field (defaults to false).
37125      */
37126     disabled : false,
37127
37128     /**
37129      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
37130      */
37131     inputType : undefined,
37132     
37133     /**
37134      * @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).
37135          */
37136         tabIndex : undefined,
37137         
37138     // private
37139     isFormField : true,
37140
37141     // private
37142     hasFocus : false,
37143     /**
37144      * @property {Roo.Element} fieldEl
37145      * Element Containing the rendered Field (with label etc.)
37146      */
37147     /**
37148      * @cfg {Mixed} value A value to initialize this field with.
37149      */
37150     value : undefined,
37151
37152     /**
37153      * @cfg {String} name The field's HTML name attribute.
37154      */
37155     /**
37156      * @cfg {String} cls A CSS class to apply to the field's underlying element.
37157      */
37158
37159         // private ??
37160         initComponent : function(){
37161         Roo.form.Field.superclass.initComponent.call(this);
37162         this.addEvents({
37163             /**
37164              * @event focus
37165              * Fires when this field receives input focus.
37166              * @param {Roo.form.Field} this
37167              */
37168             focus : true,
37169             /**
37170              * @event blur
37171              * Fires when this field loses input focus.
37172              * @param {Roo.form.Field} this
37173              */
37174             blur : true,
37175             /**
37176              * @event specialkey
37177              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
37178              * {@link Roo.EventObject#getKey} to determine which key was pressed.
37179              * @param {Roo.form.Field} this
37180              * @param {Roo.EventObject} e The event object
37181              */
37182             specialkey : true,
37183             /**
37184              * @event change
37185              * Fires just before the field blurs if the field value has changed.
37186              * @param {Roo.form.Field} this
37187              * @param {Mixed} newValue The new value
37188              * @param {Mixed} oldValue The original value
37189              */
37190             change : true,
37191             /**
37192              * @event invalid
37193              * Fires after the field has been marked as invalid.
37194              * @param {Roo.form.Field} this
37195              * @param {String} msg The validation message
37196              */
37197             invalid : true,
37198             /**
37199              * @event valid
37200              * Fires after the field has been validated with no errors.
37201              * @param {Roo.form.Field} this
37202              */
37203             valid : true,
37204              /**
37205              * @event keyup
37206              * Fires after the key up
37207              * @param {Roo.form.Field} this
37208              * @param {Roo.EventObject}  e The event Object
37209              */
37210             keyup : true
37211         });
37212     },
37213
37214     /**
37215      * Returns the name attribute of the field if available
37216      * @return {String} name The field name
37217      */
37218     getName: function(){
37219          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37220     },
37221
37222     // private
37223     onRender : function(ct, position){
37224         Roo.form.Field.superclass.onRender.call(this, ct, position);
37225         if(!this.el){
37226             var cfg = this.getAutoCreate();
37227             if(!cfg.name){
37228                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37229             }
37230             if (!cfg.name.length) {
37231                 delete cfg.name;
37232             }
37233             if(this.inputType){
37234                 cfg.type = this.inputType;
37235             }
37236             this.el = ct.createChild(cfg, position);
37237         }
37238         var type = this.el.dom.type;
37239         if(type){
37240             if(type == 'password'){
37241                 type = 'text';
37242             }
37243             this.el.addClass('x-form-'+type);
37244         }
37245         if(this.readOnly){
37246             this.el.dom.readOnly = true;
37247         }
37248         if(this.tabIndex !== undefined){
37249             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37250         }
37251
37252         this.el.addClass([this.fieldClass, this.cls]);
37253         this.initValue();
37254     },
37255
37256     /**
37257      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37258      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37259      * @return {Roo.form.Field} this
37260      */
37261     applyTo : function(target){
37262         this.allowDomMove = false;
37263         this.el = Roo.get(target);
37264         this.render(this.el.dom.parentNode);
37265         return this;
37266     },
37267
37268     // private
37269     initValue : function(){
37270         if(this.value !== undefined){
37271             this.setValue(this.value);
37272         }else if(this.el.dom.value.length > 0){
37273             this.setValue(this.el.dom.value);
37274         }
37275     },
37276
37277     /**
37278      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37279      */
37280     isDirty : function() {
37281         if(this.disabled) {
37282             return false;
37283         }
37284         return String(this.getValue()) !== String(this.originalValue);
37285     },
37286
37287     // private
37288     afterRender : function(){
37289         Roo.form.Field.superclass.afterRender.call(this);
37290         this.initEvents();
37291     },
37292
37293     // private
37294     fireKey : function(e){
37295         //Roo.log('field ' + e.getKey());
37296         if(e.isNavKeyPress()){
37297             this.fireEvent("specialkey", this, e);
37298         }
37299     },
37300
37301     /**
37302      * Resets the current field value to the originally loaded value and clears any validation messages
37303      */
37304     reset : function(){
37305         this.setValue(this.resetValue);
37306         this.clearInvalid();
37307     },
37308
37309     // private
37310     initEvents : function(){
37311         // safari killled keypress - so keydown is now used..
37312         this.el.on("keydown" , this.fireKey,  this);
37313         this.el.on("focus", this.onFocus,  this);
37314         this.el.on("blur", this.onBlur,  this);
37315         this.el.relayEvent('keyup', this);
37316
37317         // reference to original value for reset
37318         this.originalValue = this.getValue();
37319         this.resetValue =  this.getValue();
37320     },
37321
37322     // private
37323     onFocus : function(){
37324         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37325             this.el.addClass(this.focusClass);
37326         }
37327         if(!this.hasFocus){
37328             this.hasFocus = true;
37329             this.startValue = this.getValue();
37330             this.fireEvent("focus", this);
37331         }
37332     },
37333
37334     beforeBlur : Roo.emptyFn,
37335
37336     // private
37337     onBlur : function(){
37338         this.beforeBlur();
37339         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37340             this.el.removeClass(this.focusClass);
37341         }
37342         this.hasFocus = false;
37343         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37344             this.validate();
37345         }
37346         var v = this.getValue();
37347         if(String(v) !== String(this.startValue)){
37348             this.fireEvent('change', this, v, this.startValue);
37349         }
37350         this.fireEvent("blur", this);
37351     },
37352
37353     /**
37354      * Returns whether or not the field value is currently valid
37355      * @param {Boolean} preventMark True to disable marking the field invalid
37356      * @return {Boolean} True if the value is valid, else false
37357      */
37358     isValid : function(preventMark){
37359         if(this.disabled){
37360             return true;
37361         }
37362         var restore = this.preventMark;
37363         this.preventMark = preventMark === true;
37364         var v = this.validateValue(this.processValue(this.getRawValue()));
37365         this.preventMark = restore;
37366         return v;
37367     },
37368
37369     /**
37370      * Validates the field value
37371      * @return {Boolean} True if the value is valid, else false
37372      */
37373     validate : function(){
37374         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37375             this.clearInvalid();
37376             return true;
37377         }
37378         return false;
37379     },
37380
37381     processValue : function(value){
37382         return value;
37383     },
37384
37385     // private
37386     // Subclasses should provide the validation implementation by overriding this
37387     validateValue : function(value){
37388         return true;
37389     },
37390
37391     /**
37392      * Mark this field as invalid
37393      * @param {String} msg The validation message
37394      */
37395     markInvalid : function(msg){
37396         if(!this.rendered || this.preventMark){ // not rendered
37397             return;
37398         }
37399         
37400         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37401         
37402         obj.el.addClass(this.invalidClass);
37403         msg = msg || this.invalidText;
37404         switch(this.msgTarget){
37405             case 'qtip':
37406                 obj.el.dom.qtip = msg;
37407                 obj.el.dom.qclass = 'x-form-invalid-tip';
37408                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37409                     Roo.QuickTips.enable();
37410                 }
37411                 break;
37412             case 'title':
37413                 this.el.dom.title = msg;
37414                 break;
37415             case 'under':
37416                 if(!this.errorEl){
37417                     var elp = this.el.findParent('.x-form-element', 5, true);
37418                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37419                     this.errorEl.setWidth(elp.getWidth(true)-20);
37420                 }
37421                 this.errorEl.update(msg);
37422                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37423                 break;
37424             case 'side':
37425                 if(!this.errorIcon){
37426                     var elp = this.el.findParent('.x-form-element', 5, true);
37427                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37428                 }
37429                 this.alignErrorIcon();
37430                 this.errorIcon.dom.qtip = msg;
37431                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37432                 this.errorIcon.show();
37433                 this.on('resize', this.alignErrorIcon, this);
37434                 break;
37435             default:
37436                 var t = Roo.getDom(this.msgTarget);
37437                 t.innerHTML = msg;
37438                 t.style.display = this.msgDisplay;
37439                 break;
37440         }
37441         this.fireEvent('invalid', this, msg);
37442     },
37443
37444     // private
37445     alignErrorIcon : function(){
37446         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37447     },
37448
37449     /**
37450      * Clear any invalid styles/messages for this field
37451      */
37452     clearInvalid : function(){
37453         if(!this.rendered || this.preventMark){ // not rendered
37454             return;
37455         }
37456         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37457         
37458         obj.el.removeClass(this.invalidClass);
37459         switch(this.msgTarget){
37460             case 'qtip':
37461                 obj.el.dom.qtip = '';
37462                 break;
37463             case 'title':
37464                 this.el.dom.title = '';
37465                 break;
37466             case 'under':
37467                 if(this.errorEl){
37468                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37469                 }
37470                 break;
37471             case 'side':
37472                 if(this.errorIcon){
37473                     this.errorIcon.dom.qtip = '';
37474                     this.errorIcon.hide();
37475                     this.un('resize', this.alignErrorIcon, this);
37476                 }
37477                 break;
37478             default:
37479                 var t = Roo.getDom(this.msgTarget);
37480                 t.innerHTML = '';
37481                 t.style.display = 'none';
37482                 break;
37483         }
37484         this.fireEvent('valid', this);
37485     },
37486
37487     /**
37488      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37489      * @return {Mixed} value The field value
37490      */
37491     getRawValue : function(){
37492         var v = this.el.getValue();
37493         
37494         return v;
37495     },
37496
37497     /**
37498      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37499      * @return {Mixed} value The field value
37500      */
37501     getValue : function(){
37502         var v = this.el.getValue();
37503          
37504         return v;
37505     },
37506
37507     /**
37508      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37509      * @param {Mixed} value The value to set
37510      */
37511     setRawValue : function(v){
37512         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37513     },
37514
37515     /**
37516      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37517      * @param {Mixed} value The value to set
37518      */
37519     setValue : function(v){
37520         this.value = v;
37521         if(this.rendered){
37522             this.el.dom.value = (v === null || v === undefined ? '' : v);
37523              this.validate();
37524         }
37525     },
37526
37527     adjustSize : function(w, h){
37528         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37529         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37530         return s;
37531     },
37532
37533     adjustWidth : function(tag, w){
37534         tag = tag.toLowerCase();
37535         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37536             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37537                 if(tag == 'input'){
37538                     return w + 2;
37539                 }
37540                 if(tag == 'textarea'){
37541                     return w-2;
37542                 }
37543             }else if(Roo.isOpera){
37544                 if(tag == 'input'){
37545                     return w + 2;
37546                 }
37547                 if(tag == 'textarea'){
37548                     return w-2;
37549                 }
37550             }
37551         }
37552         return w;
37553     }
37554 });
37555
37556
37557 // anything other than normal should be considered experimental
37558 Roo.form.Field.msgFx = {
37559     normal : {
37560         show: function(msgEl, f){
37561             msgEl.setDisplayed('block');
37562         },
37563
37564         hide : function(msgEl, f){
37565             msgEl.setDisplayed(false).update('');
37566         }
37567     },
37568
37569     slide : {
37570         show: function(msgEl, f){
37571             msgEl.slideIn('t', {stopFx:true});
37572         },
37573
37574         hide : function(msgEl, f){
37575             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37576         }
37577     },
37578
37579     slideRight : {
37580         show: function(msgEl, f){
37581             msgEl.fixDisplay();
37582             msgEl.alignTo(f.el, 'tl-tr');
37583             msgEl.slideIn('l', {stopFx:true});
37584         },
37585
37586         hide : function(msgEl, f){
37587             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37588         }
37589     }
37590 };/*
37591  * Based on:
37592  * Ext JS Library 1.1.1
37593  * Copyright(c) 2006-2007, Ext JS, LLC.
37594  *
37595  * Originally Released Under LGPL - original licence link has changed is not relivant.
37596  *
37597  * Fork - LGPL
37598  * <script type="text/javascript">
37599  */
37600  
37601
37602 /**
37603  * @class Roo.form.TextField
37604  * @extends Roo.form.Field
37605  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37606  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37607  * @constructor
37608  * Creates a new TextField
37609  * @param {Object} config Configuration options
37610  */
37611 Roo.form.TextField = function(config){
37612     Roo.form.TextField.superclass.constructor.call(this, config);
37613     this.addEvents({
37614         /**
37615          * @event autosize
37616          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37617          * according to the default logic, but this event provides a hook for the developer to apply additional
37618          * logic at runtime to resize the field if needed.
37619              * @param {Roo.form.Field} this This text field
37620              * @param {Number} width The new field width
37621              */
37622         autosize : true
37623     });
37624 };
37625
37626 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37627     /**
37628      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37629      */
37630     grow : false,
37631     /**
37632      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37633      */
37634     growMin : 30,
37635     /**
37636      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37637      */
37638     growMax : 800,
37639     /**
37640      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37641      */
37642     vtype : null,
37643     /**
37644      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37645      */
37646     maskRe : null,
37647     /**
37648      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37649      */
37650     disableKeyFilter : false,
37651     /**
37652      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37653      */
37654     allowBlank : true,
37655     /**
37656      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37657      */
37658     minLength : 0,
37659     /**
37660      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37661      */
37662     maxLength : Number.MAX_VALUE,
37663     /**
37664      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37665      */
37666     minLengthText : "The minimum length for this field is {0}",
37667     /**
37668      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37669      */
37670     maxLengthText : "The maximum length for this field is {0}",
37671     /**
37672      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37673      */
37674     selectOnFocus : false,
37675     /**
37676      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37677      */
37678     blankText : "This field is required",
37679     /**
37680      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37681      * If available, this function will be called only after the basic validators all return true, and will be passed the
37682      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37683      */
37684     validator : null,
37685     /**
37686      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37687      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37688      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37689      */
37690     regex : null,
37691     /**
37692      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37693      */
37694     regexText : "",
37695     /**
37696      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37697      */
37698     emptyText : null,
37699    
37700
37701     // private
37702     initEvents : function()
37703     {
37704         if (this.emptyText) {
37705             this.el.attr('placeholder', this.emptyText);
37706         }
37707         
37708         Roo.form.TextField.superclass.initEvents.call(this);
37709         if(this.validationEvent == 'keyup'){
37710             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37711             this.el.on('keyup', this.filterValidation, this);
37712         }
37713         else if(this.validationEvent !== false){
37714             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37715         }
37716         
37717         if(this.selectOnFocus){
37718             this.on("focus", this.preFocus, this);
37719             
37720         }
37721         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37722             this.el.on("keypress", this.filterKeys, this);
37723         }
37724         if(this.grow){
37725             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37726             this.el.on("click", this.autoSize,  this);
37727         }
37728         if(this.el.is('input[type=password]') && Roo.isSafari){
37729             this.el.on('keydown', this.SafariOnKeyDown, this);
37730         }
37731     },
37732
37733     processValue : function(value){
37734         if(this.stripCharsRe){
37735             var newValue = value.replace(this.stripCharsRe, '');
37736             if(newValue !== value){
37737                 this.setRawValue(newValue);
37738                 return newValue;
37739             }
37740         }
37741         return value;
37742     },
37743
37744     filterValidation : function(e){
37745         if(!e.isNavKeyPress()){
37746             this.validationTask.delay(this.validationDelay);
37747         }
37748     },
37749
37750     // private
37751     onKeyUp : function(e){
37752         if(!e.isNavKeyPress()){
37753             this.autoSize();
37754         }
37755     },
37756
37757     /**
37758      * Resets the current field value to the originally-loaded value and clears any validation messages.
37759      *  
37760      */
37761     reset : function(){
37762         Roo.form.TextField.superclass.reset.call(this);
37763        
37764     },
37765
37766     
37767     // private
37768     preFocus : function(){
37769         
37770         if(this.selectOnFocus){
37771             this.el.dom.select();
37772         }
37773     },
37774
37775     
37776     // private
37777     filterKeys : function(e){
37778         var k = e.getKey();
37779         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37780             return;
37781         }
37782         var c = e.getCharCode(), cc = String.fromCharCode(c);
37783         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37784             return;
37785         }
37786         if(!this.maskRe.test(cc)){
37787             e.stopEvent();
37788         }
37789     },
37790
37791     setValue : function(v){
37792         
37793         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37794         
37795         this.autoSize();
37796     },
37797
37798     /**
37799      * Validates a value according to the field's validation rules and marks the field as invalid
37800      * if the validation fails
37801      * @param {Mixed} value The value to validate
37802      * @return {Boolean} True if the value is valid, else false
37803      */
37804     validateValue : function(value){
37805         if(value.length < 1)  { // if it's blank
37806              if(this.allowBlank){
37807                 this.clearInvalid();
37808                 return true;
37809              }else{
37810                 this.markInvalid(this.blankText);
37811                 return false;
37812              }
37813         }
37814         if(value.length < this.minLength){
37815             this.markInvalid(String.format(this.minLengthText, this.minLength));
37816             return false;
37817         }
37818         if(value.length > this.maxLength){
37819             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37820             return false;
37821         }
37822         if(this.vtype){
37823             var vt = Roo.form.VTypes;
37824             if(!vt[this.vtype](value, this)){
37825                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37826                 return false;
37827             }
37828         }
37829         if(typeof this.validator == "function"){
37830             var msg = this.validator(value);
37831             if(msg !== true){
37832                 this.markInvalid(msg);
37833                 return false;
37834             }
37835         }
37836         if(this.regex && !this.regex.test(value)){
37837             this.markInvalid(this.regexText);
37838             return false;
37839         }
37840         return true;
37841     },
37842
37843     /**
37844      * Selects text in this field
37845      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37846      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37847      */
37848     selectText : function(start, end){
37849         var v = this.getRawValue();
37850         if(v.length > 0){
37851             start = start === undefined ? 0 : start;
37852             end = end === undefined ? v.length : end;
37853             var d = this.el.dom;
37854             if(d.setSelectionRange){
37855                 d.setSelectionRange(start, end);
37856             }else if(d.createTextRange){
37857                 var range = d.createTextRange();
37858                 range.moveStart("character", start);
37859                 range.moveEnd("character", v.length-end);
37860                 range.select();
37861             }
37862         }
37863     },
37864
37865     /**
37866      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37867      * This only takes effect if grow = true, and fires the autosize event.
37868      */
37869     autoSize : function(){
37870         if(!this.grow || !this.rendered){
37871             return;
37872         }
37873         if(!this.metrics){
37874             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37875         }
37876         var el = this.el;
37877         var v = el.dom.value;
37878         var d = document.createElement('div');
37879         d.appendChild(document.createTextNode(v));
37880         v = d.innerHTML;
37881         d = null;
37882         v += "&#160;";
37883         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37884         this.el.setWidth(w);
37885         this.fireEvent("autosize", this, w);
37886     },
37887     
37888     // private
37889     SafariOnKeyDown : function(event)
37890     {
37891         // this is a workaround for a password hang bug on chrome/ webkit.
37892         
37893         var isSelectAll = false;
37894         
37895         if(this.el.dom.selectionEnd > 0){
37896             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37897         }
37898         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37899             event.preventDefault();
37900             this.setValue('');
37901             return;
37902         }
37903         
37904         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
37905             
37906             event.preventDefault();
37907             // this is very hacky as keydown always get's upper case.
37908             
37909             var cc = String.fromCharCode(event.getCharCode());
37910             
37911             
37912             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37913             
37914         }
37915         
37916         
37917     }
37918 });/*
37919  * Based on:
37920  * Ext JS Library 1.1.1
37921  * Copyright(c) 2006-2007, Ext JS, LLC.
37922  *
37923  * Originally Released Under LGPL - original licence link has changed is not relivant.
37924  *
37925  * Fork - LGPL
37926  * <script type="text/javascript">
37927  */
37928  
37929 /**
37930  * @class Roo.form.Hidden
37931  * @extends Roo.form.TextField
37932  * Simple Hidden element used on forms 
37933  * 
37934  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37935  * 
37936  * @constructor
37937  * Creates a new Hidden form element.
37938  * @param {Object} config Configuration options
37939  */
37940
37941
37942
37943 // easy hidden field...
37944 Roo.form.Hidden = function(config){
37945     Roo.form.Hidden.superclass.constructor.call(this, config);
37946 };
37947   
37948 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37949     fieldLabel:      '',
37950     inputType:      'hidden',
37951     width:          50,
37952     allowBlank:     true,
37953     labelSeparator: '',
37954     hidden:         true,
37955     itemCls :       'x-form-item-display-none'
37956
37957
37958 });
37959
37960
37961 /*
37962  * Based on:
37963  * Ext JS Library 1.1.1
37964  * Copyright(c) 2006-2007, Ext JS, LLC.
37965  *
37966  * Originally Released Under LGPL - original licence link has changed is not relivant.
37967  *
37968  * Fork - LGPL
37969  * <script type="text/javascript">
37970  */
37971  
37972 /**
37973  * @class Roo.form.TriggerField
37974  * @extends Roo.form.TextField
37975  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37976  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37977  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37978  * for which you can provide a custom implementation.  For example:
37979  * <pre><code>
37980 var trigger = new Roo.form.TriggerField();
37981 trigger.onTriggerClick = myTriggerFn;
37982 trigger.applyTo('my-field');
37983 </code></pre>
37984  *
37985  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37986  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37987  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37988  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37989  * @constructor
37990  * Create a new TriggerField.
37991  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37992  * to the base TextField)
37993  */
37994 Roo.form.TriggerField = function(config){
37995     this.mimicing = false;
37996     Roo.form.TriggerField.superclass.constructor.call(this, config);
37997 };
37998
37999 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
38000     /**
38001      * @cfg {String} triggerClass A CSS class to apply to the trigger
38002      */
38003     /**
38004      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38005      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
38006      */
38007     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
38008     /**
38009      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
38010      */
38011     hideTrigger:false,
38012
38013     /** @cfg {Boolean} grow @hide */
38014     /** @cfg {Number} growMin @hide */
38015     /** @cfg {Number} growMax @hide */
38016
38017     /**
38018      * @hide 
38019      * @method
38020      */
38021     autoSize: Roo.emptyFn,
38022     // private
38023     monitorTab : true,
38024     // private
38025     deferHeight : true,
38026
38027     
38028     actionMode : 'wrap',
38029     // private
38030     onResize : function(w, h){
38031         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
38032         if(typeof w == 'number'){
38033             var x = w - this.trigger.getWidth();
38034             this.el.setWidth(this.adjustWidth('input', x));
38035             this.trigger.setStyle('left', x+'px');
38036         }
38037     },
38038
38039     // private
38040     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38041
38042     // private
38043     getResizeEl : function(){
38044         return this.wrap;
38045     },
38046
38047     // private
38048     getPositionEl : function(){
38049         return this.wrap;
38050     },
38051
38052     // private
38053     alignErrorIcon : function(){
38054         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
38055     },
38056
38057     // private
38058     onRender : function(ct, position){
38059         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
38060         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
38061         this.trigger = this.wrap.createChild(this.triggerConfig ||
38062                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
38063         if(this.hideTrigger){
38064             this.trigger.setDisplayed(false);
38065         }
38066         this.initTrigger();
38067         if(!this.width){
38068             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
38069         }
38070     },
38071
38072     // private
38073     initTrigger : function(){
38074         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
38075         this.trigger.addClassOnOver('x-form-trigger-over');
38076         this.trigger.addClassOnClick('x-form-trigger-click');
38077     },
38078
38079     // private
38080     onDestroy : function(){
38081         if(this.trigger){
38082             this.trigger.removeAllListeners();
38083             this.trigger.remove();
38084         }
38085         if(this.wrap){
38086             this.wrap.remove();
38087         }
38088         Roo.form.TriggerField.superclass.onDestroy.call(this);
38089     },
38090
38091     // private
38092     onFocus : function(){
38093         Roo.form.TriggerField.superclass.onFocus.call(this);
38094         if(!this.mimicing){
38095             this.wrap.addClass('x-trigger-wrap-focus');
38096             this.mimicing = true;
38097             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
38098             if(this.monitorTab){
38099                 this.el.on("keydown", this.checkTab, this);
38100             }
38101         }
38102     },
38103
38104     // private
38105     checkTab : function(e){
38106         if(e.getKey() == e.TAB){
38107             this.triggerBlur();
38108         }
38109     },
38110
38111     // private
38112     onBlur : function(){
38113         // do nothing
38114     },
38115
38116     // private
38117     mimicBlur : function(e, t){
38118         if(!this.wrap.contains(t) && this.validateBlur()){
38119             this.triggerBlur();
38120         }
38121     },
38122
38123     // private
38124     triggerBlur : function(){
38125         this.mimicing = false;
38126         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
38127         if(this.monitorTab){
38128             this.el.un("keydown", this.checkTab, this);
38129         }
38130         this.wrap.removeClass('x-trigger-wrap-focus');
38131         Roo.form.TriggerField.superclass.onBlur.call(this);
38132     },
38133
38134     // private
38135     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
38136     validateBlur : function(e, t){
38137         return true;
38138     },
38139
38140     // private
38141     onDisable : function(){
38142         Roo.form.TriggerField.superclass.onDisable.call(this);
38143         if(this.wrap){
38144             this.wrap.addClass('x-item-disabled');
38145         }
38146     },
38147
38148     // private
38149     onEnable : function(){
38150         Roo.form.TriggerField.superclass.onEnable.call(this);
38151         if(this.wrap){
38152             this.wrap.removeClass('x-item-disabled');
38153         }
38154     },
38155
38156     // private
38157     onShow : function(){
38158         var ae = this.getActionEl();
38159         
38160         if(ae){
38161             ae.dom.style.display = '';
38162             ae.dom.style.visibility = 'visible';
38163         }
38164     },
38165
38166     // private
38167     
38168     onHide : function(){
38169         var ae = this.getActionEl();
38170         ae.dom.style.display = 'none';
38171     },
38172
38173     /**
38174      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
38175      * by an implementing function.
38176      * @method
38177      * @param {EventObject} e
38178      */
38179     onTriggerClick : Roo.emptyFn
38180 });
38181
38182 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
38183 // to be extended by an implementing class.  For an example of implementing this class, see the custom
38184 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
38185 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
38186     initComponent : function(){
38187         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
38188
38189         this.triggerConfig = {
38190             tag:'span', cls:'x-form-twin-triggers', cn:[
38191             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
38192             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
38193         ]};
38194     },
38195
38196     getTrigger : function(index){
38197         return this.triggers[index];
38198     },
38199
38200     initTrigger : function(){
38201         var ts = this.trigger.select('.x-form-trigger', true);
38202         this.wrap.setStyle('overflow', 'hidden');
38203         var triggerField = this;
38204         ts.each(function(t, all, index){
38205             t.hide = function(){
38206                 var w = triggerField.wrap.getWidth();
38207                 this.dom.style.display = 'none';
38208                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38209             };
38210             t.show = function(){
38211                 var w = triggerField.wrap.getWidth();
38212                 this.dom.style.display = '';
38213                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38214             };
38215             var triggerIndex = 'Trigger'+(index+1);
38216
38217             if(this['hide'+triggerIndex]){
38218                 t.dom.style.display = 'none';
38219             }
38220             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38221             t.addClassOnOver('x-form-trigger-over');
38222             t.addClassOnClick('x-form-trigger-click');
38223         }, this);
38224         this.triggers = ts.elements;
38225     },
38226
38227     onTrigger1Click : Roo.emptyFn,
38228     onTrigger2Click : Roo.emptyFn
38229 });/*
38230  * Based on:
38231  * Ext JS Library 1.1.1
38232  * Copyright(c) 2006-2007, Ext JS, LLC.
38233  *
38234  * Originally Released Under LGPL - original licence link has changed is not relivant.
38235  *
38236  * Fork - LGPL
38237  * <script type="text/javascript">
38238  */
38239  
38240 /**
38241  * @class Roo.form.TextArea
38242  * @extends Roo.form.TextField
38243  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38244  * support for auto-sizing.
38245  * @constructor
38246  * Creates a new TextArea
38247  * @param {Object} config Configuration options
38248  */
38249 Roo.form.TextArea = function(config){
38250     Roo.form.TextArea.superclass.constructor.call(this, config);
38251     // these are provided exchanges for backwards compat
38252     // minHeight/maxHeight were replaced by growMin/growMax to be
38253     // compatible with TextField growing config values
38254     if(this.minHeight !== undefined){
38255         this.growMin = this.minHeight;
38256     }
38257     if(this.maxHeight !== undefined){
38258         this.growMax = this.maxHeight;
38259     }
38260 };
38261
38262 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38263     /**
38264      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38265      */
38266     growMin : 60,
38267     /**
38268      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38269      */
38270     growMax: 1000,
38271     /**
38272      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38273      * in the field (equivalent to setting overflow: hidden, defaults to false)
38274      */
38275     preventScrollbars: false,
38276     /**
38277      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38278      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38279      */
38280
38281     // private
38282     onRender : function(ct, position){
38283         if(!this.el){
38284             this.defaultAutoCreate = {
38285                 tag: "textarea",
38286                 style:"width:300px;height:60px;",
38287                 autocomplete: "new-password"
38288             };
38289         }
38290         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38291         if(this.grow){
38292             this.textSizeEl = Roo.DomHelper.append(document.body, {
38293                 tag: "pre", cls: "x-form-grow-sizer"
38294             });
38295             if(this.preventScrollbars){
38296                 this.el.setStyle("overflow", "hidden");
38297             }
38298             this.el.setHeight(this.growMin);
38299         }
38300     },
38301
38302     onDestroy : function(){
38303         if(this.textSizeEl){
38304             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38305         }
38306         Roo.form.TextArea.superclass.onDestroy.call(this);
38307     },
38308
38309     // private
38310     onKeyUp : function(e){
38311         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38312             this.autoSize();
38313         }
38314     },
38315
38316     /**
38317      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38318      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38319      */
38320     autoSize : function(){
38321         if(!this.grow || !this.textSizeEl){
38322             return;
38323         }
38324         var el = this.el;
38325         var v = el.dom.value;
38326         var ts = this.textSizeEl;
38327
38328         ts.innerHTML = '';
38329         ts.appendChild(document.createTextNode(v));
38330         v = ts.innerHTML;
38331
38332         Roo.fly(ts).setWidth(this.el.getWidth());
38333         if(v.length < 1){
38334             v = "&#160;&#160;";
38335         }else{
38336             if(Roo.isIE){
38337                 v = v.replace(/\n/g, '<p>&#160;</p>');
38338             }
38339             v += "&#160;\n&#160;";
38340         }
38341         ts.innerHTML = v;
38342         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38343         if(h != this.lastHeight){
38344             this.lastHeight = h;
38345             this.el.setHeight(h);
38346             this.fireEvent("autosize", this, h);
38347         }
38348     }
38349 });/*
38350  * Based on:
38351  * Ext JS Library 1.1.1
38352  * Copyright(c) 2006-2007, Ext JS, LLC.
38353  *
38354  * Originally Released Under LGPL - original licence link has changed is not relivant.
38355  *
38356  * Fork - LGPL
38357  * <script type="text/javascript">
38358  */
38359  
38360
38361 /**
38362  * @class Roo.form.NumberField
38363  * @extends Roo.form.TextField
38364  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38365  * @constructor
38366  * Creates a new NumberField
38367  * @param {Object} config Configuration options
38368  */
38369 Roo.form.NumberField = function(config){
38370     Roo.form.NumberField.superclass.constructor.call(this, config);
38371 };
38372
38373 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38374     /**
38375      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38376      */
38377     fieldClass: "x-form-field x-form-num-field",
38378     /**
38379      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38380      */
38381     allowDecimals : true,
38382     /**
38383      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38384      */
38385     decimalSeparator : ".",
38386     /**
38387      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38388      */
38389     decimalPrecision : 2,
38390     /**
38391      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38392      */
38393     allowNegative : true,
38394     /**
38395      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38396      */
38397     minValue : Number.NEGATIVE_INFINITY,
38398     /**
38399      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38400      */
38401     maxValue : Number.MAX_VALUE,
38402     /**
38403      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38404      */
38405     minText : "The minimum value for this field is {0}",
38406     /**
38407      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38408      */
38409     maxText : "The maximum value for this field is {0}",
38410     /**
38411      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38412      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38413      */
38414     nanText : "{0} is not a valid number",
38415
38416     // private
38417     initEvents : function(){
38418         Roo.form.NumberField.superclass.initEvents.call(this);
38419         var allowed = "0123456789";
38420         if(this.allowDecimals){
38421             allowed += this.decimalSeparator;
38422         }
38423         if(this.allowNegative){
38424             allowed += "-";
38425         }
38426         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38427         var keyPress = function(e){
38428             var k = e.getKey();
38429             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38430                 return;
38431             }
38432             var c = e.getCharCode();
38433             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38434                 e.stopEvent();
38435             }
38436         };
38437         this.el.on("keypress", keyPress, this);
38438     },
38439
38440     // private
38441     validateValue : function(value){
38442         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38443             return false;
38444         }
38445         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38446              return true;
38447         }
38448         var num = this.parseValue(value);
38449         if(isNaN(num)){
38450             this.markInvalid(String.format(this.nanText, value));
38451             return false;
38452         }
38453         if(num < this.minValue){
38454             this.markInvalid(String.format(this.minText, this.minValue));
38455             return false;
38456         }
38457         if(num > this.maxValue){
38458             this.markInvalid(String.format(this.maxText, this.maxValue));
38459             return false;
38460         }
38461         return true;
38462     },
38463
38464     getValue : function(){
38465         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38466     },
38467
38468     // private
38469     parseValue : function(value){
38470         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38471         return isNaN(value) ? '' : value;
38472     },
38473
38474     // private
38475     fixPrecision : function(value){
38476         var nan = isNaN(value);
38477         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38478             return nan ? '' : value;
38479         }
38480         return parseFloat(value).toFixed(this.decimalPrecision);
38481     },
38482
38483     setValue : function(v){
38484         v = this.fixPrecision(v);
38485         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38486     },
38487
38488     // private
38489     decimalPrecisionFcn : function(v){
38490         return Math.floor(v);
38491     },
38492
38493     beforeBlur : function(){
38494         var v = this.parseValue(this.getRawValue());
38495         if(v){
38496             this.setValue(v);
38497         }
38498     }
38499 });/*
38500  * Based on:
38501  * Ext JS Library 1.1.1
38502  * Copyright(c) 2006-2007, Ext JS, LLC.
38503  *
38504  * Originally Released Under LGPL - original licence link has changed is not relivant.
38505  *
38506  * Fork - LGPL
38507  * <script type="text/javascript">
38508  */
38509  
38510 /**
38511  * @class Roo.form.DateField
38512  * @extends Roo.form.TriggerField
38513  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38514 * @constructor
38515 * Create a new DateField
38516 * @param {Object} config
38517  */
38518 Roo.form.DateField = function(config){
38519     Roo.form.DateField.superclass.constructor.call(this, config);
38520     
38521       this.addEvents({
38522          
38523         /**
38524          * @event select
38525          * Fires when a date is selected
38526              * @param {Roo.form.DateField} combo This combo box
38527              * @param {Date} date The date selected
38528              */
38529         'select' : true
38530          
38531     });
38532     
38533     
38534     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38535     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38536     this.ddMatch = null;
38537     if(this.disabledDates){
38538         var dd = this.disabledDates;
38539         var re = "(?:";
38540         for(var i = 0; i < dd.length; i++){
38541             re += dd[i];
38542             if(i != dd.length-1) re += "|";
38543         }
38544         this.ddMatch = new RegExp(re + ")");
38545     }
38546 };
38547
38548 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38549     /**
38550      * @cfg {String} format
38551      * The default date format string which can be overriden for localization support.  The format must be
38552      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38553      */
38554     format : "m/d/y",
38555     /**
38556      * @cfg {String} altFormats
38557      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38558      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38559      */
38560     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38561     /**
38562      * @cfg {Array} disabledDays
38563      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38564      */
38565     disabledDays : null,
38566     /**
38567      * @cfg {String} disabledDaysText
38568      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38569      */
38570     disabledDaysText : "Disabled",
38571     /**
38572      * @cfg {Array} disabledDates
38573      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38574      * expression so they are very powerful. Some examples:
38575      * <ul>
38576      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38577      * <li>["03/08", "09/16"] would disable those days for every year</li>
38578      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38579      * <li>["03/../2006"] would disable every day in March 2006</li>
38580      * <li>["^03"] would disable every day in every March</li>
38581      * </ul>
38582      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38583      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38584      */
38585     disabledDates : null,
38586     /**
38587      * @cfg {String} disabledDatesText
38588      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38589      */
38590     disabledDatesText : "Disabled",
38591     /**
38592      * @cfg {Date/String} minValue
38593      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38594      * valid format (defaults to null).
38595      */
38596     minValue : null,
38597     /**
38598      * @cfg {Date/String} maxValue
38599      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38600      * valid format (defaults to null).
38601      */
38602     maxValue : null,
38603     /**
38604      * @cfg {String} minText
38605      * The error text to display when the date in the cell is before minValue (defaults to
38606      * 'The date in this field must be after {minValue}').
38607      */
38608     minText : "The date in this field must be equal to or after {0}",
38609     /**
38610      * @cfg {String} maxText
38611      * The error text to display when the date in the cell is after maxValue (defaults to
38612      * 'The date in this field must be before {maxValue}').
38613      */
38614     maxText : "The date in this field must be equal to or before {0}",
38615     /**
38616      * @cfg {String} invalidText
38617      * The error text to display when the date in the field is invalid (defaults to
38618      * '{value} is not a valid date - it must be in the format {format}').
38619      */
38620     invalidText : "{0} is not a valid date - it must be in the format {1}",
38621     /**
38622      * @cfg {String} triggerClass
38623      * An additional CSS class used to style the trigger button.  The trigger will always get the
38624      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38625      * which displays a calendar icon).
38626      */
38627     triggerClass : 'x-form-date-trigger',
38628     
38629
38630     /**
38631      * @cfg {Boolean} useIso
38632      * if enabled, then the date field will use a hidden field to store the 
38633      * real value as iso formated date. default (false)
38634      */ 
38635     useIso : false,
38636     /**
38637      * @cfg {String/Object} autoCreate
38638      * A DomHelper element spec, or true for a default element spec (defaults to
38639      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38640      */ 
38641     // private
38642     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38643     
38644     // private
38645     hiddenField: false,
38646     
38647     onRender : function(ct, position)
38648     {
38649         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38650         if (this.useIso) {
38651             //this.el.dom.removeAttribute('name'); 
38652             Roo.log("Changing name?");
38653             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38654             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38655                     'before', true);
38656             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38657             // prevent input submission
38658             this.hiddenName = this.name;
38659         }
38660             
38661             
38662     },
38663     
38664     // private
38665     validateValue : function(value)
38666     {
38667         value = this.formatDate(value);
38668         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38669             Roo.log('super failed');
38670             return false;
38671         }
38672         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38673              return true;
38674         }
38675         var svalue = value;
38676         value = this.parseDate(value);
38677         if(!value){
38678             Roo.log('parse date failed' + svalue);
38679             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38680             return false;
38681         }
38682         var time = value.getTime();
38683         if(this.minValue && time < this.minValue.getTime()){
38684             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38685             return false;
38686         }
38687         if(this.maxValue && time > this.maxValue.getTime()){
38688             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38689             return false;
38690         }
38691         if(this.disabledDays){
38692             var day = value.getDay();
38693             for(var i = 0; i < this.disabledDays.length; i++) {
38694                 if(day === this.disabledDays[i]){
38695                     this.markInvalid(this.disabledDaysText);
38696                     return false;
38697                 }
38698             }
38699         }
38700         var fvalue = this.formatDate(value);
38701         if(this.ddMatch && this.ddMatch.test(fvalue)){
38702             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38703             return false;
38704         }
38705         return true;
38706     },
38707
38708     // private
38709     // Provides logic to override the default TriggerField.validateBlur which just returns true
38710     validateBlur : function(){
38711         return !this.menu || !this.menu.isVisible();
38712     },
38713     
38714     getName: function()
38715     {
38716         // returns hidden if it's set..
38717         if (!this.rendered) {return ''};
38718         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38719         
38720     },
38721
38722     /**
38723      * Returns the current date value of the date field.
38724      * @return {Date} The date value
38725      */
38726     getValue : function(){
38727         
38728         return  this.hiddenField ?
38729                 this.hiddenField.value :
38730                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38731     },
38732
38733     /**
38734      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38735      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38736      * (the default format used is "m/d/y").
38737      * <br />Usage:
38738      * <pre><code>
38739 //All of these calls set the same date value (May 4, 2006)
38740
38741 //Pass a date object:
38742 var dt = new Date('5/4/06');
38743 dateField.setValue(dt);
38744
38745 //Pass a date string (default format):
38746 dateField.setValue('5/4/06');
38747
38748 //Pass a date string (custom format):
38749 dateField.format = 'Y-m-d';
38750 dateField.setValue('2006-5-4');
38751 </code></pre>
38752      * @param {String/Date} date The date or valid date string
38753      */
38754     setValue : function(date){
38755         if (this.hiddenField) {
38756             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38757         }
38758         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38759         // make sure the value field is always stored as a date..
38760         this.value = this.parseDate(date);
38761         
38762         
38763     },
38764
38765     // private
38766     parseDate : function(value){
38767         if(!value || value instanceof Date){
38768             return value;
38769         }
38770         var v = Date.parseDate(value, this.format);
38771          if (!v && this.useIso) {
38772             v = Date.parseDate(value, 'Y-m-d');
38773         }
38774         if(!v && this.altFormats){
38775             if(!this.altFormatsArray){
38776                 this.altFormatsArray = this.altFormats.split("|");
38777             }
38778             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38779                 v = Date.parseDate(value, this.altFormatsArray[i]);
38780             }
38781         }
38782         return v;
38783     },
38784
38785     // private
38786     formatDate : function(date, fmt){
38787         return (!date || !(date instanceof Date)) ?
38788                date : date.dateFormat(fmt || this.format);
38789     },
38790
38791     // private
38792     menuListeners : {
38793         select: function(m, d){
38794             
38795             this.setValue(d);
38796             this.fireEvent('select', this, d);
38797         },
38798         show : function(){ // retain focus styling
38799             this.onFocus();
38800         },
38801         hide : function(){
38802             this.focus.defer(10, this);
38803             var ml = this.menuListeners;
38804             this.menu.un("select", ml.select,  this);
38805             this.menu.un("show", ml.show,  this);
38806             this.menu.un("hide", ml.hide,  this);
38807         }
38808     },
38809
38810     // private
38811     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38812     onTriggerClick : function(){
38813         if(this.disabled){
38814             return;
38815         }
38816         if(this.menu == null){
38817             this.menu = new Roo.menu.DateMenu();
38818         }
38819         Roo.apply(this.menu.picker,  {
38820             showClear: this.allowBlank,
38821             minDate : this.minValue,
38822             maxDate : this.maxValue,
38823             disabledDatesRE : this.ddMatch,
38824             disabledDatesText : this.disabledDatesText,
38825             disabledDays : this.disabledDays,
38826             disabledDaysText : this.disabledDaysText,
38827             format : this.useIso ? 'Y-m-d' : this.format,
38828             minText : String.format(this.minText, this.formatDate(this.minValue)),
38829             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38830         });
38831         this.menu.on(Roo.apply({}, this.menuListeners, {
38832             scope:this
38833         }));
38834         this.menu.picker.setValue(this.getValue() || new Date());
38835         this.menu.show(this.el, "tl-bl?");
38836     },
38837
38838     beforeBlur : function(){
38839         var v = this.parseDate(this.getRawValue());
38840         if(v){
38841             this.setValue(v);
38842         }
38843     },
38844
38845     /*@
38846      * overide
38847      * 
38848      */
38849     isDirty : function() {
38850         if(this.disabled) {
38851             return false;
38852         }
38853         
38854         if(typeof(this.startValue) === 'undefined'){
38855             return false;
38856         }
38857         
38858         return String(this.getValue()) !== String(this.startValue);
38859         
38860     }
38861 });/*
38862  * Based on:
38863  * Ext JS Library 1.1.1
38864  * Copyright(c) 2006-2007, Ext JS, LLC.
38865  *
38866  * Originally Released Under LGPL - original licence link has changed is not relivant.
38867  *
38868  * Fork - LGPL
38869  * <script type="text/javascript">
38870  */
38871  
38872 /**
38873  * @class Roo.form.MonthField
38874  * @extends Roo.form.TriggerField
38875  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38876 * @constructor
38877 * Create a new MonthField
38878 * @param {Object} config
38879  */
38880 Roo.form.MonthField = function(config){
38881     
38882     Roo.form.MonthField.superclass.constructor.call(this, config);
38883     
38884       this.addEvents({
38885          
38886         /**
38887          * @event select
38888          * Fires when a date is selected
38889              * @param {Roo.form.MonthFieeld} combo This combo box
38890              * @param {Date} date The date selected
38891              */
38892         'select' : true
38893          
38894     });
38895     
38896     
38897     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38898     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38899     this.ddMatch = null;
38900     if(this.disabledDates){
38901         var dd = this.disabledDates;
38902         var re = "(?:";
38903         for(var i = 0; i < dd.length; i++){
38904             re += dd[i];
38905             if(i != dd.length-1) re += "|";
38906         }
38907         this.ddMatch = new RegExp(re + ")");
38908     }
38909 };
38910
38911 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38912     /**
38913      * @cfg {String} format
38914      * The default date format string which can be overriden for localization support.  The format must be
38915      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38916      */
38917     format : "M Y",
38918     /**
38919      * @cfg {String} altFormats
38920      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38921      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38922      */
38923     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38924     /**
38925      * @cfg {Array} disabledDays
38926      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38927      */
38928     disabledDays : [0,1,2,3,4,5,6],
38929     /**
38930      * @cfg {String} disabledDaysText
38931      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38932      */
38933     disabledDaysText : "Disabled",
38934     /**
38935      * @cfg {Array} disabledDates
38936      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38937      * expression so they are very powerful. Some examples:
38938      * <ul>
38939      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38940      * <li>["03/08", "09/16"] would disable those days for every year</li>
38941      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38942      * <li>["03/../2006"] would disable every day in March 2006</li>
38943      * <li>["^03"] would disable every day in every March</li>
38944      * </ul>
38945      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38946      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38947      */
38948     disabledDates : null,
38949     /**
38950      * @cfg {String} disabledDatesText
38951      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38952      */
38953     disabledDatesText : "Disabled",
38954     /**
38955      * @cfg {Date/String} minValue
38956      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38957      * valid format (defaults to null).
38958      */
38959     minValue : null,
38960     /**
38961      * @cfg {Date/String} maxValue
38962      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38963      * valid format (defaults to null).
38964      */
38965     maxValue : null,
38966     /**
38967      * @cfg {String} minText
38968      * The error text to display when the date in the cell is before minValue (defaults to
38969      * 'The date in this field must be after {minValue}').
38970      */
38971     minText : "The date in this field must be equal to or after {0}",
38972     /**
38973      * @cfg {String} maxTextf
38974      * The error text to display when the date in the cell is after maxValue (defaults to
38975      * 'The date in this field must be before {maxValue}').
38976      */
38977     maxText : "The date in this field must be equal to or before {0}",
38978     /**
38979      * @cfg {String} invalidText
38980      * The error text to display when the date in the field is invalid (defaults to
38981      * '{value} is not a valid date - it must be in the format {format}').
38982      */
38983     invalidText : "{0} is not a valid date - it must be in the format {1}",
38984     /**
38985      * @cfg {String} triggerClass
38986      * An additional CSS class used to style the trigger button.  The trigger will always get the
38987      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38988      * which displays a calendar icon).
38989      */
38990     triggerClass : 'x-form-date-trigger',
38991     
38992
38993     /**
38994      * @cfg {Boolean} useIso
38995      * if enabled, then the date field will use a hidden field to store the 
38996      * real value as iso formated date. default (true)
38997      */ 
38998     useIso : true,
38999     /**
39000      * @cfg {String/Object} autoCreate
39001      * A DomHelper element spec, or true for a default element spec (defaults to
39002      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
39003      */ 
39004     // private
39005     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
39006     
39007     // private
39008     hiddenField: false,
39009     
39010     hideMonthPicker : false,
39011     
39012     onRender : function(ct, position)
39013     {
39014         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
39015         if (this.useIso) {
39016             this.el.dom.removeAttribute('name'); 
39017             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
39018                     'before', true);
39019             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
39020             // prevent input submission
39021             this.hiddenName = this.name;
39022         }
39023             
39024             
39025     },
39026     
39027     // private
39028     validateValue : function(value)
39029     {
39030         value = this.formatDate(value);
39031         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
39032             return false;
39033         }
39034         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
39035              return true;
39036         }
39037         var svalue = value;
39038         value = this.parseDate(value);
39039         if(!value){
39040             this.markInvalid(String.format(this.invalidText, svalue, this.format));
39041             return false;
39042         }
39043         var time = value.getTime();
39044         if(this.minValue && time < this.minValue.getTime()){
39045             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
39046             return false;
39047         }
39048         if(this.maxValue && time > this.maxValue.getTime()){
39049             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
39050             return false;
39051         }
39052         /*if(this.disabledDays){
39053             var day = value.getDay();
39054             for(var i = 0; i < this.disabledDays.length; i++) {
39055                 if(day === this.disabledDays[i]){
39056                     this.markInvalid(this.disabledDaysText);
39057                     return false;
39058                 }
39059             }
39060         }
39061         */
39062         var fvalue = this.formatDate(value);
39063         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
39064             this.markInvalid(String.format(this.disabledDatesText, fvalue));
39065             return false;
39066         }
39067         */
39068         return true;
39069     },
39070
39071     // private
39072     // Provides logic to override the default TriggerField.validateBlur which just returns true
39073     validateBlur : function(){
39074         return !this.menu || !this.menu.isVisible();
39075     },
39076
39077     /**
39078      * Returns the current date value of the date field.
39079      * @return {Date} The date value
39080      */
39081     getValue : function(){
39082         
39083         
39084         
39085         return  this.hiddenField ?
39086                 this.hiddenField.value :
39087                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
39088     },
39089
39090     /**
39091      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
39092      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
39093      * (the default format used is "m/d/y").
39094      * <br />Usage:
39095      * <pre><code>
39096 //All of these calls set the same date value (May 4, 2006)
39097
39098 //Pass a date object:
39099 var dt = new Date('5/4/06');
39100 monthField.setValue(dt);
39101
39102 //Pass a date string (default format):
39103 monthField.setValue('5/4/06');
39104
39105 //Pass a date string (custom format):
39106 monthField.format = 'Y-m-d';
39107 monthField.setValue('2006-5-4');
39108 </code></pre>
39109      * @param {String/Date} date The date or valid date string
39110      */
39111     setValue : function(date){
39112         Roo.log('month setValue' + date);
39113         // can only be first of month..
39114         
39115         var val = this.parseDate(date);
39116         
39117         if (this.hiddenField) {
39118             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
39119         }
39120         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
39121         this.value = this.parseDate(date);
39122     },
39123
39124     // private
39125     parseDate : function(value){
39126         if(!value || value instanceof Date){
39127             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
39128             return value;
39129         }
39130         var v = Date.parseDate(value, this.format);
39131         if (!v && this.useIso) {
39132             v = Date.parseDate(value, 'Y-m-d');
39133         }
39134         if (v) {
39135             // 
39136             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
39137         }
39138         
39139         
39140         if(!v && this.altFormats){
39141             if(!this.altFormatsArray){
39142                 this.altFormatsArray = this.altFormats.split("|");
39143             }
39144             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
39145                 v = Date.parseDate(value, this.altFormatsArray[i]);
39146             }
39147         }
39148         return v;
39149     },
39150
39151     // private
39152     formatDate : function(date, fmt){
39153         return (!date || !(date instanceof Date)) ?
39154                date : date.dateFormat(fmt || this.format);
39155     },
39156
39157     // private
39158     menuListeners : {
39159         select: function(m, d){
39160             this.setValue(d);
39161             this.fireEvent('select', this, d);
39162         },
39163         show : function(){ // retain focus styling
39164             this.onFocus();
39165         },
39166         hide : function(){
39167             this.focus.defer(10, this);
39168             var ml = this.menuListeners;
39169             this.menu.un("select", ml.select,  this);
39170             this.menu.un("show", ml.show,  this);
39171             this.menu.un("hide", ml.hide,  this);
39172         }
39173     },
39174     // private
39175     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
39176     onTriggerClick : function(){
39177         if(this.disabled){
39178             return;
39179         }
39180         if(this.menu == null){
39181             this.menu = new Roo.menu.DateMenu();
39182            
39183         }
39184         
39185         Roo.apply(this.menu.picker,  {
39186             
39187             showClear: this.allowBlank,
39188             minDate : this.minValue,
39189             maxDate : this.maxValue,
39190             disabledDatesRE : this.ddMatch,
39191             disabledDatesText : this.disabledDatesText,
39192             
39193             format : this.useIso ? 'Y-m-d' : this.format,
39194             minText : String.format(this.minText, this.formatDate(this.minValue)),
39195             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39196             
39197         });
39198          this.menu.on(Roo.apply({}, this.menuListeners, {
39199             scope:this
39200         }));
39201        
39202         
39203         var m = this.menu;
39204         var p = m.picker;
39205         
39206         // hide month picker get's called when we called by 'before hide';
39207         
39208         var ignorehide = true;
39209         p.hideMonthPicker  = function(disableAnim){
39210             if (ignorehide) {
39211                 return;
39212             }
39213              if(this.monthPicker){
39214                 Roo.log("hideMonthPicker called");
39215                 if(disableAnim === true){
39216                     this.monthPicker.hide();
39217                 }else{
39218                     this.monthPicker.slideOut('t', {duration:.2});
39219                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39220                     p.fireEvent("select", this, this.value);
39221                     m.hide();
39222                 }
39223             }
39224         }
39225         
39226         Roo.log('picker set value');
39227         Roo.log(this.getValue());
39228         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39229         m.show(this.el, 'tl-bl?');
39230         ignorehide  = false;
39231         // this will trigger hideMonthPicker..
39232         
39233         
39234         // hidden the day picker
39235         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39236         
39237         
39238         
39239       
39240         
39241         p.showMonthPicker.defer(100, p);
39242     
39243         
39244        
39245     },
39246
39247     beforeBlur : function(){
39248         var v = this.parseDate(this.getRawValue());
39249         if(v){
39250             this.setValue(v);
39251         }
39252     }
39253
39254     /** @cfg {Boolean} grow @hide */
39255     /** @cfg {Number} growMin @hide */
39256     /** @cfg {Number} growMax @hide */
39257     /**
39258      * @hide
39259      * @method autoSize
39260      */
39261 });/*
39262  * Based on:
39263  * Ext JS Library 1.1.1
39264  * Copyright(c) 2006-2007, Ext JS, LLC.
39265  *
39266  * Originally Released Under LGPL - original licence link has changed is not relivant.
39267  *
39268  * Fork - LGPL
39269  * <script type="text/javascript">
39270  */
39271  
39272
39273 /**
39274  * @class Roo.form.ComboBox
39275  * @extends Roo.form.TriggerField
39276  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39277  * @constructor
39278  * Create a new ComboBox.
39279  * @param {Object} config Configuration options
39280  */
39281 Roo.form.ComboBox = function(config){
39282     Roo.form.ComboBox.superclass.constructor.call(this, config);
39283     this.addEvents({
39284         /**
39285          * @event expand
39286          * Fires when the dropdown list is expanded
39287              * @param {Roo.form.ComboBox} combo This combo box
39288              */
39289         'expand' : true,
39290         /**
39291          * @event collapse
39292          * Fires when the dropdown list is collapsed
39293              * @param {Roo.form.ComboBox} combo This combo box
39294              */
39295         'collapse' : true,
39296         /**
39297          * @event beforeselect
39298          * Fires before a list item is selected. Return false to cancel the selection.
39299              * @param {Roo.form.ComboBox} combo This combo box
39300              * @param {Roo.data.Record} record The data record returned from the underlying store
39301              * @param {Number} index The index of the selected item in the dropdown list
39302              */
39303         'beforeselect' : true,
39304         /**
39305          * @event select
39306          * Fires when a list item is selected
39307              * @param {Roo.form.ComboBox} combo This combo box
39308              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39309              * @param {Number} index The index of the selected item in the dropdown list
39310              */
39311         'select' : true,
39312         /**
39313          * @event beforequery
39314          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39315          * The event object passed has these properties:
39316              * @param {Roo.form.ComboBox} combo This combo box
39317              * @param {String} query The query
39318              * @param {Boolean} forceAll true to force "all" query
39319              * @param {Boolean} cancel true to cancel the query
39320              * @param {Object} e The query event object
39321              */
39322         'beforequery': true,
39323          /**
39324          * @event add
39325          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39326              * @param {Roo.form.ComboBox} combo This combo box
39327              */
39328         'add' : true,
39329         /**
39330          * @event edit
39331          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39332              * @param {Roo.form.ComboBox} combo This combo box
39333              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39334              */
39335         'edit' : true
39336         
39337         
39338     });
39339     if(this.transform){
39340         this.allowDomMove = false;
39341         var s = Roo.getDom(this.transform);
39342         if(!this.hiddenName){
39343             this.hiddenName = s.name;
39344         }
39345         if(!this.store){
39346             this.mode = 'local';
39347             var d = [], opts = s.options;
39348             for(var i = 0, len = opts.length;i < len; i++){
39349                 var o = opts[i];
39350                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39351                 if(o.selected) {
39352                     this.value = value;
39353                 }
39354                 d.push([value, o.text]);
39355             }
39356             this.store = new Roo.data.SimpleStore({
39357                 'id': 0,
39358                 fields: ['value', 'text'],
39359                 data : d
39360             });
39361             this.valueField = 'value';
39362             this.displayField = 'text';
39363         }
39364         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39365         if(!this.lazyRender){
39366             this.target = true;
39367             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39368             s.parentNode.removeChild(s); // remove it
39369             this.render(this.el.parentNode);
39370         }else{
39371             s.parentNode.removeChild(s); // remove it
39372         }
39373
39374     }
39375     if (this.store) {
39376         this.store = Roo.factory(this.store, Roo.data);
39377     }
39378     
39379     this.selectedIndex = -1;
39380     if(this.mode == 'local'){
39381         if(config.queryDelay === undefined){
39382             this.queryDelay = 10;
39383         }
39384         if(config.minChars === undefined){
39385             this.minChars = 0;
39386         }
39387     }
39388 };
39389
39390 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39391     /**
39392      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39393      */
39394     /**
39395      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39396      * rendering into an Roo.Editor, defaults to false)
39397      */
39398     /**
39399      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39400      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39401      */
39402     /**
39403      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39404      */
39405     /**
39406      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39407      * the dropdown list (defaults to undefined, with no header element)
39408      */
39409
39410      /**
39411      * @cfg {String/Roo.Template} tpl The template to use to render the output
39412      */
39413      
39414     // private
39415     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39416     /**
39417      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39418      */
39419     listWidth: undefined,
39420     /**
39421      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39422      * mode = 'remote' or 'text' if mode = 'local')
39423      */
39424     displayField: undefined,
39425     /**
39426      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39427      * mode = 'remote' or 'value' if mode = 'local'). 
39428      * Note: use of a valueField requires the user make a selection
39429      * in order for a value to be mapped.
39430      */
39431     valueField: undefined,
39432     
39433     
39434     /**
39435      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39436      * field's data value (defaults to the underlying DOM element's name)
39437      */
39438     hiddenName: undefined,
39439     /**
39440      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39441      */
39442     listClass: '',
39443     /**
39444      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39445      */
39446     selectedClass: 'x-combo-selected',
39447     /**
39448      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39449      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39450      * which displays a downward arrow icon).
39451      */
39452     triggerClass : 'x-form-arrow-trigger',
39453     /**
39454      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39455      */
39456     shadow:'sides',
39457     /**
39458      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39459      * anchor positions (defaults to 'tl-bl')
39460      */
39461     listAlign: 'tl-bl?',
39462     /**
39463      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39464      */
39465     maxHeight: 300,
39466     /**
39467      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39468      * query specified by the allQuery config option (defaults to 'query')
39469      */
39470     triggerAction: 'query',
39471     /**
39472      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39473      * (defaults to 4, does not apply if editable = false)
39474      */
39475     minChars : 4,
39476     /**
39477      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39478      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39479      */
39480     typeAhead: false,
39481     /**
39482      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39483      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39484      */
39485     queryDelay: 500,
39486     /**
39487      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39488      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39489      */
39490     pageSize: 0,
39491     /**
39492      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39493      * when editable = true (defaults to false)
39494      */
39495     selectOnFocus:false,
39496     /**
39497      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39498      */
39499     queryParam: 'query',
39500     /**
39501      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39502      * when mode = 'remote' (defaults to 'Loading...')
39503      */
39504     loadingText: 'Loading...',
39505     /**
39506      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39507      */
39508     resizable: false,
39509     /**
39510      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39511      */
39512     handleHeight : 8,
39513     /**
39514      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39515      * traditional select (defaults to true)
39516      */
39517     editable: true,
39518     /**
39519      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39520      */
39521     allQuery: '',
39522     /**
39523      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39524      */
39525     mode: 'remote',
39526     /**
39527      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39528      * listWidth has a higher value)
39529      */
39530     minListWidth : 70,
39531     /**
39532      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39533      * allow the user to set arbitrary text into the field (defaults to false)
39534      */
39535     forceSelection:false,
39536     /**
39537      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39538      * if typeAhead = true (defaults to 250)
39539      */
39540     typeAheadDelay : 250,
39541     /**
39542      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39543      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39544      */
39545     valueNotFoundText : undefined,
39546     /**
39547      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39548      */
39549     blockFocus : false,
39550     
39551     /**
39552      * @cfg {Boolean} disableClear Disable showing of clear button.
39553      */
39554     disableClear : false,
39555     /**
39556      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39557      */
39558     alwaysQuery : false,
39559     
39560     //private
39561     addicon : false,
39562     editicon: false,
39563     
39564     // element that contains real text value.. (when hidden is used..)
39565      
39566     // private
39567     onRender : function(ct, position){
39568         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39569         if(this.hiddenName){
39570             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39571                     'before', true);
39572             this.hiddenField.value =
39573                 this.hiddenValue !== undefined ? this.hiddenValue :
39574                 this.value !== undefined ? this.value : '';
39575
39576             // prevent input submission
39577             this.el.dom.removeAttribute('name');
39578              
39579              
39580         }
39581         if(Roo.isGecko){
39582             this.el.dom.setAttribute('autocomplete', 'off');
39583         }
39584
39585         var cls = 'x-combo-list';
39586
39587         this.list = new Roo.Layer({
39588             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39589         });
39590
39591         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39592         this.list.setWidth(lw);
39593         this.list.swallowEvent('mousewheel');
39594         this.assetHeight = 0;
39595
39596         if(this.title){
39597             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39598             this.assetHeight += this.header.getHeight();
39599         }
39600
39601         this.innerList = this.list.createChild({cls:cls+'-inner'});
39602         this.innerList.on('mouseover', this.onViewOver, this);
39603         this.innerList.on('mousemove', this.onViewMove, this);
39604         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39605         
39606         if(this.allowBlank && !this.pageSize && !this.disableClear){
39607             this.footer = this.list.createChild({cls:cls+'-ft'});
39608             this.pageTb = new Roo.Toolbar(this.footer);
39609            
39610         }
39611         if(this.pageSize){
39612             this.footer = this.list.createChild({cls:cls+'-ft'});
39613             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39614                     {pageSize: this.pageSize});
39615             
39616         }
39617         
39618         if (this.pageTb && this.allowBlank && !this.disableClear) {
39619             var _this = this;
39620             this.pageTb.add(new Roo.Toolbar.Fill(), {
39621                 cls: 'x-btn-icon x-btn-clear',
39622                 text: '&#160;',
39623                 handler: function()
39624                 {
39625                     _this.collapse();
39626                     _this.clearValue();
39627                     _this.onSelect(false, -1);
39628                 }
39629             });
39630         }
39631         if (this.footer) {
39632             this.assetHeight += this.footer.getHeight();
39633         }
39634         
39635
39636         if(!this.tpl){
39637             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39638         }
39639
39640         this.view = new Roo.View(this.innerList, this.tpl, {
39641             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39642         });
39643
39644         this.view.on('click', this.onViewClick, this);
39645
39646         this.store.on('beforeload', this.onBeforeLoad, this);
39647         this.store.on('load', this.onLoad, this);
39648         this.store.on('loadexception', this.onLoadException, this);
39649
39650         if(this.resizable){
39651             this.resizer = new Roo.Resizable(this.list,  {
39652                pinned:true, handles:'se'
39653             });
39654             this.resizer.on('resize', function(r, w, h){
39655                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39656                 this.listWidth = w;
39657                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39658                 this.restrictHeight();
39659             }, this);
39660             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39661         }
39662         if(!this.editable){
39663             this.editable = true;
39664             this.setEditable(false);
39665         }  
39666         
39667         
39668         if (typeof(this.events.add.listeners) != 'undefined') {
39669             
39670             this.addicon = this.wrap.createChild(
39671                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39672        
39673             this.addicon.on('click', function(e) {
39674                 this.fireEvent('add', this);
39675             }, this);
39676         }
39677         if (typeof(this.events.edit.listeners) != 'undefined') {
39678             
39679             this.editicon = this.wrap.createChild(
39680                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39681             if (this.addicon) {
39682                 this.editicon.setStyle('margin-left', '40px');
39683             }
39684             this.editicon.on('click', function(e) {
39685                 
39686                 // we fire even  if inothing is selected..
39687                 this.fireEvent('edit', this, this.lastData );
39688                 
39689             }, this);
39690         }
39691         
39692         
39693         
39694     },
39695
39696     // private
39697     initEvents : function(){
39698         Roo.form.ComboBox.superclass.initEvents.call(this);
39699
39700         this.keyNav = new Roo.KeyNav(this.el, {
39701             "up" : function(e){
39702                 this.inKeyMode = true;
39703                 this.selectPrev();
39704             },
39705
39706             "down" : function(e){
39707                 if(!this.isExpanded()){
39708                     this.onTriggerClick();
39709                 }else{
39710                     this.inKeyMode = true;
39711                     this.selectNext();
39712                 }
39713             },
39714
39715             "enter" : function(e){
39716                 this.onViewClick();
39717                 //return true;
39718             },
39719
39720             "esc" : function(e){
39721                 this.collapse();
39722             },
39723
39724             "tab" : function(e){
39725                 this.onViewClick(false);
39726                 this.fireEvent("specialkey", this, e);
39727                 return true;
39728             },
39729
39730             scope : this,
39731
39732             doRelay : function(foo, bar, hname){
39733                 if(hname == 'down' || this.scope.isExpanded()){
39734                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39735                 }
39736                 return true;
39737             },
39738
39739             forceKeyDown: true
39740         });
39741         this.queryDelay = Math.max(this.queryDelay || 10,
39742                 this.mode == 'local' ? 10 : 250);
39743         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39744         if(this.typeAhead){
39745             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39746         }
39747         if(this.editable !== false){
39748             this.el.on("keyup", this.onKeyUp, this);
39749         }
39750         if(this.forceSelection){
39751             this.on('blur', this.doForce, this);
39752         }
39753     },
39754
39755     onDestroy : function(){
39756         if(this.view){
39757             this.view.setStore(null);
39758             this.view.el.removeAllListeners();
39759             this.view.el.remove();
39760             this.view.purgeListeners();
39761         }
39762         if(this.list){
39763             this.list.destroy();
39764         }
39765         if(this.store){
39766             this.store.un('beforeload', this.onBeforeLoad, this);
39767             this.store.un('load', this.onLoad, this);
39768             this.store.un('loadexception', this.onLoadException, this);
39769         }
39770         Roo.form.ComboBox.superclass.onDestroy.call(this);
39771     },
39772
39773     // private
39774     fireKey : function(e){
39775         if(e.isNavKeyPress() && !this.list.isVisible()){
39776             this.fireEvent("specialkey", this, e);
39777         }
39778     },
39779
39780     // private
39781     onResize: function(w, h){
39782         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39783         
39784         if(typeof w != 'number'){
39785             // we do not handle it!?!?
39786             return;
39787         }
39788         var tw = this.trigger.getWidth();
39789         tw += this.addicon ? this.addicon.getWidth() : 0;
39790         tw += this.editicon ? this.editicon.getWidth() : 0;
39791         var x = w - tw;
39792         this.el.setWidth( this.adjustWidth('input', x));
39793             
39794         this.trigger.setStyle('left', x+'px');
39795         
39796         if(this.list && this.listWidth === undefined){
39797             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39798             this.list.setWidth(lw);
39799             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39800         }
39801         
39802     
39803         
39804     },
39805
39806     /**
39807      * Allow or prevent the user from directly editing the field text.  If false is passed,
39808      * the user will only be able to select from the items defined in the dropdown list.  This method
39809      * is the runtime equivalent of setting the 'editable' config option at config time.
39810      * @param {Boolean} value True to allow the user to directly edit the field text
39811      */
39812     setEditable : function(value){
39813         if(value == this.editable){
39814             return;
39815         }
39816         this.editable = value;
39817         if(!value){
39818             this.el.dom.setAttribute('readOnly', true);
39819             this.el.on('mousedown', this.onTriggerClick,  this);
39820             this.el.addClass('x-combo-noedit');
39821         }else{
39822             this.el.dom.setAttribute('readOnly', false);
39823             this.el.un('mousedown', this.onTriggerClick,  this);
39824             this.el.removeClass('x-combo-noedit');
39825         }
39826     },
39827
39828     // private
39829     onBeforeLoad : function(){
39830         if(!this.hasFocus){
39831             return;
39832         }
39833         this.innerList.update(this.loadingText ?
39834                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39835         this.restrictHeight();
39836         this.selectedIndex = -1;
39837     },
39838
39839     // private
39840     onLoad : function(){
39841         if(!this.hasFocus){
39842             return;
39843         }
39844         if(this.store.getCount() > 0){
39845             this.expand();
39846             this.restrictHeight();
39847             if(this.lastQuery == this.allQuery){
39848                 if(this.editable){
39849                     this.el.dom.select();
39850                 }
39851                 if(!this.selectByValue(this.value, true)){
39852                     this.select(0, true);
39853                 }
39854             }else{
39855                 this.selectNext();
39856                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39857                     this.taTask.delay(this.typeAheadDelay);
39858                 }
39859             }
39860         }else{
39861             this.onEmptyResults();
39862         }
39863         //this.el.focus();
39864     },
39865     // private
39866     onLoadException : function()
39867     {
39868         this.collapse();
39869         Roo.log(this.store.reader.jsonData);
39870         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39871             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39872         }
39873         
39874         
39875     },
39876     // private
39877     onTypeAhead : function(){
39878         if(this.store.getCount() > 0){
39879             var r = this.store.getAt(0);
39880             var newValue = r.data[this.displayField];
39881             var len = newValue.length;
39882             var selStart = this.getRawValue().length;
39883             if(selStart != len){
39884                 this.setRawValue(newValue);
39885                 this.selectText(selStart, newValue.length);
39886             }
39887         }
39888     },
39889
39890     // private
39891     onSelect : function(record, index){
39892         if(this.fireEvent('beforeselect', this, record, index) !== false){
39893             this.setFromData(index > -1 ? record.data : false);
39894             this.collapse();
39895             this.fireEvent('select', this, record, index);
39896         }
39897     },
39898
39899     /**
39900      * Returns the currently selected field value or empty string if no value is set.
39901      * @return {String} value The selected value
39902      */
39903     getValue : function(){
39904         if(this.valueField){
39905             return typeof this.value != 'undefined' ? this.value : '';
39906         }
39907         return Roo.form.ComboBox.superclass.getValue.call(this);
39908     },
39909
39910     /**
39911      * Clears any text/value currently set in the field
39912      */
39913     clearValue : function(){
39914         if(this.hiddenField){
39915             this.hiddenField.value = '';
39916         }
39917         this.value = '';
39918         this.setRawValue('');
39919         this.lastSelectionText = '';
39920         
39921     },
39922
39923     /**
39924      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39925      * will be displayed in the field.  If the value does not match the data value of an existing item,
39926      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39927      * Otherwise the field will be blank (although the value will still be set).
39928      * @param {String} value The value to match
39929      */
39930     setValue : function(v){
39931         var text = v;
39932         if(this.valueField){
39933             var r = this.findRecord(this.valueField, v);
39934             if(r){
39935                 text = r.data[this.displayField];
39936             }else if(this.valueNotFoundText !== undefined){
39937                 text = this.valueNotFoundText;
39938             }
39939         }
39940         this.lastSelectionText = text;
39941         if(this.hiddenField){
39942             this.hiddenField.value = v;
39943         }
39944         Roo.form.ComboBox.superclass.setValue.call(this, text);
39945         this.value = v;
39946     },
39947     /**
39948      * @property {Object} the last set data for the element
39949      */
39950     
39951     lastData : false,
39952     /**
39953      * Sets the value of the field based on a object which is related to the record format for the store.
39954      * @param {Object} value the value to set as. or false on reset?
39955      */
39956     setFromData : function(o){
39957         var dv = ''; // display value
39958         var vv = ''; // value value..
39959         this.lastData = o;
39960         if (this.displayField) {
39961             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39962         } else {
39963             // this is an error condition!!!
39964             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39965         }
39966         
39967         if(this.valueField){
39968             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39969         }
39970         if(this.hiddenField){
39971             this.hiddenField.value = vv;
39972             
39973             this.lastSelectionText = dv;
39974             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39975             this.value = vv;
39976             return;
39977         }
39978         // no hidden field.. - we store the value in 'value', but still display
39979         // display field!!!!
39980         this.lastSelectionText = dv;
39981         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39982         this.value = vv;
39983         
39984         
39985     },
39986     // private
39987     reset : function(){
39988         // overridden so that last data is reset..
39989         this.setValue(this.resetValue);
39990         this.clearInvalid();
39991         this.lastData = false;
39992         if (this.view) {
39993             this.view.clearSelections();
39994         }
39995     },
39996     // private
39997     findRecord : function(prop, value){
39998         var record;
39999         if(this.store.getCount() > 0){
40000             this.store.each(function(r){
40001                 if(r.data[prop] == value){
40002                     record = r;
40003                     return false;
40004                 }
40005                 return true;
40006             });
40007         }
40008         return record;
40009     },
40010     
40011     getName: function()
40012     {
40013         // returns hidden if it's set..
40014         if (!this.rendered) {return ''};
40015         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40016         
40017     },
40018     // private
40019     onViewMove : function(e, t){
40020         this.inKeyMode = false;
40021     },
40022
40023     // private
40024     onViewOver : function(e, t){
40025         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
40026             return;
40027         }
40028         var item = this.view.findItemFromChild(t);
40029         if(item){
40030             var index = this.view.indexOf(item);
40031             this.select(index, false);
40032         }
40033     },
40034
40035     // private
40036     onViewClick : function(doFocus)
40037     {
40038         var index = this.view.getSelectedIndexes()[0];
40039         var r = this.store.getAt(index);
40040         if(r){
40041             this.onSelect(r, index);
40042         }
40043         if(doFocus !== false && !this.blockFocus){
40044             this.el.focus();
40045         }
40046     },
40047
40048     // private
40049     restrictHeight : function(){
40050         this.innerList.dom.style.height = '';
40051         var inner = this.innerList.dom;
40052         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
40053         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
40054         this.list.beginUpdate();
40055         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
40056         this.list.alignTo(this.el, this.listAlign);
40057         this.list.endUpdate();
40058     },
40059
40060     // private
40061     onEmptyResults : function(){
40062         this.collapse();
40063     },
40064
40065     /**
40066      * Returns true if the dropdown list is expanded, else false.
40067      */
40068     isExpanded : function(){
40069         return this.list.isVisible();
40070     },
40071
40072     /**
40073      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
40074      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40075      * @param {String} value The data value of the item to select
40076      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40077      * selected item if it is not currently in view (defaults to true)
40078      * @return {Boolean} True if the value matched an item in the list, else false
40079      */
40080     selectByValue : function(v, scrollIntoView){
40081         if(v !== undefined && v !== null){
40082             var r = this.findRecord(this.valueField || this.displayField, v);
40083             if(r){
40084                 this.select(this.store.indexOf(r), scrollIntoView);
40085                 return true;
40086             }
40087         }
40088         return false;
40089     },
40090
40091     /**
40092      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
40093      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40094      * @param {Number} index The zero-based index of the list item to select
40095      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40096      * selected item if it is not currently in view (defaults to true)
40097      */
40098     select : function(index, scrollIntoView){
40099         this.selectedIndex = index;
40100         this.view.select(index);
40101         if(scrollIntoView !== false){
40102             var el = this.view.getNode(index);
40103             if(el){
40104                 this.innerList.scrollChildIntoView(el, false);
40105             }
40106         }
40107     },
40108
40109     // private
40110     selectNext : function(){
40111         var ct = this.store.getCount();
40112         if(ct > 0){
40113             if(this.selectedIndex == -1){
40114                 this.select(0);
40115             }else if(this.selectedIndex < ct-1){
40116                 this.select(this.selectedIndex+1);
40117             }
40118         }
40119     },
40120
40121     // private
40122     selectPrev : function(){
40123         var ct = this.store.getCount();
40124         if(ct > 0){
40125             if(this.selectedIndex == -1){
40126                 this.select(0);
40127             }else if(this.selectedIndex != 0){
40128                 this.select(this.selectedIndex-1);
40129             }
40130         }
40131     },
40132
40133     // private
40134     onKeyUp : function(e){
40135         if(this.editable !== false && !e.isSpecialKey()){
40136             this.lastKey = e.getKey();
40137             this.dqTask.delay(this.queryDelay);
40138         }
40139     },
40140
40141     // private
40142     validateBlur : function(){
40143         return !this.list || !this.list.isVisible();   
40144     },
40145
40146     // private
40147     initQuery : function(){
40148         this.doQuery(this.getRawValue());
40149     },
40150
40151     // private
40152     doForce : function(){
40153         if(this.el.dom.value.length > 0){
40154             this.el.dom.value =
40155                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
40156              
40157         }
40158     },
40159
40160     /**
40161      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
40162      * query allowing the query action to be canceled if needed.
40163      * @param {String} query The SQL query to execute
40164      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
40165      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
40166      * saved in the current store (defaults to false)
40167      */
40168     doQuery : function(q, forceAll){
40169         if(q === undefined || q === null){
40170             q = '';
40171         }
40172         var qe = {
40173             query: q,
40174             forceAll: forceAll,
40175             combo: this,
40176             cancel:false
40177         };
40178         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
40179             return false;
40180         }
40181         q = qe.query;
40182         forceAll = qe.forceAll;
40183         if(forceAll === true || (q.length >= this.minChars)){
40184             if(this.lastQuery != q || this.alwaysQuery){
40185                 this.lastQuery = q;
40186                 if(this.mode == 'local'){
40187                     this.selectedIndex = -1;
40188                     if(forceAll){
40189                         this.store.clearFilter();
40190                     }else{
40191                         this.store.filter(this.displayField, q);
40192                     }
40193                     this.onLoad();
40194                 }else{
40195                     this.store.baseParams[this.queryParam] = q;
40196                     this.store.load({
40197                         params: this.getParams(q)
40198                     });
40199                     this.expand();
40200                 }
40201             }else{
40202                 this.selectedIndex = -1;
40203                 this.onLoad();   
40204             }
40205         }
40206     },
40207
40208     // private
40209     getParams : function(q){
40210         var p = {};
40211         //p[this.queryParam] = q;
40212         if(this.pageSize){
40213             p.start = 0;
40214             p.limit = this.pageSize;
40215         }
40216         return p;
40217     },
40218
40219     /**
40220      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40221      */
40222     collapse : function(){
40223         if(!this.isExpanded()){
40224             return;
40225         }
40226         this.list.hide();
40227         Roo.get(document).un('mousedown', this.collapseIf, this);
40228         Roo.get(document).un('mousewheel', this.collapseIf, this);
40229         if (!this.editable) {
40230             Roo.get(document).un('keydown', this.listKeyPress, this);
40231         }
40232         this.fireEvent('collapse', this);
40233     },
40234
40235     // private
40236     collapseIf : function(e){
40237         if(!e.within(this.wrap) && !e.within(this.list)){
40238             this.collapse();
40239         }
40240     },
40241
40242     /**
40243      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40244      */
40245     expand : function(){
40246         if(this.isExpanded() || !this.hasFocus){
40247             return;
40248         }
40249         this.list.alignTo(this.el, this.listAlign);
40250         this.list.show();
40251         Roo.get(document).on('mousedown', this.collapseIf, this);
40252         Roo.get(document).on('mousewheel', this.collapseIf, this);
40253         if (!this.editable) {
40254             Roo.get(document).on('keydown', this.listKeyPress, this);
40255         }
40256         
40257         this.fireEvent('expand', this);
40258     },
40259
40260     // private
40261     // Implements the default empty TriggerField.onTriggerClick function
40262     onTriggerClick : function(){
40263         if(this.disabled){
40264             return;
40265         }
40266         if(this.isExpanded()){
40267             this.collapse();
40268             if (!this.blockFocus) {
40269                 this.el.focus();
40270             }
40271             
40272         }else {
40273             this.hasFocus = true;
40274             if(this.triggerAction == 'all') {
40275                 this.doQuery(this.allQuery, true);
40276             } else {
40277                 this.doQuery(this.getRawValue());
40278             }
40279             if (!this.blockFocus) {
40280                 this.el.focus();
40281             }
40282         }
40283     },
40284     listKeyPress : function(e)
40285     {
40286         //Roo.log('listkeypress');
40287         // scroll to first matching element based on key pres..
40288         if (e.isSpecialKey()) {
40289             return false;
40290         }
40291         var k = String.fromCharCode(e.getKey()).toUpperCase();
40292         //Roo.log(k);
40293         var match  = false;
40294         var csel = this.view.getSelectedNodes();
40295         var cselitem = false;
40296         if (csel.length) {
40297             var ix = this.view.indexOf(csel[0]);
40298             cselitem  = this.store.getAt(ix);
40299             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40300                 cselitem = false;
40301             }
40302             
40303         }
40304         
40305         this.store.each(function(v) { 
40306             if (cselitem) {
40307                 // start at existing selection.
40308                 if (cselitem.id == v.id) {
40309                     cselitem = false;
40310                 }
40311                 return;
40312             }
40313                 
40314             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40315                 match = this.store.indexOf(v);
40316                 return false;
40317             }
40318         }, this);
40319         
40320         if (match === false) {
40321             return true; // no more action?
40322         }
40323         // scroll to?
40324         this.view.select(match);
40325         var sn = Roo.get(this.view.getSelectedNodes()[0])
40326         sn.scrollIntoView(sn.dom.parentNode, false);
40327     }
40328
40329     /** 
40330     * @cfg {Boolean} grow 
40331     * @hide 
40332     */
40333     /** 
40334     * @cfg {Number} growMin 
40335     * @hide 
40336     */
40337     /** 
40338     * @cfg {Number} growMax 
40339     * @hide 
40340     */
40341     /**
40342      * @hide
40343      * @method autoSize
40344      */
40345 });/*
40346  * Copyright(c) 2010-2012, Roo J Solutions Limited
40347  *
40348  * Licence LGPL
40349  *
40350  */
40351
40352 /**
40353  * @class Roo.form.ComboBoxArray
40354  * @extends Roo.form.TextField
40355  * A facebook style adder... for lists of email / people / countries  etc...
40356  * pick multiple items from a combo box, and shows each one.
40357  *
40358  *  Fred [x]  Brian [x]  [Pick another |v]
40359  *
40360  *
40361  *  For this to work: it needs various extra information
40362  *    - normal combo problay has
40363  *      name, hiddenName
40364  *    + displayField, valueField
40365  *
40366  *    For our purpose...
40367  *
40368  *
40369  *   If we change from 'extends' to wrapping...
40370  *   
40371  *  
40372  *
40373  
40374  
40375  * @constructor
40376  * Create a new ComboBoxArray.
40377  * @param {Object} config Configuration options
40378  */
40379  
40380
40381 Roo.form.ComboBoxArray = function(config)
40382 {
40383     this.addEvents({
40384         /**
40385          * @event beforeremove
40386          * Fires before remove the value from the list
40387              * @param {Roo.form.ComboBoxArray} _self This combo box array
40388              * @param {Roo.form.ComboBoxArray.Item} item removed item
40389              */
40390         'beforeremove' : true,
40391         /**
40392          * @event remove
40393          * Fires when remove the value from the list
40394              * @param {Roo.form.ComboBoxArray} _self This combo box array
40395              * @param {Roo.form.ComboBoxArray.Item} item removed item
40396              */
40397         'remove' : true
40398         
40399         
40400     });
40401     
40402     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40403     
40404     this.items = new Roo.util.MixedCollection(false);
40405     
40406     // construct the child combo...
40407     
40408     
40409     
40410     
40411    
40412     
40413 }
40414
40415  
40416 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40417
40418     /**
40419      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40420      */
40421     
40422     lastData : false,
40423     
40424     // behavies liek a hiddne field
40425     inputType:      'hidden',
40426     /**
40427      * @cfg {Number} width The width of the box that displays the selected element
40428      */ 
40429     width:          300,
40430
40431     
40432     
40433     /**
40434      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40435      */
40436     name : false,
40437     /**
40438      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40439      */
40440     hiddenName : false,
40441     
40442     
40443     // private the array of items that are displayed..
40444     items  : false,
40445     // private - the hidden field el.
40446     hiddenEl : false,
40447     // private - the filed el..
40448     el : false,
40449     
40450     //validateValue : function() { return true; }, // all values are ok!
40451     //onAddClick: function() { },
40452     
40453     onRender : function(ct, position) 
40454     {
40455         
40456         // create the standard hidden element
40457         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40458         
40459         
40460         // give fake names to child combo;
40461         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40462         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40463         
40464         this.combo = Roo.factory(this.combo, Roo.form);
40465         this.combo.onRender(ct, position);
40466         if (typeof(this.combo.width) != 'undefined') {
40467             this.combo.onResize(this.combo.width,0);
40468         }
40469         
40470         this.combo.initEvents();
40471         
40472         // assigned so form know we need to do this..
40473         this.store          = this.combo.store;
40474         this.valueField     = this.combo.valueField;
40475         this.displayField   = this.combo.displayField ;
40476         
40477         
40478         this.combo.wrap.addClass('x-cbarray-grp');
40479         
40480         var cbwrap = this.combo.wrap.createChild(
40481             {tag: 'div', cls: 'x-cbarray-cb'},
40482             this.combo.el.dom
40483         );
40484         
40485              
40486         this.hiddenEl = this.combo.wrap.createChild({
40487             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40488         });
40489         this.el = this.combo.wrap.createChild({
40490             tag: 'input',  type:'hidden' , name: this.name, value : ''
40491         });
40492          //   this.el.dom.removeAttribute("name");
40493         
40494         
40495         this.outerWrap = this.combo.wrap;
40496         this.wrap = cbwrap;
40497         
40498         this.outerWrap.setWidth(this.width);
40499         this.outerWrap.dom.removeChild(this.el.dom);
40500         
40501         this.wrap.dom.appendChild(this.el.dom);
40502         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40503         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40504         
40505         this.combo.trigger.setStyle('position','relative');
40506         this.combo.trigger.setStyle('left', '0px');
40507         this.combo.trigger.setStyle('top', '2px');
40508         
40509         this.combo.el.setStyle('vertical-align', 'text-bottom');
40510         
40511         //this.trigger.setStyle('vertical-align', 'top');
40512         
40513         // this should use the code from combo really... on('add' ....)
40514         if (this.adder) {
40515             
40516         
40517             this.adder = this.outerWrap.createChild(
40518                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40519             var _t = this;
40520             this.adder.on('click', function(e) {
40521                 _t.fireEvent('adderclick', this, e);
40522             }, _t);
40523         }
40524         //var _t = this;
40525         //this.adder.on('click', this.onAddClick, _t);
40526         
40527         
40528         this.combo.on('select', function(cb, rec, ix) {
40529             this.addItem(rec.data);
40530             
40531             cb.setValue('');
40532             cb.el.dom.value = '';
40533             //cb.lastData = rec.data;
40534             // add to list
40535             
40536         }, this);
40537         
40538         
40539     },
40540     
40541     
40542     getName: function()
40543     {
40544         // returns hidden if it's set..
40545         if (!this.rendered) {return ''};
40546         return  this.hiddenName ? this.hiddenName : this.name;
40547         
40548     },
40549     
40550     
40551     onResize: function(w, h){
40552         
40553         return;
40554         // not sure if this is needed..
40555         //this.combo.onResize(w,h);
40556         
40557         if(typeof w != 'number'){
40558             // we do not handle it!?!?
40559             return;
40560         }
40561         var tw = this.combo.trigger.getWidth();
40562         tw += this.addicon ? this.addicon.getWidth() : 0;
40563         tw += this.editicon ? this.editicon.getWidth() : 0;
40564         var x = w - tw;
40565         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40566             
40567         this.combo.trigger.setStyle('left', '0px');
40568         
40569         if(this.list && this.listWidth === undefined){
40570             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40571             this.list.setWidth(lw);
40572             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40573         }
40574         
40575     
40576         
40577     },
40578     
40579     addItem: function(rec)
40580     {
40581         var valueField = this.combo.valueField;
40582         var displayField = this.combo.displayField;
40583         if (this.items.indexOfKey(rec[valueField]) > -1) {
40584             //console.log("GOT " + rec.data.id);
40585             return;
40586         }
40587         
40588         var x = new Roo.form.ComboBoxArray.Item({
40589             //id : rec[this.idField],
40590             data : rec,
40591             displayField : displayField ,
40592             tipField : displayField ,
40593             cb : this
40594         });
40595         // use the 
40596         this.items.add(rec[valueField],x);
40597         // add it before the element..
40598         this.updateHiddenEl();
40599         x.render(this.outerWrap, this.wrap.dom);
40600         // add the image handler..
40601     },
40602     
40603     updateHiddenEl : function()
40604     {
40605         this.validate();
40606         if (!this.hiddenEl) {
40607             return;
40608         }
40609         var ar = [];
40610         var idField = this.combo.valueField;
40611         
40612         this.items.each(function(f) {
40613             ar.push(f.data[idField]);
40614            
40615         });
40616         this.hiddenEl.dom.value = ar.join(',');
40617         this.validate();
40618     },
40619     
40620     reset : function()
40621     {
40622         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40623         this.items.each(function(f) {
40624            f.remove(); 
40625         });
40626         this.el.dom.value = '';
40627         if (this.hiddenEl) {
40628             this.hiddenEl.dom.value = '';
40629         }
40630         
40631     },
40632     getValue: function()
40633     {
40634         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40635     },
40636     setValue: function(v) // not a valid action - must use addItems..
40637     {
40638          
40639         this.reset();
40640         
40641         
40642         
40643         if (this.store.isLocal && (typeof(v) == 'string')) {
40644             // then we can use the store to find the values..
40645             // comma seperated at present.. this needs to allow JSON based encoding..
40646             this.hiddenEl.value  = v;
40647             var v_ar = [];
40648             Roo.each(v.split(','), function(k) {
40649                 Roo.log("CHECK " + this.valueField + ',' + k);
40650                 var li = this.store.query(this.valueField, k);
40651                 if (!li.length) {
40652                     return;
40653                 }
40654                 var add = {};
40655                 add[this.valueField] = k;
40656                 add[this.displayField] = li.item(0).data[this.displayField];
40657                 
40658                 this.addItem(add);
40659             }, this) 
40660              
40661         }
40662         if (typeof(v) == 'object' ) {
40663             // then let's assume it's an array of objects..
40664             Roo.each(v, function(l) {
40665                 this.addItem(l);
40666             }, this);
40667              
40668         }
40669         
40670         
40671     },
40672     setFromData: function(v)
40673     {
40674         // this recieves an object, if setValues is called.
40675         this.reset();
40676         this.el.dom.value = v[this.displayField];
40677         this.hiddenEl.dom.value = v[this.valueField];
40678         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40679             return;
40680         }
40681         var kv = v[this.valueField];
40682         var dv = v[this.displayField];
40683         kv = typeof(kv) != 'string' ? '' : kv;
40684         dv = typeof(dv) != 'string' ? '' : dv;
40685         
40686         
40687         var keys = kv.split(',');
40688         var display = dv.split(',');
40689         for (var i = 0 ; i < keys.length; i++) {
40690             
40691             add = {};
40692             add[this.valueField] = keys[i];
40693             add[this.displayField] = display[i];
40694             this.addItem(add);
40695         }
40696       
40697         
40698     },
40699     
40700     /**
40701      * Validates the combox array value
40702      * @return {Boolean} True if the value is valid, else false
40703      */
40704     validate : function(){
40705         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40706             this.clearInvalid();
40707             return true;
40708         }
40709         return false;
40710     },
40711     
40712     validateValue : function(value){
40713         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40714         
40715     },
40716     
40717     /*@
40718      * overide
40719      * 
40720      */
40721     isDirty : function() {
40722         if(this.disabled) {
40723             return false;
40724         }
40725         
40726         try {
40727             var d = Roo.decode(String(this.originalValue));
40728         } catch (e) {
40729             return String(this.getValue()) !== String(this.originalValue);
40730         }
40731         
40732         var originalValue = [];
40733         
40734         for (var i = 0; i < d.length; i++){
40735             originalValue.push(d[i][this.valueField]);
40736         }
40737         
40738         return String(this.getValue()) !== String(originalValue.join(','));
40739         
40740     }
40741     
40742 });
40743
40744
40745
40746 /**
40747  * @class Roo.form.ComboBoxArray.Item
40748  * @extends Roo.BoxComponent
40749  * A selected item in the list
40750  *  Fred [x]  Brian [x]  [Pick another |v]
40751  * 
40752  * @constructor
40753  * Create a new item.
40754  * @param {Object} config Configuration options
40755  */
40756  
40757 Roo.form.ComboBoxArray.Item = function(config) {
40758     config.id = Roo.id();
40759     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40760 }
40761
40762 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40763     data : {},
40764     cb: false,
40765     displayField : false,
40766     tipField : false,
40767     
40768     
40769     defaultAutoCreate : {
40770         tag: 'div',
40771         cls: 'x-cbarray-item',
40772         cn : [ 
40773             { tag: 'div' },
40774             {
40775                 tag: 'img',
40776                 width:16,
40777                 height : 16,
40778                 src : Roo.BLANK_IMAGE_URL ,
40779                 align: 'center'
40780             }
40781         ]
40782         
40783     },
40784     
40785  
40786     onRender : function(ct, position)
40787     {
40788         Roo.form.Field.superclass.onRender.call(this, ct, position);
40789         
40790         if(!this.el){
40791             var cfg = this.getAutoCreate();
40792             this.el = ct.createChild(cfg, position);
40793         }
40794         
40795         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40796         
40797         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40798             this.cb.renderer(this.data) :
40799             String.format('{0}',this.data[this.displayField]);
40800         
40801             
40802         this.el.child('div').dom.setAttribute('qtip',
40803                         String.format('{0}',this.data[this.tipField])
40804         );
40805         
40806         this.el.child('img').on('click', this.remove, this);
40807         
40808     },
40809    
40810     remove : function()
40811     {
40812         if(this.cb.disabled){
40813             return;
40814         }
40815         
40816         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
40817             this.cb.items.remove(this);
40818             this.el.child('img').un('click', this.remove, this);
40819             this.el.remove();
40820             this.cb.updateHiddenEl();
40821
40822             this.cb.fireEvent('remove', this.cb, this);
40823         }
40824         
40825     }
40826 });/*
40827  * Based on:
40828  * Ext JS Library 1.1.1
40829  * Copyright(c) 2006-2007, Ext JS, LLC.
40830  *
40831  * Originally Released Under LGPL - original licence link has changed is not relivant.
40832  *
40833  * Fork - LGPL
40834  * <script type="text/javascript">
40835  */
40836 /**
40837  * @class Roo.form.Checkbox
40838  * @extends Roo.form.Field
40839  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40840  * @constructor
40841  * Creates a new Checkbox
40842  * @param {Object} config Configuration options
40843  */
40844 Roo.form.Checkbox = function(config){
40845     Roo.form.Checkbox.superclass.constructor.call(this, config);
40846     this.addEvents({
40847         /**
40848          * @event check
40849          * Fires when the checkbox is checked or unchecked.
40850              * @param {Roo.form.Checkbox} this This checkbox
40851              * @param {Boolean} checked The new checked value
40852              */
40853         check : true
40854     });
40855 };
40856
40857 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40858     /**
40859      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40860      */
40861     focusClass : undefined,
40862     /**
40863      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40864      */
40865     fieldClass: "x-form-field",
40866     /**
40867      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40868      */
40869     checked: false,
40870     /**
40871      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40872      * {tag: "input", type: "checkbox", autocomplete: "off"})
40873      */
40874     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40875     /**
40876      * @cfg {String} boxLabel The text that appears beside the checkbox
40877      */
40878     boxLabel : "",
40879     /**
40880      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40881      */  
40882     inputValue : '1',
40883     /**
40884      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40885      */
40886      valueOff: '0', // value when not checked..
40887
40888     actionMode : 'viewEl', 
40889     //
40890     // private
40891     itemCls : 'x-menu-check-item x-form-item',
40892     groupClass : 'x-menu-group-item',
40893     inputType : 'hidden',
40894     
40895     
40896     inSetChecked: false, // check that we are not calling self...
40897     
40898     inputElement: false, // real input element?
40899     basedOn: false, // ????
40900     
40901     isFormField: true, // not sure where this is needed!!!!
40902
40903     onResize : function(){
40904         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40905         if(!this.boxLabel){
40906             this.el.alignTo(this.wrap, 'c-c');
40907         }
40908     },
40909
40910     initEvents : function(){
40911         Roo.form.Checkbox.superclass.initEvents.call(this);
40912         this.el.on("click", this.onClick,  this);
40913         this.el.on("change", this.onClick,  this);
40914     },
40915
40916
40917     getResizeEl : function(){
40918         return this.wrap;
40919     },
40920
40921     getPositionEl : function(){
40922         return this.wrap;
40923     },
40924
40925     // private
40926     onRender : function(ct, position){
40927         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40928         /*
40929         if(this.inputValue !== undefined){
40930             this.el.dom.value = this.inputValue;
40931         }
40932         */
40933         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40934         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40935         var viewEl = this.wrap.createChild({ 
40936             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40937         this.viewEl = viewEl;   
40938         this.wrap.on('click', this.onClick,  this); 
40939         
40940         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40941         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40942         
40943         
40944         
40945         if(this.boxLabel){
40946             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40947         //    viewEl.on('click', this.onClick,  this); 
40948         }
40949         //if(this.checked){
40950             this.setChecked(this.checked);
40951         //}else{
40952             //this.checked = this.el.dom;
40953         //}
40954
40955     },
40956
40957     // private
40958     initValue : Roo.emptyFn,
40959
40960     /**
40961      * Returns the checked state of the checkbox.
40962      * @return {Boolean} True if checked, else false
40963      */
40964     getValue : function(){
40965         if(this.el){
40966             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40967         }
40968         return this.valueOff;
40969         
40970     },
40971
40972         // private
40973     onClick : function(){ 
40974         if (this.disabled) {
40975             return;
40976         }
40977         this.setChecked(!this.checked);
40978
40979         //if(this.el.dom.checked != this.checked){
40980         //    this.setValue(this.el.dom.checked);
40981        // }
40982     },
40983
40984     /**
40985      * Sets the checked state of the checkbox.
40986      * On is always based on a string comparison between inputValue and the param.
40987      * @param {Boolean/String} value - the value to set 
40988      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40989      */
40990     setValue : function(v,suppressEvent){
40991         
40992         
40993         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40994         //if(this.el && this.el.dom){
40995         //    this.el.dom.checked = this.checked;
40996         //    this.el.dom.defaultChecked = this.checked;
40997         //}
40998         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40999         //this.fireEvent("check", this, this.checked);
41000     },
41001     // private..
41002     setChecked : function(state,suppressEvent)
41003     {
41004         if (this.inSetChecked) {
41005             this.checked = state;
41006             return;
41007         }
41008         
41009     
41010         if(this.wrap){
41011             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
41012         }
41013         this.checked = state;
41014         if(suppressEvent !== true){
41015             this.fireEvent('check', this, state);
41016         }
41017         this.inSetChecked = true;
41018         this.el.dom.value = state ? this.inputValue : this.valueOff;
41019         this.inSetChecked = false;
41020         
41021     },
41022     // handle setting of hidden value by some other method!!?!?
41023     setFromHidden: function()
41024     {
41025         if(!this.el){
41026             return;
41027         }
41028         //console.log("SET FROM HIDDEN");
41029         //alert('setFrom hidden');
41030         this.setValue(this.el.dom.value);
41031     },
41032     
41033     onDestroy : function()
41034     {
41035         if(this.viewEl){
41036             Roo.get(this.viewEl).remove();
41037         }
41038          
41039         Roo.form.Checkbox.superclass.onDestroy.call(this);
41040     }
41041
41042 });/*
41043  * Based on:
41044  * Ext JS Library 1.1.1
41045  * Copyright(c) 2006-2007, Ext JS, LLC.
41046  *
41047  * Originally Released Under LGPL - original licence link has changed is not relivant.
41048  *
41049  * Fork - LGPL
41050  * <script type="text/javascript">
41051  */
41052  
41053 /**
41054  * @class Roo.form.Radio
41055  * @extends Roo.form.Checkbox
41056  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
41057  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
41058  * @constructor
41059  * Creates a new Radio
41060  * @param {Object} config Configuration options
41061  */
41062 Roo.form.Radio = function(){
41063     Roo.form.Radio.superclass.constructor.apply(this, arguments);
41064 };
41065 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
41066     inputType: 'radio',
41067
41068     /**
41069      * If this radio is part of a group, it will return the selected value
41070      * @return {String}
41071      */
41072     getGroupValue : function(){
41073         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
41074     },
41075     
41076     
41077     onRender : function(ct, position){
41078         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
41079         
41080         if(this.inputValue !== undefined){
41081             this.el.dom.value = this.inputValue;
41082         }
41083          
41084         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
41085         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
41086         //var viewEl = this.wrap.createChild({ 
41087         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
41088         //this.viewEl = viewEl;   
41089         //this.wrap.on('click', this.onClick,  this); 
41090         
41091         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
41092         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
41093         
41094         
41095         
41096         if(this.boxLabel){
41097             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
41098         //    viewEl.on('click', this.onClick,  this); 
41099         }
41100          if(this.checked){
41101             this.el.dom.checked =   'checked' ;
41102         }
41103          
41104     } 
41105     
41106     
41107 });//<script type="text/javascript">
41108
41109 /*
41110  * Based  Ext JS Library 1.1.1
41111  * Copyright(c) 2006-2007, Ext JS, LLC.
41112  * LGPL
41113  *
41114  */
41115  
41116 /**
41117  * @class Roo.HtmlEditorCore
41118  * @extends Roo.Component
41119  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
41120  *
41121  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
41122  */
41123
41124 Roo.HtmlEditorCore = function(config){
41125     
41126     
41127     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
41128     
41129     
41130     this.addEvents({
41131         /**
41132          * @event initialize
41133          * Fires when the editor is fully initialized (including the iframe)
41134          * @param {Roo.HtmlEditorCore} this
41135          */
41136         initialize: true,
41137         /**
41138          * @event activate
41139          * Fires when the editor is first receives the focus. Any insertion must wait
41140          * until after this event.
41141          * @param {Roo.HtmlEditorCore} this
41142          */
41143         activate: true,
41144          /**
41145          * @event beforesync
41146          * Fires before the textarea is updated with content from the editor iframe. Return false
41147          * to cancel the sync.
41148          * @param {Roo.HtmlEditorCore} this
41149          * @param {String} html
41150          */
41151         beforesync: true,
41152          /**
41153          * @event beforepush
41154          * Fires before the iframe editor is updated with content from the textarea. Return false
41155          * to cancel the push.
41156          * @param {Roo.HtmlEditorCore} this
41157          * @param {String} html
41158          */
41159         beforepush: true,
41160          /**
41161          * @event sync
41162          * Fires when the textarea is updated with content from the editor iframe.
41163          * @param {Roo.HtmlEditorCore} this
41164          * @param {String} html
41165          */
41166         sync: true,
41167          /**
41168          * @event push
41169          * Fires when the iframe editor is updated with content from the textarea.
41170          * @param {Roo.HtmlEditorCore} this
41171          * @param {String} html
41172          */
41173         push: true,
41174         
41175         /**
41176          * @event editorevent
41177          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41178          * @param {Roo.HtmlEditorCore} this
41179          */
41180         editorevent: true
41181         
41182     });
41183     
41184     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
41185     
41186     // defaults : white / black...
41187     this.applyBlacklists();
41188     
41189     
41190     
41191 };
41192
41193
41194 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41195
41196
41197      /**
41198      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41199      */
41200     
41201     owner : false,
41202     
41203      /**
41204      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41205      *                        Roo.resizable.
41206      */
41207     resizable : false,
41208      /**
41209      * @cfg {Number} height (in pixels)
41210      */   
41211     height: 300,
41212    /**
41213      * @cfg {Number} width (in pixels)
41214      */   
41215     width: 500,
41216     
41217     /**
41218      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41219      * 
41220      */
41221     stylesheets: false,
41222     
41223     // id of frame..
41224     frameId: false,
41225     
41226     // private properties
41227     validationEvent : false,
41228     deferHeight: true,
41229     initialized : false,
41230     activated : false,
41231     sourceEditMode : false,
41232     onFocus : Roo.emptyFn,
41233     iframePad:3,
41234     hideMode:'offsets',
41235     
41236     clearUp: true,
41237     
41238     // blacklist + whitelisted elements..
41239     black: false,
41240     white: false,
41241      
41242     
41243
41244     /**
41245      * Protected method that will not generally be called directly. It
41246      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41247      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41248      */
41249     getDocMarkup : function(){
41250         // body styles..
41251         var st = '';
41252         
41253         // inherit styels from page...?? 
41254         if (this.stylesheets === false) {
41255             
41256             Roo.get(document.head).select('style').each(function(node) {
41257                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41258             });
41259             
41260             Roo.get(document.head).select('link').each(function(node) { 
41261                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41262             });
41263             
41264         } else if (!this.stylesheets.length) {
41265                 // simple..
41266                 st = '<style type="text/css">' +
41267                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41268                    '</style>';
41269         } else { 
41270             
41271         }
41272         
41273         st +=  '<style type="text/css">' +
41274             'IMG { cursor: pointer } ' +
41275         '</style>';
41276
41277         
41278         return '<html><head>' + st  +
41279             //<style type="text/css">' +
41280             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41281             //'</style>' +
41282             ' </head><body class="roo-htmleditor-body"></body></html>';
41283     },
41284
41285     // private
41286     onRender : function(ct, position)
41287     {
41288         var _t = this;
41289         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41290         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41291         
41292         
41293         this.el.dom.style.border = '0 none';
41294         this.el.dom.setAttribute('tabIndex', -1);
41295         this.el.addClass('x-hidden hide');
41296         
41297         
41298         
41299         if(Roo.isIE){ // fix IE 1px bogus margin
41300             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41301         }
41302        
41303         
41304         this.frameId = Roo.id();
41305         
41306          
41307         
41308         var iframe = this.owner.wrap.createChild({
41309             tag: 'iframe',
41310             cls: 'form-control', // bootstrap..
41311             id: this.frameId,
41312             name: this.frameId,
41313             frameBorder : 'no',
41314             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41315         }, this.el
41316         );
41317         
41318         
41319         this.iframe = iframe.dom;
41320
41321          this.assignDocWin();
41322         
41323         this.doc.designMode = 'on';
41324        
41325         this.doc.open();
41326         this.doc.write(this.getDocMarkup());
41327         this.doc.close();
41328
41329         
41330         var task = { // must defer to wait for browser to be ready
41331             run : function(){
41332                 //console.log("run task?" + this.doc.readyState);
41333                 this.assignDocWin();
41334                 if(this.doc.body || this.doc.readyState == 'complete'){
41335                     try {
41336                         this.doc.designMode="on";
41337                     } catch (e) {
41338                         return;
41339                     }
41340                     Roo.TaskMgr.stop(task);
41341                     this.initEditor.defer(10, this);
41342                 }
41343             },
41344             interval : 10,
41345             duration: 10000,
41346             scope: this
41347         };
41348         Roo.TaskMgr.start(task);
41349
41350     },
41351
41352     // private
41353     onResize : function(w, h)
41354     {
41355          Roo.log('resize: ' +w + ',' + h );
41356         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41357         if(!this.iframe){
41358             return;
41359         }
41360         if(typeof w == 'number'){
41361             
41362             this.iframe.style.width = w + 'px';
41363         }
41364         if(typeof h == 'number'){
41365             
41366             this.iframe.style.height = h + 'px';
41367             if(this.doc){
41368                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41369             }
41370         }
41371         
41372     },
41373
41374     /**
41375      * Toggles the editor between standard and source edit mode.
41376      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41377      */
41378     toggleSourceEdit : function(sourceEditMode){
41379         
41380         this.sourceEditMode = sourceEditMode === true;
41381         
41382         if(this.sourceEditMode){
41383  
41384             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41385             
41386         }else{
41387             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41388             //this.iframe.className = '';
41389             this.deferFocus();
41390         }
41391         //this.setSize(this.owner.wrap.getSize());
41392         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41393     },
41394
41395     
41396   
41397
41398     /**
41399      * Protected method that will not generally be called directly. If you need/want
41400      * custom HTML cleanup, this is the method you should override.
41401      * @param {String} html The HTML to be cleaned
41402      * return {String} The cleaned HTML
41403      */
41404     cleanHtml : function(html){
41405         html = String(html);
41406         if(html.length > 5){
41407             if(Roo.isSafari){ // strip safari nonsense
41408                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41409             }
41410         }
41411         if(html == '&nbsp;'){
41412             html = '';
41413         }
41414         return html;
41415     },
41416
41417     /**
41418      * HTML Editor -> Textarea
41419      * Protected method that will not generally be called directly. Syncs the contents
41420      * of the editor iframe with the textarea.
41421      */
41422     syncValue : function(){
41423         if(this.initialized){
41424             var bd = (this.doc.body || this.doc.documentElement);
41425             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41426             var html = bd.innerHTML;
41427             if(Roo.isSafari){
41428                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41429                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41430                 if(m && m[1]){
41431                     html = '<div style="'+m[0]+'">' + html + '</div>';
41432                 }
41433             }
41434             html = this.cleanHtml(html);
41435             // fix up the special chars.. normaly like back quotes in word...
41436             // however we do not want to do this with chinese..
41437             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41438                 var cc = b.charCodeAt();
41439                 if (
41440                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41441                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41442                     (cc >= 0xf900 && cc < 0xfb00 )
41443                 ) {
41444                         return b;
41445                 }
41446                 return "&#"+cc+";" 
41447             });
41448             if(this.owner.fireEvent('beforesync', this, html) !== false){
41449                 this.el.dom.value = html;
41450                 this.owner.fireEvent('sync', this, html);
41451             }
41452         }
41453     },
41454
41455     /**
41456      * Protected method that will not generally be called directly. Pushes the value of the textarea
41457      * into the iframe editor.
41458      */
41459     pushValue : function(){
41460         if(this.initialized){
41461             var v = this.el.dom.value.trim();
41462             
41463 //            if(v.length < 1){
41464 //                v = '&#160;';
41465 //            }
41466             
41467             if(this.owner.fireEvent('beforepush', this, v) !== false){
41468                 var d = (this.doc.body || this.doc.documentElement);
41469                 d.innerHTML = v;
41470                 this.cleanUpPaste();
41471                 this.el.dom.value = d.innerHTML;
41472                 this.owner.fireEvent('push', this, v);
41473             }
41474         }
41475     },
41476
41477     // private
41478     deferFocus : function(){
41479         this.focus.defer(10, this);
41480     },
41481
41482     // doc'ed in Field
41483     focus : function(){
41484         if(this.win && !this.sourceEditMode){
41485             this.win.focus();
41486         }else{
41487             this.el.focus();
41488         }
41489     },
41490     
41491     assignDocWin: function()
41492     {
41493         var iframe = this.iframe;
41494         
41495          if(Roo.isIE){
41496             this.doc = iframe.contentWindow.document;
41497             this.win = iframe.contentWindow;
41498         } else {
41499 //            if (!Roo.get(this.frameId)) {
41500 //                return;
41501 //            }
41502 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41503 //            this.win = Roo.get(this.frameId).dom.contentWindow;
41504             
41505             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
41506                 return;
41507             }
41508             
41509             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41510             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
41511         }
41512     },
41513     
41514     // private
41515     initEditor : function(){
41516         //console.log("INIT EDITOR");
41517         this.assignDocWin();
41518         
41519         
41520         
41521         this.doc.designMode="on";
41522         this.doc.open();
41523         this.doc.write(this.getDocMarkup());
41524         this.doc.close();
41525         
41526         var dbody = (this.doc.body || this.doc.documentElement);
41527         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41528         // this copies styles from the containing element into thsi one..
41529         // not sure why we need all of this..
41530         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41531         
41532         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41533         //ss['background-attachment'] = 'fixed'; // w3c
41534         dbody.bgProperties = 'fixed'; // ie
41535         //Roo.DomHelper.applyStyles(dbody, ss);
41536         Roo.EventManager.on(this.doc, {
41537             //'mousedown': this.onEditorEvent,
41538             'mouseup': this.onEditorEvent,
41539             'dblclick': this.onEditorEvent,
41540             'click': this.onEditorEvent,
41541             'keyup': this.onEditorEvent,
41542             buffer:100,
41543             scope: this
41544         });
41545         if(Roo.isGecko){
41546             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41547         }
41548         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41549             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41550         }
41551         this.initialized = true;
41552
41553         this.owner.fireEvent('initialize', this);
41554         this.pushValue();
41555     },
41556
41557     // private
41558     onDestroy : function(){
41559         
41560         
41561         
41562         if(this.rendered){
41563             
41564             //for (var i =0; i < this.toolbars.length;i++) {
41565             //    // fixme - ask toolbars for heights?
41566             //    this.toolbars[i].onDestroy();
41567            // }
41568             
41569             //this.wrap.dom.innerHTML = '';
41570             //this.wrap.remove();
41571         }
41572     },
41573
41574     // private
41575     onFirstFocus : function(){
41576         
41577         this.assignDocWin();
41578         
41579         
41580         this.activated = true;
41581          
41582     
41583         if(Roo.isGecko){ // prevent silly gecko errors
41584             this.win.focus();
41585             var s = this.win.getSelection();
41586             if(!s.focusNode || s.focusNode.nodeType != 3){
41587                 var r = s.getRangeAt(0);
41588                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41589                 r.collapse(true);
41590                 this.deferFocus();
41591             }
41592             try{
41593                 this.execCmd('useCSS', true);
41594                 this.execCmd('styleWithCSS', false);
41595             }catch(e){}
41596         }
41597         this.owner.fireEvent('activate', this);
41598     },
41599
41600     // private
41601     adjustFont: function(btn){
41602         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41603         //if(Roo.isSafari){ // safari
41604         //    adjust *= 2;
41605        // }
41606         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41607         if(Roo.isSafari){ // safari
41608             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41609             v =  (v < 10) ? 10 : v;
41610             v =  (v > 48) ? 48 : v;
41611             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41612             
41613         }
41614         
41615         
41616         v = Math.max(1, v+adjust);
41617         
41618         this.execCmd('FontSize', v  );
41619     },
41620
41621     onEditorEvent : function(e)
41622     {
41623         this.owner.fireEvent('editorevent', this, e);
41624       //  this.updateToolbar();
41625         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41626     },
41627
41628     insertTag : function(tg)
41629     {
41630         // could be a bit smarter... -> wrap the current selected tRoo..
41631         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41632             
41633             range = this.createRange(this.getSelection());
41634             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41635             wrappingNode.appendChild(range.extractContents());
41636             range.insertNode(wrappingNode);
41637
41638             return;
41639             
41640             
41641             
41642         }
41643         this.execCmd("formatblock",   tg);
41644         
41645     },
41646     
41647     insertText : function(txt)
41648     {
41649         
41650         
41651         var range = this.createRange();
41652         range.deleteContents();
41653                //alert(Sender.getAttribute('label'));
41654                
41655         range.insertNode(this.doc.createTextNode(txt));
41656     } ,
41657     
41658      
41659
41660     /**
41661      * Executes a Midas editor command on the editor document and performs necessary focus and
41662      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41663      * @param {String} cmd The Midas command
41664      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41665      */
41666     relayCmd : function(cmd, value){
41667         this.win.focus();
41668         this.execCmd(cmd, value);
41669         this.owner.fireEvent('editorevent', this);
41670         //this.updateToolbar();
41671         this.owner.deferFocus();
41672     },
41673
41674     /**
41675      * Executes a Midas editor command directly on the editor document.
41676      * For visual commands, you should use {@link #relayCmd} instead.
41677      * <b>This should only be called after the editor is initialized.</b>
41678      * @param {String} cmd The Midas command
41679      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41680      */
41681     execCmd : function(cmd, value){
41682         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41683         this.syncValue();
41684     },
41685  
41686  
41687    
41688     /**
41689      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41690      * to insert tRoo.
41691      * @param {String} text | dom node.. 
41692      */
41693     insertAtCursor : function(text)
41694     {
41695         
41696         
41697         
41698         if(!this.activated){
41699             return;
41700         }
41701         /*
41702         if(Roo.isIE){
41703             this.win.focus();
41704             var r = this.doc.selection.createRange();
41705             if(r){
41706                 r.collapse(true);
41707                 r.pasteHTML(text);
41708                 this.syncValue();
41709                 this.deferFocus();
41710             
41711             }
41712             return;
41713         }
41714         */
41715         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41716             this.win.focus();
41717             
41718             
41719             // from jquery ui (MIT licenced)
41720             var range, node;
41721             var win = this.win;
41722             
41723             if (win.getSelection && win.getSelection().getRangeAt) {
41724                 range = win.getSelection().getRangeAt(0);
41725                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41726                 range.insertNode(node);
41727             } else if (win.document.selection && win.document.selection.createRange) {
41728                 // no firefox support
41729                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41730                 win.document.selection.createRange().pasteHTML(txt);
41731             } else {
41732                 // no firefox support
41733                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41734                 this.execCmd('InsertHTML', txt);
41735             } 
41736             
41737             this.syncValue();
41738             
41739             this.deferFocus();
41740         }
41741     },
41742  // private
41743     mozKeyPress : function(e){
41744         if(e.ctrlKey){
41745             var c = e.getCharCode(), cmd;
41746           
41747             if(c > 0){
41748                 c = String.fromCharCode(c).toLowerCase();
41749                 switch(c){
41750                     case 'b':
41751                         cmd = 'bold';
41752                         break;
41753                     case 'i':
41754                         cmd = 'italic';
41755                         break;
41756                     
41757                     case 'u':
41758                         cmd = 'underline';
41759                         break;
41760                     
41761                     case 'v':
41762                         this.cleanUpPaste.defer(100, this);
41763                         return;
41764                         
41765                 }
41766                 if(cmd){
41767                     this.win.focus();
41768                     this.execCmd(cmd);
41769                     this.deferFocus();
41770                     e.preventDefault();
41771                 }
41772                 
41773             }
41774         }
41775     },
41776
41777     // private
41778     fixKeys : function(){ // load time branching for fastest keydown performance
41779         if(Roo.isIE){
41780             return function(e){
41781                 var k = e.getKey(), r;
41782                 if(k == e.TAB){
41783                     e.stopEvent();
41784                     r = this.doc.selection.createRange();
41785                     if(r){
41786                         r.collapse(true);
41787                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41788                         this.deferFocus();
41789                     }
41790                     return;
41791                 }
41792                 
41793                 if(k == e.ENTER){
41794                     r = this.doc.selection.createRange();
41795                     if(r){
41796                         var target = r.parentElement();
41797                         if(!target || target.tagName.toLowerCase() != 'li'){
41798                             e.stopEvent();
41799                             r.pasteHTML('<br />');
41800                             r.collapse(false);
41801                             r.select();
41802                         }
41803                     }
41804                 }
41805                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41806                     this.cleanUpPaste.defer(100, this);
41807                     return;
41808                 }
41809                 
41810                 
41811             };
41812         }else if(Roo.isOpera){
41813             return function(e){
41814                 var k = e.getKey();
41815                 if(k == e.TAB){
41816                     e.stopEvent();
41817                     this.win.focus();
41818                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41819                     this.deferFocus();
41820                 }
41821                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41822                     this.cleanUpPaste.defer(100, this);
41823                     return;
41824                 }
41825                 
41826             };
41827         }else if(Roo.isSafari){
41828             return function(e){
41829                 var k = e.getKey();
41830                 
41831                 if(k == e.TAB){
41832                     e.stopEvent();
41833                     this.execCmd('InsertText','\t');
41834                     this.deferFocus();
41835                     return;
41836                 }
41837                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41838                     this.cleanUpPaste.defer(100, this);
41839                     return;
41840                 }
41841                 
41842              };
41843         }
41844     }(),
41845     
41846     getAllAncestors: function()
41847     {
41848         var p = this.getSelectedNode();
41849         var a = [];
41850         if (!p) {
41851             a.push(p); // push blank onto stack..
41852             p = this.getParentElement();
41853         }
41854         
41855         
41856         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41857             a.push(p);
41858             p = p.parentNode;
41859         }
41860         a.push(this.doc.body);
41861         return a;
41862     },
41863     lastSel : false,
41864     lastSelNode : false,
41865     
41866     
41867     getSelection : function() 
41868     {
41869         this.assignDocWin();
41870         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41871     },
41872     
41873     getSelectedNode: function() 
41874     {
41875         // this may only work on Gecko!!!
41876         
41877         // should we cache this!!!!
41878         
41879         
41880         
41881          
41882         var range = this.createRange(this.getSelection()).cloneRange();
41883         
41884         if (Roo.isIE) {
41885             var parent = range.parentElement();
41886             while (true) {
41887                 var testRange = range.duplicate();
41888                 testRange.moveToElementText(parent);
41889                 if (testRange.inRange(range)) {
41890                     break;
41891                 }
41892                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41893                     break;
41894                 }
41895                 parent = parent.parentElement;
41896             }
41897             return parent;
41898         }
41899         
41900         // is ancestor a text element.
41901         var ac =  range.commonAncestorContainer;
41902         if (ac.nodeType == 3) {
41903             ac = ac.parentNode;
41904         }
41905         
41906         var ar = ac.childNodes;
41907          
41908         var nodes = [];
41909         var other_nodes = [];
41910         var has_other_nodes = false;
41911         for (var i=0;i<ar.length;i++) {
41912             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41913                 continue;
41914             }
41915             // fullly contained node.
41916             
41917             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41918                 nodes.push(ar[i]);
41919                 continue;
41920             }
41921             
41922             // probably selected..
41923             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41924                 other_nodes.push(ar[i]);
41925                 continue;
41926             }
41927             // outer..
41928             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41929                 continue;
41930             }
41931             
41932             
41933             has_other_nodes = true;
41934         }
41935         if (!nodes.length && other_nodes.length) {
41936             nodes= other_nodes;
41937         }
41938         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41939             return false;
41940         }
41941         
41942         return nodes[0];
41943     },
41944     createRange: function(sel)
41945     {
41946         // this has strange effects when using with 
41947         // top toolbar - not sure if it's a great idea.
41948         //this.editor.contentWindow.focus();
41949         if (typeof sel != "undefined") {
41950             try {
41951                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41952             } catch(e) {
41953                 return this.doc.createRange();
41954             }
41955         } else {
41956             return this.doc.createRange();
41957         }
41958     },
41959     getParentElement: function()
41960     {
41961         
41962         this.assignDocWin();
41963         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41964         
41965         var range = this.createRange(sel);
41966          
41967         try {
41968             var p = range.commonAncestorContainer;
41969             while (p.nodeType == 3) { // text node
41970                 p = p.parentNode;
41971             }
41972             return p;
41973         } catch (e) {
41974             return null;
41975         }
41976     
41977     },
41978     /***
41979      *
41980      * Range intersection.. the hard stuff...
41981      *  '-1' = before
41982      *  '0' = hits..
41983      *  '1' = after.
41984      *         [ -- selected range --- ]
41985      *   [fail]                        [fail]
41986      *
41987      *    basically..
41988      *      if end is before start or  hits it. fail.
41989      *      if start is after end or hits it fail.
41990      *
41991      *   if either hits (but other is outside. - then it's not 
41992      *   
41993      *    
41994      **/
41995     
41996     
41997     // @see http://www.thismuchiknow.co.uk/?p=64.
41998     rangeIntersectsNode : function(range, node)
41999     {
42000         var nodeRange = node.ownerDocument.createRange();
42001         try {
42002             nodeRange.selectNode(node);
42003         } catch (e) {
42004             nodeRange.selectNodeContents(node);
42005         }
42006     
42007         var rangeStartRange = range.cloneRange();
42008         rangeStartRange.collapse(true);
42009     
42010         var rangeEndRange = range.cloneRange();
42011         rangeEndRange.collapse(false);
42012     
42013         var nodeStartRange = nodeRange.cloneRange();
42014         nodeStartRange.collapse(true);
42015     
42016         var nodeEndRange = nodeRange.cloneRange();
42017         nodeEndRange.collapse(false);
42018     
42019         return rangeStartRange.compareBoundaryPoints(
42020                  Range.START_TO_START, nodeEndRange) == -1 &&
42021                rangeEndRange.compareBoundaryPoints(
42022                  Range.START_TO_START, nodeStartRange) == 1;
42023         
42024          
42025     },
42026     rangeCompareNode : function(range, node)
42027     {
42028         var nodeRange = node.ownerDocument.createRange();
42029         try {
42030             nodeRange.selectNode(node);
42031         } catch (e) {
42032             nodeRange.selectNodeContents(node);
42033         }
42034         
42035         
42036         range.collapse(true);
42037     
42038         nodeRange.collapse(true);
42039      
42040         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
42041         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
42042          
42043         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
42044         
42045         var nodeIsBefore   =  ss == 1;
42046         var nodeIsAfter    = ee == -1;
42047         
42048         if (nodeIsBefore && nodeIsAfter)
42049             return 0; // outer
42050         if (!nodeIsBefore && nodeIsAfter)
42051             return 1; //right trailed.
42052         
42053         if (nodeIsBefore && !nodeIsAfter)
42054             return 2;  // left trailed.
42055         // fully contined.
42056         return 3;
42057     },
42058
42059     // private? - in a new class?
42060     cleanUpPaste :  function()
42061     {
42062         // cleans up the whole document..
42063         Roo.log('cleanuppaste');
42064         
42065         this.cleanUpChildren(this.doc.body);
42066         var clean = this.cleanWordChars(this.doc.body.innerHTML);
42067         if (clean != this.doc.body.innerHTML) {
42068             this.doc.body.innerHTML = clean;
42069         }
42070         
42071     },
42072     
42073     cleanWordChars : function(input) {// change the chars to hex code
42074         var he = Roo.HtmlEditorCore;
42075         
42076         var output = input;
42077         Roo.each(he.swapCodes, function(sw) { 
42078             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
42079             
42080             output = output.replace(swapper, sw[1]);
42081         });
42082         
42083         return output;
42084     },
42085     
42086     
42087     cleanUpChildren : function (n)
42088     {
42089         if (!n.childNodes.length) {
42090             return;
42091         }
42092         for (var i = n.childNodes.length-1; i > -1 ; i--) {
42093            this.cleanUpChild(n.childNodes[i]);
42094         }
42095     },
42096     
42097     
42098         
42099     
42100     cleanUpChild : function (node)
42101     {
42102         var ed = this;
42103         //console.log(node);
42104         if (node.nodeName == "#text") {
42105             // clean up silly Windows -- stuff?
42106             return; 
42107         }
42108         if (node.nodeName == "#comment") {
42109             node.parentNode.removeChild(node);
42110             // clean up silly Windows -- stuff?
42111             return; 
42112         }
42113         var lcname = node.tagName.toLowerCase();
42114         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
42115         // whitelist of tags..
42116         
42117         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
42118             // remove node.
42119             node.parentNode.removeChild(node);
42120             return;
42121             
42122         }
42123         
42124         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
42125         
42126         // remove <a name=....> as rendering on yahoo mailer is borked with this.
42127         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
42128         
42129         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
42130         //    remove_keep_children = true;
42131         //}
42132         
42133         if (remove_keep_children) {
42134             this.cleanUpChildren(node);
42135             // inserts everything just before this node...
42136             while (node.childNodes.length) {
42137                 var cn = node.childNodes[0];
42138                 node.removeChild(cn);
42139                 node.parentNode.insertBefore(cn, node);
42140             }
42141             node.parentNode.removeChild(node);
42142             return;
42143         }
42144         
42145         if (!node.attributes || !node.attributes.length) {
42146             this.cleanUpChildren(node);
42147             return;
42148         }
42149         
42150         function cleanAttr(n,v)
42151         {
42152             
42153             if (v.match(/^\./) || v.match(/^\//)) {
42154                 return;
42155             }
42156             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
42157                 return;
42158             }
42159             if (v.match(/^#/)) {
42160                 return;
42161             }
42162 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
42163             node.removeAttribute(n);
42164             
42165         }
42166         
42167         var cwhite = this.cwhite;
42168         var cblack = this.cblack;
42169             
42170         function cleanStyle(n,v)
42171         {
42172             if (v.match(/expression/)) { //XSS?? should we even bother..
42173                 node.removeAttribute(n);
42174                 return;
42175             }
42176             
42177             var parts = v.split(/;/);
42178             var clean = [];
42179             
42180             Roo.each(parts, function(p) {
42181                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42182                 if (!p.length) {
42183                     return true;
42184                 }
42185                 var l = p.split(':').shift().replace(/\s+/g,'');
42186                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42187                 
42188                 if ( cwhite.length && cblack.indexOf(l) > -1) {
42189 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42190                     //node.removeAttribute(n);
42191                     return true;
42192                 }
42193                 //Roo.log()
42194                 // only allow 'c whitelisted system attributes'
42195                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42196 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42197                     //node.removeAttribute(n);
42198                     return true;
42199                 }
42200                 
42201                 
42202                  
42203                 
42204                 clean.push(p);
42205                 return true;
42206             });
42207             if (clean.length) { 
42208                 node.setAttribute(n, clean.join(';'));
42209             } else {
42210                 node.removeAttribute(n);
42211             }
42212             
42213         }
42214         
42215         
42216         for (var i = node.attributes.length-1; i > -1 ; i--) {
42217             var a = node.attributes[i];
42218             //console.log(a);
42219             
42220             if (a.name.toLowerCase().substr(0,2)=='on')  {
42221                 node.removeAttribute(a.name);
42222                 continue;
42223             }
42224             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42225                 node.removeAttribute(a.name);
42226                 continue;
42227             }
42228             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42229                 cleanAttr(a.name,a.value); // fixme..
42230                 continue;
42231             }
42232             if (a.name == 'style') {
42233                 cleanStyle(a.name,a.value);
42234                 continue;
42235             }
42236             /// clean up MS crap..
42237             // tecnically this should be a list of valid class'es..
42238             
42239             
42240             if (a.name == 'class') {
42241                 if (a.value.match(/^Mso/)) {
42242                     node.className = '';
42243                 }
42244                 
42245                 if (a.value.match(/body/)) {
42246                     node.className = '';
42247                 }
42248                 continue;
42249             }
42250             
42251             // style cleanup!?
42252             // class cleanup?
42253             
42254         }
42255         
42256         
42257         this.cleanUpChildren(node);
42258         
42259         
42260     },
42261     
42262     /**
42263      * Clean up MS wordisms...
42264      */
42265     cleanWord : function(node)
42266     {
42267         
42268         
42269         if (!node) {
42270             this.cleanWord(this.doc.body);
42271             return;
42272         }
42273         if (node.nodeName == "#text") {
42274             // clean up silly Windows -- stuff?
42275             return; 
42276         }
42277         if (node.nodeName == "#comment") {
42278             node.parentNode.removeChild(node);
42279             // clean up silly Windows -- stuff?
42280             return; 
42281         }
42282         
42283         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42284             node.parentNode.removeChild(node);
42285             return;
42286         }
42287         
42288         // remove - but keep children..
42289         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42290             while (node.childNodes.length) {
42291                 var cn = node.childNodes[0];
42292                 node.removeChild(cn);
42293                 node.parentNode.insertBefore(cn, node);
42294             }
42295             node.parentNode.removeChild(node);
42296             this.iterateChildren(node, this.cleanWord);
42297             return;
42298         }
42299         // clean styles
42300         if (node.className.length) {
42301             
42302             var cn = node.className.split(/\W+/);
42303             var cna = [];
42304             Roo.each(cn, function(cls) {
42305                 if (cls.match(/Mso[a-zA-Z]+/)) {
42306                     return;
42307                 }
42308                 cna.push(cls);
42309             });
42310             node.className = cna.length ? cna.join(' ') : '';
42311             if (!cna.length) {
42312                 node.removeAttribute("class");
42313             }
42314         }
42315         
42316         if (node.hasAttribute("lang")) {
42317             node.removeAttribute("lang");
42318         }
42319         
42320         if (node.hasAttribute("style")) {
42321             
42322             var styles = node.getAttribute("style").split(";");
42323             var nstyle = [];
42324             Roo.each(styles, function(s) {
42325                 if (!s.match(/:/)) {
42326                     return;
42327                 }
42328                 var kv = s.split(":");
42329                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42330                     return;
42331                 }
42332                 // what ever is left... we allow.
42333                 nstyle.push(s);
42334             });
42335             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42336             if (!nstyle.length) {
42337                 node.removeAttribute('style');
42338             }
42339         }
42340         this.iterateChildren(node, this.cleanWord);
42341         
42342         
42343         
42344     },
42345     /**
42346      * iterateChildren of a Node, calling fn each time, using this as the scole..
42347      * @param {DomNode} node node to iterate children of.
42348      * @param {Function} fn method of this class to call on each item.
42349      */
42350     iterateChildren : function(node, fn)
42351     {
42352         if (!node.childNodes.length) {
42353                 return;
42354         }
42355         for (var i = node.childNodes.length-1; i > -1 ; i--) {
42356            fn.call(this, node.childNodes[i])
42357         }
42358     },
42359     
42360     
42361     /**
42362      * cleanTableWidths.
42363      *
42364      * Quite often pasting from word etc.. results in tables with column and widths.
42365      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
42366      *
42367      */
42368     cleanTableWidths : function(node)
42369     {
42370          
42371          
42372         if (!node) {
42373             this.cleanTableWidths(this.doc.body);
42374             return;
42375         }
42376         
42377         // ignore list...
42378         if (node.nodeName == "#text" || node.nodeName == "#comment") {
42379             return; 
42380         }
42381         Roo.log(node.tagName);
42382         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
42383             this.iterateChildren(node, this.cleanTableWidths);
42384             return;
42385         }
42386         if (node.hasAttribute('width')) {
42387             node.removeAttribute('width');
42388         }
42389         
42390          
42391         if (node.hasAttribute("style")) {
42392             // pretty basic...
42393             
42394             var styles = node.getAttribute("style").split(";");
42395             var nstyle = [];
42396             Roo.each(styles, function(s) {
42397                 if (!s.match(/:/)) {
42398                     return;
42399                 }
42400                 var kv = s.split(":");
42401                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
42402                     return;
42403                 }
42404                 // what ever is left... we allow.
42405                 nstyle.push(s);
42406             });
42407             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42408             if (!nstyle.length) {
42409                 node.removeAttribute('style');
42410             }
42411         }
42412         
42413         this.iterateChildren(node, this.cleanTableWidths);
42414         
42415         
42416     },
42417     
42418     
42419     
42420     
42421     domToHTML : function(currentElement, depth, nopadtext) {
42422         
42423         depth = depth || 0;
42424         nopadtext = nopadtext || false;
42425     
42426         if (!currentElement) {
42427             return this.domToHTML(this.doc.body);
42428         }
42429         
42430         //Roo.log(currentElement);
42431         var j;
42432         var allText = false;
42433         var nodeName = currentElement.nodeName;
42434         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42435         
42436         if  (nodeName == '#text') {
42437             
42438             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
42439         }
42440         
42441         
42442         var ret = '';
42443         if (nodeName != 'BODY') {
42444              
42445             var i = 0;
42446             // Prints the node tagName, such as <A>, <IMG>, etc
42447             if (tagName) {
42448                 var attr = [];
42449                 for(i = 0; i < currentElement.attributes.length;i++) {
42450                     // quoting?
42451                     var aname = currentElement.attributes.item(i).name;
42452                     if (!currentElement.attributes.item(i).value.length) {
42453                         continue;
42454                     }
42455                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42456                 }
42457                 
42458                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42459             } 
42460             else {
42461                 
42462                 // eack
42463             }
42464         } else {
42465             tagName = false;
42466         }
42467         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42468             return ret;
42469         }
42470         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42471             nopadtext = true;
42472         }
42473         
42474         
42475         // Traverse the tree
42476         i = 0;
42477         var currentElementChild = currentElement.childNodes.item(i);
42478         var allText = true;
42479         var innerHTML  = '';
42480         lastnode = '';
42481         while (currentElementChild) {
42482             // Formatting code (indent the tree so it looks nice on the screen)
42483             var nopad = nopadtext;
42484             if (lastnode == 'SPAN') {
42485                 nopad  = true;
42486             }
42487             // text
42488             if  (currentElementChild.nodeName == '#text') {
42489                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42490                 toadd = nopadtext ? toadd : toadd.trim();
42491                 if (!nopad && toadd.length > 80) {
42492                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42493                 }
42494                 innerHTML  += toadd;
42495                 
42496                 i++;
42497                 currentElementChild = currentElement.childNodes.item(i);
42498                 lastNode = '';
42499                 continue;
42500             }
42501             allText = false;
42502             
42503             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42504                 
42505             // Recursively traverse the tree structure of the child node
42506             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42507             lastnode = currentElementChild.nodeName;
42508             i++;
42509             currentElementChild=currentElement.childNodes.item(i);
42510         }
42511         
42512         ret += innerHTML;
42513         
42514         if (!allText) {
42515                 // The remaining code is mostly for formatting the tree
42516             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42517         }
42518         
42519         
42520         if (tagName) {
42521             ret+= "</"+tagName+">";
42522         }
42523         return ret;
42524         
42525     },
42526         
42527     applyBlacklists : function()
42528     {
42529         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
42530         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
42531         
42532         this.white = [];
42533         this.black = [];
42534         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
42535             if (b.indexOf(tag) > -1) {
42536                 return;
42537             }
42538             this.white.push(tag);
42539             
42540         }, this);
42541         
42542         Roo.each(w, function(tag) {
42543             if (b.indexOf(tag) > -1) {
42544                 return;
42545             }
42546             if (this.white.indexOf(tag) > -1) {
42547                 return;
42548             }
42549             this.white.push(tag);
42550             
42551         }, this);
42552         
42553         
42554         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
42555             if (w.indexOf(tag) > -1) {
42556                 return;
42557             }
42558             this.black.push(tag);
42559             
42560         }, this);
42561         
42562         Roo.each(b, function(tag) {
42563             if (w.indexOf(tag) > -1) {
42564                 return;
42565             }
42566             if (this.black.indexOf(tag) > -1) {
42567                 return;
42568             }
42569             this.black.push(tag);
42570             
42571         }, this);
42572         
42573         
42574         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
42575         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
42576         
42577         this.cwhite = [];
42578         this.cblack = [];
42579         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
42580             if (b.indexOf(tag) > -1) {
42581                 return;
42582             }
42583             this.cwhite.push(tag);
42584             
42585         }, this);
42586         
42587         Roo.each(w, function(tag) {
42588             if (b.indexOf(tag) > -1) {
42589                 return;
42590             }
42591             if (this.cwhite.indexOf(tag) > -1) {
42592                 return;
42593             }
42594             this.cwhite.push(tag);
42595             
42596         }, this);
42597         
42598         
42599         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
42600             if (w.indexOf(tag) > -1) {
42601                 return;
42602             }
42603             this.cblack.push(tag);
42604             
42605         }, this);
42606         
42607         Roo.each(b, function(tag) {
42608             if (w.indexOf(tag) > -1) {
42609                 return;
42610             }
42611             if (this.cblack.indexOf(tag) > -1) {
42612                 return;
42613             }
42614             this.cblack.push(tag);
42615             
42616         }, this);
42617     },
42618     
42619     setStylesheets : function(stylesheets)
42620     {
42621         if(typeof(stylesheets) == 'string'){
42622             Roo.get(this.iframe.contentDocument.head).createChild({
42623                 tag : 'link',
42624                 rel : 'stylesheet',
42625                 type : 'text/css',
42626                 href : stylesheets
42627             });
42628             
42629             return;
42630         }
42631         var _this = this;
42632      
42633         Roo.each(stylesheets, function(s) {
42634             if(!s.length){
42635                 return;
42636             }
42637             
42638             Roo.get(_this.iframe.contentDocument.head).createChild({
42639                 tag : 'link',
42640                 rel : 'stylesheet',
42641                 type : 'text/css',
42642                 href : s
42643             });
42644         });
42645
42646         
42647     },
42648     
42649     removeStylesheets : function()
42650     {
42651         var _this = this;
42652         
42653         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
42654             s.remove();
42655         });
42656     }
42657     
42658     // hide stuff that is not compatible
42659     /**
42660      * @event blur
42661      * @hide
42662      */
42663     /**
42664      * @event change
42665      * @hide
42666      */
42667     /**
42668      * @event focus
42669      * @hide
42670      */
42671     /**
42672      * @event specialkey
42673      * @hide
42674      */
42675     /**
42676      * @cfg {String} fieldClass @hide
42677      */
42678     /**
42679      * @cfg {String} focusClass @hide
42680      */
42681     /**
42682      * @cfg {String} autoCreate @hide
42683      */
42684     /**
42685      * @cfg {String} inputType @hide
42686      */
42687     /**
42688      * @cfg {String} invalidClass @hide
42689      */
42690     /**
42691      * @cfg {String} invalidText @hide
42692      */
42693     /**
42694      * @cfg {String} msgFx @hide
42695      */
42696     /**
42697      * @cfg {String} validateOnBlur @hide
42698      */
42699 });
42700
42701 Roo.HtmlEditorCore.white = [
42702         'area', 'br', 'img', 'input', 'hr', 'wbr',
42703         
42704        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42705        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42706        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42707        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42708        'table',   'ul',         'xmp', 
42709        
42710        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42711       'thead',   'tr', 
42712      
42713       'dir', 'menu', 'ol', 'ul', 'dl',
42714        
42715       'embed',  'object'
42716 ];
42717
42718
42719 Roo.HtmlEditorCore.black = [
42720     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42721         'applet', // 
42722         'base',   'basefont', 'bgsound', 'blink',  'body', 
42723         'frame',  'frameset', 'head',    'html',   'ilayer', 
42724         'iframe', 'layer',  'link',     'meta',    'object',   
42725         'script', 'style' ,'title',  'xml' // clean later..
42726 ];
42727 Roo.HtmlEditorCore.clean = [
42728     'script', 'style', 'title', 'xml'
42729 ];
42730 Roo.HtmlEditorCore.remove = [
42731     'font'
42732 ];
42733 // attributes..
42734
42735 Roo.HtmlEditorCore.ablack = [
42736     'on'
42737 ];
42738     
42739 Roo.HtmlEditorCore.aclean = [ 
42740     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42741 ];
42742
42743 // protocols..
42744 Roo.HtmlEditorCore.pwhite= [
42745         'http',  'https',  'mailto'
42746 ];
42747
42748 // white listed style attributes.
42749 Roo.HtmlEditorCore.cwhite= [
42750       //  'text-align', /// default is to allow most things..
42751       
42752          
42753 //        'font-size'//??
42754 ];
42755
42756 // black listed style attributes.
42757 Roo.HtmlEditorCore.cblack= [
42758       //  'font-size' -- this can be set by the project 
42759 ];
42760
42761
42762 Roo.HtmlEditorCore.swapCodes   =[ 
42763     [    8211, "--" ], 
42764     [    8212, "--" ], 
42765     [    8216,  "'" ],  
42766     [    8217, "'" ],  
42767     [    8220, '"' ],  
42768     [    8221, '"' ],  
42769     [    8226, "*" ],  
42770     [    8230, "..." ]
42771 ]; 
42772
42773     //<script type="text/javascript">
42774
42775 /*
42776  * Ext JS Library 1.1.1
42777  * Copyright(c) 2006-2007, Ext JS, LLC.
42778  * Licence LGPL
42779  * 
42780  */
42781  
42782  
42783 Roo.form.HtmlEditor = function(config){
42784     
42785     
42786     
42787     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42788     
42789     if (!this.toolbars) {
42790         this.toolbars = [];
42791     }
42792     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42793     
42794     
42795 };
42796
42797 /**
42798  * @class Roo.form.HtmlEditor
42799  * @extends Roo.form.Field
42800  * Provides a lightweight HTML Editor component.
42801  *
42802  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42803  * 
42804  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42805  * supported by this editor.</b><br/><br/>
42806  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42807  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42808  */
42809 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42810     /**
42811      * @cfg {Boolean} clearUp
42812      */
42813     clearUp : true,
42814       /**
42815      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42816      */
42817     toolbars : false,
42818    
42819      /**
42820      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42821      *                        Roo.resizable.
42822      */
42823     resizable : false,
42824      /**
42825      * @cfg {Number} height (in pixels)
42826      */   
42827     height: 300,
42828    /**
42829      * @cfg {Number} width (in pixels)
42830      */   
42831     width: 500,
42832     
42833     /**
42834      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42835      * 
42836      */
42837     stylesheets: false,
42838     
42839     
42840      /**
42841      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
42842      * 
42843      */
42844     cblack: false,
42845     /**
42846      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
42847      * 
42848      */
42849     cwhite: false,
42850     
42851      /**
42852      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
42853      * 
42854      */
42855     black: false,
42856     /**
42857      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
42858      * 
42859      */
42860     white: false,
42861     
42862     // id of frame..
42863     frameId: false,
42864     
42865     // private properties
42866     validationEvent : false,
42867     deferHeight: true,
42868     initialized : false,
42869     activated : false,
42870     
42871     onFocus : Roo.emptyFn,
42872     iframePad:3,
42873     hideMode:'offsets',
42874     
42875     actionMode : 'container', // defaults to hiding it...
42876     
42877     defaultAutoCreate : { // modified by initCompnoent..
42878         tag: "textarea",
42879         style:"width:500px;height:300px;",
42880         autocomplete: "new-password"
42881     },
42882
42883     // private
42884     initComponent : function(){
42885         this.addEvents({
42886             /**
42887              * @event initialize
42888              * Fires when the editor is fully initialized (including the iframe)
42889              * @param {HtmlEditor} this
42890              */
42891             initialize: true,
42892             /**
42893              * @event activate
42894              * Fires when the editor is first receives the focus. Any insertion must wait
42895              * until after this event.
42896              * @param {HtmlEditor} this
42897              */
42898             activate: true,
42899              /**
42900              * @event beforesync
42901              * Fires before the textarea is updated with content from the editor iframe. Return false
42902              * to cancel the sync.
42903              * @param {HtmlEditor} this
42904              * @param {String} html
42905              */
42906             beforesync: true,
42907              /**
42908              * @event beforepush
42909              * Fires before the iframe editor is updated with content from the textarea. Return false
42910              * to cancel the push.
42911              * @param {HtmlEditor} this
42912              * @param {String} html
42913              */
42914             beforepush: true,
42915              /**
42916              * @event sync
42917              * Fires when the textarea is updated with content from the editor iframe.
42918              * @param {HtmlEditor} this
42919              * @param {String} html
42920              */
42921             sync: true,
42922              /**
42923              * @event push
42924              * Fires when the iframe editor is updated with content from the textarea.
42925              * @param {HtmlEditor} this
42926              * @param {String} html
42927              */
42928             push: true,
42929              /**
42930              * @event editmodechange
42931              * Fires when the editor switches edit modes
42932              * @param {HtmlEditor} this
42933              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42934              */
42935             editmodechange: true,
42936             /**
42937              * @event editorevent
42938              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42939              * @param {HtmlEditor} this
42940              */
42941             editorevent: true,
42942             /**
42943              * @event firstfocus
42944              * Fires when on first focus - needed by toolbars..
42945              * @param {HtmlEditor} this
42946              */
42947             firstfocus: true,
42948             /**
42949              * @event autosave
42950              * Auto save the htmlEditor value as a file into Events
42951              * @param {HtmlEditor} this
42952              */
42953             autosave: true,
42954             /**
42955              * @event savedpreview
42956              * preview the saved version of htmlEditor
42957              * @param {HtmlEditor} this
42958              */
42959             savedpreview: true,
42960             
42961             /**
42962             * @event stylesheetsclick
42963             * Fires when press the Sytlesheets button
42964             * @param {Roo.HtmlEditorCore} this
42965             */
42966             stylesheetsclick: true
42967         });
42968         this.defaultAutoCreate =  {
42969             tag: "textarea",
42970             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42971             autocomplete: "new-password"
42972         };
42973     },
42974
42975     /**
42976      * Protected method that will not generally be called directly. It
42977      * is called when the editor creates its toolbar. Override this method if you need to
42978      * add custom toolbar buttons.
42979      * @param {HtmlEditor} editor
42980      */
42981     createToolbar : function(editor){
42982         Roo.log("create toolbars");
42983         if (!editor.toolbars || !editor.toolbars.length) {
42984             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
42985         }
42986         
42987         for (var i =0 ; i < editor.toolbars.length;i++) {
42988             editor.toolbars[i] = Roo.factory(
42989                     typeof(editor.toolbars[i]) == 'string' ?
42990                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
42991                 Roo.form.HtmlEditor);
42992             editor.toolbars[i].init(editor);
42993         }
42994          
42995         
42996     },
42997
42998      
42999     // private
43000     onRender : function(ct, position)
43001     {
43002         var _t = this;
43003         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
43004         
43005         this.wrap = this.el.wrap({
43006             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
43007         });
43008         
43009         this.editorcore.onRender(ct, position);
43010          
43011         if (this.resizable) {
43012             this.resizeEl = new Roo.Resizable(this.wrap, {
43013                 pinned : true,
43014                 wrap: true,
43015                 dynamic : true,
43016                 minHeight : this.height,
43017                 height: this.height,
43018                 handles : this.resizable,
43019                 width: this.width,
43020                 listeners : {
43021                     resize : function(r, w, h) {
43022                         _t.onResize(w,h); // -something
43023                     }
43024                 }
43025             });
43026             
43027         }
43028         this.createToolbar(this);
43029        
43030         
43031         if(!this.width){
43032             this.setSize(this.wrap.getSize());
43033         }
43034         if (this.resizeEl) {
43035             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
43036             // should trigger onReize..
43037         }
43038         
43039         this.keyNav = new Roo.KeyNav(this.el, {
43040             
43041             "tab" : function(e){
43042                 e.preventDefault();
43043                 
43044                 var value = this.getValue();
43045                 
43046                 var start = this.el.dom.selectionStart;
43047                 var end = this.el.dom.selectionEnd;
43048                 
43049                 if(!e.shiftKey){
43050                     
43051                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
43052                     this.el.dom.setSelectionRange(end + 1, end + 1);
43053                     return;
43054                 }
43055                 
43056                 var f = value.substring(0, start).split("\t");
43057                 
43058                 if(f.pop().length != 0){
43059                     return;
43060                 }
43061                 
43062                 this.setValue(f.join("\t") + value.substring(end));
43063                 this.el.dom.setSelectionRange(start - 1, start - 1);
43064                 
43065             },
43066             
43067             "home" : function(e){
43068                 e.preventDefault();
43069                 
43070                 var curr = this.el.dom.selectionStart;
43071                 var lines = this.getValue().split("\n");
43072                 
43073                 if(!lines.length){
43074                     return;
43075                 }
43076                 
43077                 if(e.ctrlKey){
43078                     this.el.dom.setSelectionRange(0, 0);
43079                     return;
43080                 }
43081                 
43082                 var pos = 0;
43083                 
43084                 for (var i = 0; i < lines.length;i++) {
43085                     pos += lines[i].length;
43086                     
43087                     if(i != 0){
43088                         pos += 1;
43089                     }
43090                     
43091                     if(pos < curr){
43092                         continue;
43093                     }
43094                     
43095                     pos -= lines[i].length;
43096                     
43097                     break;
43098                 }
43099                 
43100                 if(!e.shiftKey){
43101                     this.el.dom.setSelectionRange(pos, pos);
43102                     return;
43103                 }
43104                 
43105                 this.el.dom.selectionStart = pos;
43106                 this.el.dom.selectionEnd = curr;
43107             },
43108             
43109             "end" : function(e){
43110                 e.preventDefault();
43111                 
43112                 var curr = this.el.dom.selectionStart;
43113                 var lines = this.getValue().split("\n");
43114                 
43115                 if(!lines.length){
43116                     return;
43117                 }
43118                 
43119                 if(e.ctrlKey){
43120                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
43121                     return;
43122                 }
43123                 
43124                 var pos = 0;
43125                 
43126                 for (var i = 0; i < lines.length;i++) {
43127                     
43128                     pos += lines[i].length;
43129                     
43130                     if(i != 0){
43131                         pos += 1;
43132                     }
43133                     
43134                     if(pos < curr){
43135                         continue;
43136                     }
43137                     
43138                     break;
43139                 }
43140                 
43141                 if(!e.shiftKey){
43142                     this.el.dom.setSelectionRange(pos, pos);
43143                     return;
43144                 }
43145                 
43146                 this.el.dom.selectionStart = curr;
43147                 this.el.dom.selectionEnd = pos;
43148             },
43149
43150             scope : this,
43151
43152             doRelay : function(foo, bar, hname){
43153                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43154             },
43155
43156             forceKeyDown: true
43157         });
43158         
43159 //        if(this.autosave && this.w){
43160 //            this.autoSaveFn = setInterval(this.autosave, 1000);
43161 //        }
43162     },
43163
43164     // private
43165     onResize : function(w, h)
43166     {
43167         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
43168         var ew = false;
43169         var eh = false;
43170         
43171         if(this.el ){
43172             if(typeof w == 'number'){
43173                 var aw = w - this.wrap.getFrameWidth('lr');
43174                 this.el.setWidth(this.adjustWidth('textarea', aw));
43175                 ew = aw;
43176             }
43177             if(typeof h == 'number'){
43178                 var tbh = 0;
43179                 for (var i =0; i < this.toolbars.length;i++) {
43180                     // fixme - ask toolbars for heights?
43181                     tbh += this.toolbars[i].tb.el.getHeight();
43182                     if (this.toolbars[i].footer) {
43183                         tbh += this.toolbars[i].footer.el.getHeight();
43184                     }
43185                 }
43186                 
43187                 
43188                 
43189                 
43190                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
43191                 ah -= 5; // knock a few pixes off for look..
43192 //                Roo.log(ah);
43193                 this.el.setHeight(this.adjustWidth('textarea', ah));
43194                 var eh = ah;
43195             }
43196         }
43197         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
43198         this.editorcore.onResize(ew,eh);
43199         
43200     },
43201
43202     /**
43203      * Toggles the editor between standard and source edit mode.
43204      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43205      */
43206     toggleSourceEdit : function(sourceEditMode)
43207     {
43208         this.editorcore.toggleSourceEdit(sourceEditMode);
43209         
43210         if(this.editorcore.sourceEditMode){
43211             Roo.log('editor - showing textarea');
43212             
43213 //            Roo.log('in');
43214 //            Roo.log(this.syncValue());
43215             this.editorcore.syncValue();
43216             this.el.removeClass('x-hidden');
43217             this.el.dom.removeAttribute('tabIndex');
43218             this.el.focus();
43219             
43220             for (var i = 0; i < this.toolbars.length; i++) {
43221                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43222                     this.toolbars[i].tb.hide();
43223                     this.toolbars[i].footer.hide();
43224                 }
43225             }
43226             
43227         }else{
43228             Roo.log('editor - hiding textarea');
43229 //            Roo.log('out')
43230 //            Roo.log(this.pushValue()); 
43231             this.editorcore.pushValue();
43232             
43233             this.el.addClass('x-hidden');
43234             this.el.dom.setAttribute('tabIndex', -1);
43235             
43236             for (var i = 0; i < this.toolbars.length; i++) {
43237                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43238                     this.toolbars[i].tb.show();
43239                     this.toolbars[i].footer.show();
43240                 }
43241             }
43242             
43243             //this.deferFocus();
43244         }
43245         
43246         this.setSize(this.wrap.getSize());
43247         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
43248         
43249         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
43250     },
43251  
43252     // private (for BoxComponent)
43253     adjustSize : Roo.BoxComponent.prototype.adjustSize,
43254
43255     // private (for BoxComponent)
43256     getResizeEl : function(){
43257         return this.wrap;
43258     },
43259
43260     // private (for BoxComponent)
43261     getPositionEl : function(){
43262         return this.wrap;
43263     },
43264
43265     // private
43266     initEvents : function(){
43267         this.originalValue = this.getValue();
43268     },
43269
43270     /**
43271      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43272      * @method
43273      */
43274     markInvalid : Roo.emptyFn,
43275     /**
43276      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43277      * @method
43278      */
43279     clearInvalid : Roo.emptyFn,
43280
43281     setValue : function(v){
43282         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
43283         this.editorcore.pushValue();
43284     },
43285
43286      
43287     // private
43288     deferFocus : function(){
43289         this.focus.defer(10, this);
43290     },
43291
43292     // doc'ed in Field
43293     focus : function(){
43294         this.editorcore.focus();
43295         
43296     },
43297       
43298
43299     // private
43300     onDestroy : function(){
43301         
43302         
43303         
43304         if(this.rendered){
43305             
43306             for (var i =0; i < this.toolbars.length;i++) {
43307                 // fixme - ask toolbars for heights?
43308                 this.toolbars[i].onDestroy();
43309             }
43310             
43311             this.wrap.dom.innerHTML = '';
43312             this.wrap.remove();
43313         }
43314     },
43315
43316     // private
43317     onFirstFocus : function(){
43318         //Roo.log("onFirstFocus");
43319         this.editorcore.onFirstFocus();
43320          for (var i =0; i < this.toolbars.length;i++) {
43321             this.toolbars[i].onFirstFocus();
43322         }
43323         
43324     },
43325     
43326     // private
43327     syncValue : function()
43328     {
43329         this.editorcore.syncValue();
43330     },
43331     
43332     pushValue : function()
43333     {
43334         this.editorcore.pushValue();
43335     },
43336     
43337     setStylesheets : function(stylesheets)
43338     {
43339         this.editorcore.setStylesheets(stylesheets);
43340     },
43341     
43342     removeStylesheets : function()
43343     {
43344         this.editorcore.removeStylesheets();
43345     }
43346      
43347     
43348     // hide stuff that is not compatible
43349     /**
43350      * @event blur
43351      * @hide
43352      */
43353     /**
43354      * @event change
43355      * @hide
43356      */
43357     /**
43358      * @event focus
43359      * @hide
43360      */
43361     /**
43362      * @event specialkey
43363      * @hide
43364      */
43365     /**
43366      * @cfg {String} fieldClass @hide
43367      */
43368     /**
43369      * @cfg {String} focusClass @hide
43370      */
43371     /**
43372      * @cfg {String} autoCreate @hide
43373      */
43374     /**
43375      * @cfg {String} inputType @hide
43376      */
43377     /**
43378      * @cfg {String} invalidClass @hide
43379      */
43380     /**
43381      * @cfg {String} invalidText @hide
43382      */
43383     /**
43384      * @cfg {String} msgFx @hide
43385      */
43386     /**
43387      * @cfg {String} validateOnBlur @hide
43388      */
43389 });
43390  
43391     // <script type="text/javascript">
43392 /*
43393  * Based on
43394  * Ext JS Library 1.1.1
43395  * Copyright(c) 2006-2007, Ext JS, LLC.
43396  *  
43397  
43398  */
43399
43400 /**
43401  * @class Roo.form.HtmlEditorToolbar1
43402  * Basic Toolbar
43403  * 
43404  * Usage:
43405  *
43406  new Roo.form.HtmlEditor({
43407     ....
43408     toolbars : [
43409         new Roo.form.HtmlEditorToolbar1({
43410             disable : { fonts: 1 , format: 1, ..., ... , ...],
43411             btns : [ .... ]
43412         })
43413     }
43414      
43415  * 
43416  * @cfg {Object} disable List of elements to disable..
43417  * @cfg {Array} btns List of additional buttons.
43418  * 
43419  * 
43420  * NEEDS Extra CSS? 
43421  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
43422  */
43423  
43424 Roo.form.HtmlEditor.ToolbarStandard = function(config)
43425 {
43426     
43427     Roo.apply(this, config);
43428     
43429     // default disabled, based on 'good practice'..
43430     this.disable = this.disable || {};
43431     Roo.applyIf(this.disable, {
43432         fontSize : true,
43433         colors : true,
43434         specialElements : true
43435     });
43436     
43437     
43438     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43439     // dont call parent... till later.
43440 }
43441
43442 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
43443     
43444     tb: false,
43445     
43446     rendered: false,
43447     
43448     editor : false,
43449     editorcore : false,
43450     /**
43451      * @cfg {Object} disable  List of toolbar elements to disable
43452          
43453      */
43454     disable : false,
43455     
43456     
43457      /**
43458      * @cfg {String} createLinkText The default text for the create link prompt
43459      */
43460     createLinkText : 'Please enter the URL for the link:',
43461     /**
43462      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
43463      */
43464     defaultLinkValue : 'http:/'+'/',
43465    
43466     
43467       /**
43468      * @cfg {Array} fontFamilies An array of available font families
43469      */
43470     fontFamilies : [
43471         'Arial',
43472         'Courier New',
43473         'Tahoma',
43474         'Times New Roman',
43475         'Verdana'
43476     ],
43477     
43478     specialChars : [
43479            "&#169;",
43480           "&#174;",     
43481           "&#8482;",    
43482           "&#163;" ,    
43483          // "&#8212;",    
43484           "&#8230;",    
43485           "&#247;" ,    
43486         //  "&#225;" ,     ?? a acute?
43487            "&#8364;"    , //Euro
43488        //   "&#8220;"    ,
43489         //  "&#8221;"    ,
43490         //  "&#8226;"    ,
43491           "&#176;"  //   , // degrees
43492
43493          // "&#233;"     , // e ecute
43494          // "&#250;"     , // u ecute?
43495     ],
43496     
43497     specialElements : [
43498         {
43499             text: "Insert Table",
43500             xtype: 'MenuItem',
43501             xns : Roo.Menu,
43502             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
43503                 
43504         },
43505         {    
43506             text: "Insert Image",
43507             xtype: 'MenuItem',
43508             xns : Roo.Menu,
43509             ihtml : '<img src="about:blank"/>'
43510             
43511         }
43512         
43513          
43514     ],
43515     
43516     
43517     inputElements : [ 
43518             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
43519             "input:submit", "input:button", "select", "textarea", "label" ],
43520     formats : [
43521         ["p"] ,  
43522         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
43523         ["pre"],[ "code"], 
43524         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
43525         ['div'],['span']
43526     ],
43527     
43528     cleanStyles : [
43529         "font-size"
43530     ],
43531      /**
43532      * @cfg {String} defaultFont default font to use.
43533      */
43534     defaultFont: 'tahoma',
43535    
43536     fontSelect : false,
43537     
43538     
43539     formatCombo : false,
43540     
43541     init : function(editor)
43542     {
43543         this.editor = editor;
43544         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43545         var editorcore = this.editorcore;
43546         
43547         var _t = this;
43548         
43549         var fid = editorcore.frameId;
43550         var etb = this;
43551         function btn(id, toggle, handler){
43552             var xid = fid + '-'+ id ;
43553             return {
43554                 id : xid,
43555                 cmd : id,
43556                 cls : 'x-btn-icon x-edit-'+id,
43557                 enableToggle:toggle !== false,
43558                 scope: _t, // was editor...
43559                 handler:handler||_t.relayBtnCmd,
43560                 clickEvent:'mousedown',
43561                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43562                 tabIndex:-1
43563             };
43564         }
43565         
43566         
43567         
43568         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43569         this.tb = tb;
43570          // stop form submits
43571         tb.el.on('click', function(e){
43572             e.preventDefault(); // what does this do?
43573         });
43574
43575         if(!this.disable.font) { // && !Roo.isSafari){
43576             /* why no safari for fonts 
43577             editor.fontSelect = tb.el.createChild({
43578                 tag:'select',
43579                 tabIndex: -1,
43580                 cls:'x-font-select',
43581                 html: this.createFontOptions()
43582             });
43583             
43584             editor.fontSelect.on('change', function(){
43585                 var font = editor.fontSelect.dom.value;
43586                 editor.relayCmd('fontname', font);
43587                 editor.deferFocus();
43588             }, editor);
43589             
43590             tb.add(
43591                 editor.fontSelect.dom,
43592                 '-'
43593             );
43594             */
43595             
43596         };
43597         if(!this.disable.formats){
43598             this.formatCombo = new Roo.form.ComboBox({
43599                 store: new Roo.data.SimpleStore({
43600                     id : 'tag',
43601                     fields: ['tag'],
43602                     data : this.formats // from states.js
43603                 }),
43604                 blockFocus : true,
43605                 name : '',
43606                 //autoCreate : {tag: "div",  size: "20"},
43607                 displayField:'tag',
43608                 typeAhead: false,
43609                 mode: 'local',
43610                 editable : false,
43611                 triggerAction: 'all',
43612                 emptyText:'Add tag',
43613                 selectOnFocus:true,
43614                 width:135,
43615                 listeners : {
43616                     'select': function(c, r, i) {
43617                         editorcore.insertTag(r.get('tag'));
43618                         editor.focus();
43619                     }
43620                 }
43621
43622             });
43623             tb.addField(this.formatCombo);
43624             
43625         }
43626         
43627         if(!this.disable.format){
43628             tb.add(
43629                 btn('bold'),
43630                 btn('italic'),
43631                 btn('underline')
43632             );
43633         };
43634         if(!this.disable.fontSize){
43635             tb.add(
43636                 '-',
43637                 
43638                 
43639                 btn('increasefontsize', false, editorcore.adjustFont),
43640                 btn('decreasefontsize', false, editorcore.adjustFont)
43641             );
43642         };
43643         
43644         
43645         if(!this.disable.colors){
43646             tb.add(
43647                 '-', {
43648                     id:editorcore.frameId +'-forecolor',
43649                     cls:'x-btn-icon x-edit-forecolor',
43650                     clickEvent:'mousedown',
43651                     tooltip: this.buttonTips['forecolor'] || undefined,
43652                     tabIndex:-1,
43653                     menu : new Roo.menu.ColorMenu({
43654                         allowReselect: true,
43655                         focus: Roo.emptyFn,
43656                         value:'000000',
43657                         plain:true,
43658                         selectHandler: function(cp, color){
43659                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43660                             editor.deferFocus();
43661                         },
43662                         scope: editorcore,
43663                         clickEvent:'mousedown'
43664                     })
43665                 }, {
43666                     id:editorcore.frameId +'backcolor',
43667                     cls:'x-btn-icon x-edit-backcolor',
43668                     clickEvent:'mousedown',
43669                     tooltip: this.buttonTips['backcolor'] || undefined,
43670                     tabIndex:-1,
43671                     menu : new Roo.menu.ColorMenu({
43672                         focus: Roo.emptyFn,
43673                         value:'FFFFFF',
43674                         plain:true,
43675                         allowReselect: true,
43676                         selectHandler: function(cp, color){
43677                             if(Roo.isGecko){
43678                                 editorcore.execCmd('useCSS', false);
43679                                 editorcore.execCmd('hilitecolor', color);
43680                                 editorcore.execCmd('useCSS', true);
43681                                 editor.deferFocus();
43682                             }else{
43683                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43684                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43685                                 editor.deferFocus();
43686                             }
43687                         },
43688                         scope:editorcore,
43689                         clickEvent:'mousedown'
43690                     })
43691                 }
43692             );
43693         };
43694         // now add all the items...
43695         
43696
43697         if(!this.disable.alignments){
43698             tb.add(
43699                 '-',
43700                 btn('justifyleft'),
43701                 btn('justifycenter'),
43702                 btn('justifyright')
43703             );
43704         };
43705
43706         //if(!Roo.isSafari){
43707             if(!this.disable.links){
43708                 tb.add(
43709                     '-',
43710                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43711                 );
43712             };
43713
43714             if(!this.disable.lists){
43715                 tb.add(
43716                     '-',
43717                     btn('insertorderedlist'),
43718                     btn('insertunorderedlist')
43719                 );
43720             }
43721             if(!this.disable.sourceEdit){
43722                 tb.add(
43723                     '-',
43724                     btn('sourceedit', true, function(btn){
43725                         this.toggleSourceEdit(btn.pressed);
43726                     })
43727                 );
43728             }
43729         //}
43730         
43731         var smenu = { };
43732         // special menu.. - needs to be tidied up..
43733         if (!this.disable.special) {
43734             smenu = {
43735                 text: "&#169;",
43736                 cls: 'x-edit-none',
43737                 
43738                 menu : {
43739                     items : []
43740                 }
43741             };
43742             for (var i =0; i < this.specialChars.length; i++) {
43743                 smenu.menu.items.push({
43744                     
43745                     html: this.specialChars[i],
43746                     handler: function(a,b) {
43747                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43748                         //editor.insertAtCursor(a.html);
43749                         
43750                     },
43751                     tabIndex:-1
43752                 });
43753             }
43754             
43755             
43756             tb.add(smenu);
43757             
43758             
43759         }
43760         
43761         var cmenu = { };
43762         if (!this.disable.cleanStyles) {
43763             cmenu = {
43764                 cls: 'x-btn-icon x-btn-clear',
43765                 
43766                 menu : {
43767                     items : []
43768                 }
43769             };
43770             for (var i =0; i < this.cleanStyles.length; i++) {
43771                 cmenu.menu.items.push({
43772                     actiontype : this.cleanStyles[i],
43773                     html: 'Remove ' + this.cleanStyles[i],
43774                     handler: function(a,b) {
43775 //                        Roo.log(a);
43776 //                        Roo.log(b);
43777                         var c = Roo.get(editorcore.doc.body);
43778                         c.select('[style]').each(function(s) {
43779                             s.dom.style.removeProperty(a.actiontype);
43780                         });
43781                         editorcore.syncValue();
43782                     },
43783                     tabIndex:-1
43784                 });
43785             }
43786              cmenu.menu.items.push({
43787                 actiontype : 'tablewidths',
43788                 html: 'Remove Table Widths',
43789                 handler: function(a,b) {
43790                     editorcore.cleanTableWidths();
43791                     editorcore.syncValue();
43792                 },
43793                 tabIndex:-1
43794             });
43795             cmenu.menu.items.push({
43796                 actiontype : 'word',
43797                 html: 'Remove MS Word Formating',
43798                 handler: function(a,b) {
43799                     editorcore.cleanWord();
43800                     editorcore.syncValue();
43801                 },
43802                 tabIndex:-1
43803             });
43804             
43805             cmenu.menu.items.push({
43806                 actiontype : 'all',
43807                 html: 'Remove All Styles',
43808                 handler: function(a,b) {
43809                     
43810                     var c = Roo.get(editorcore.doc.body);
43811                     c.select('[style]').each(function(s) {
43812                         s.dom.removeAttribute('style');
43813                     });
43814                     editorcore.syncValue();
43815                 },
43816                 tabIndex:-1
43817             });
43818             
43819             cmenu.menu.items.push({
43820                 actiontype : 'all',
43821                 html: 'Remove All CSS Classes',
43822                 handler: function(a,b) {
43823                     
43824                     var c = Roo.get(editorcore.doc.body);
43825                     c.select('[class]').each(function(s) {
43826                         s.dom.className = '';
43827                     });
43828                     editorcore.syncValue();
43829                 },
43830                 tabIndex:-1
43831             });
43832             
43833              cmenu.menu.items.push({
43834                 actiontype : 'tidy',
43835                 html: 'Tidy HTML Source',
43836                 handler: function(a,b) {
43837                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43838                     editorcore.syncValue();
43839                 },
43840                 tabIndex:-1
43841             });
43842             
43843             
43844             tb.add(cmenu);
43845         }
43846          
43847         if (!this.disable.specialElements) {
43848             var semenu = {
43849                 text: "Other;",
43850                 cls: 'x-edit-none',
43851                 menu : {
43852                     items : []
43853                 }
43854             };
43855             for (var i =0; i < this.specialElements.length; i++) {
43856                 semenu.menu.items.push(
43857                     Roo.apply({ 
43858                         handler: function(a,b) {
43859                             editor.insertAtCursor(this.ihtml);
43860                         }
43861                     }, this.specialElements[i])
43862                 );
43863                     
43864             }
43865             
43866             tb.add(semenu);
43867             
43868             
43869         }
43870          
43871         
43872         if (this.btns) {
43873             for(var i =0; i< this.btns.length;i++) {
43874                 var b = Roo.factory(this.btns[i],Roo.form);
43875                 b.cls =  'x-edit-none';
43876                 
43877                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
43878                     b.cls += ' x-init-enable';
43879                 }
43880                 
43881                 b.scope = editorcore;
43882                 tb.add(b);
43883             }
43884         
43885         }
43886         
43887         
43888         
43889         // disable everything...
43890         
43891         this.tb.items.each(function(item){
43892             
43893            if(
43894                 item.id != editorcore.frameId+ '-sourceedit' && 
43895                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
43896             ){
43897                 
43898                 item.disable();
43899             }
43900         });
43901         this.rendered = true;
43902         
43903         // the all the btns;
43904         editor.on('editorevent', this.updateToolbar, this);
43905         // other toolbars need to implement this..
43906         //editor.on('editmodechange', this.updateToolbar, this);
43907     },
43908     
43909     
43910     relayBtnCmd : function(btn) {
43911         this.editorcore.relayCmd(btn.cmd);
43912     },
43913     // private used internally
43914     createLink : function(){
43915         Roo.log("create link?");
43916         var url = prompt(this.createLinkText, this.defaultLinkValue);
43917         if(url && url != 'http:/'+'/'){
43918             this.editorcore.relayCmd('createlink', url);
43919         }
43920     },
43921
43922     
43923     /**
43924      * Protected method that will not generally be called directly. It triggers
43925      * a toolbar update by reading the markup state of the current selection in the editor.
43926      */
43927     updateToolbar: function(){
43928
43929         if(!this.editorcore.activated){
43930             this.editor.onFirstFocus();
43931             return;
43932         }
43933
43934         var btns = this.tb.items.map, 
43935             doc = this.editorcore.doc,
43936             frameId = this.editorcore.frameId;
43937
43938         if(!this.disable.font && !Roo.isSafari){
43939             /*
43940             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43941             if(name != this.fontSelect.dom.value){
43942                 this.fontSelect.dom.value = name;
43943             }
43944             */
43945         }
43946         if(!this.disable.format){
43947             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43948             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43949             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43950         }
43951         if(!this.disable.alignments){
43952             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43953             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43954             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43955         }
43956         if(!Roo.isSafari && !this.disable.lists){
43957             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43958             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43959         }
43960         
43961         var ans = this.editorcore.getAllAncestors();
43962         if (this.formatCombo) {
43963             
43964             
43965             var store = this.formatCombo.store;
43966             this.formatCombo.setValue("");
43967             for (var i =0; i < ans.length;i++) {
43968                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43969                     // select it..
43970                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43971                     break;
43972                 }
43973             }
43974         }
43975         
43976         
43977         
43978         // hides menus... - so this cant be on a menu...
43979         Roo.menu.MenuMgr.hideAll();
43980
43981         //this.editorsyncValue();
43982     },
43983    
43984     
43985     createFontOptions : function(){
43986         var buf = [], fs = this.fontFamilies, ff, lc;
43987         
43988         
43989         
43990         for(var i = 0, len = fs.length; i< len; i++){
43991             ff = fs[i];
43992             lc = ff.toLowerCase();
43993             buf.push(
43994                 '<option value="',lc,'" style="font-family:',ff,';"',
43995                     (this.defaultFont == lc ? ' selected="true">' : '>'),
43996                     ff,
43997                 '</option>'
43998             );
43999         }
44000         return buf.join('');
44001     },
44002     
44003     toggleSourceEdit : function(sourceEditMode){
44004         
44005         Roo.log("toolbar toogle");
44006         if(sourceEditMode === undefined){
44007             sourceEditMode = !this.sourceEditMode;
44008         }
44009         this.sourceEditMode = sourceEditMode === true;
44010         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
44011         // just toggle the button?
44012         if(btn.pressed !== this.sourceEditMode){
44013             btn.toggle(this.sourceEditMode);
44014             return;
44015         }
44016         
44017         if(sourceEditMode){
44018             Roo.log("disabling buttons");
44019             this.tb.items.each(function(item){
44020                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
44021                     item.disable();
44022                 }
44023             });
44024           
44025         }else{
44026             Roo.log("enabling buttons");
44027             if(this.editorcore.initialized){
44028                 this.tb.items.each(function(item){
44029                     item.enable();
44030                 });
44031             }
44032             
44033         }
44034         Roo.log("calling toggole on editor");
44035         // tell the editor that it's been pressed..
44036         this.editor.toggleSourceEdit(sourceEditMode);
44037        
44038     },
44039      /**
44040      * Object collection of toolbar tooltips for the buttons in the editor. The key
44041      * is the command id associated with that button and the value is a valid QuickTips object.
44042      * For example:
44043 <pre><code>
44044 {
44045     bold : {
44046         title: 'Bold (Ctrl+B)',
44047         text: 'Make the selected text bold.',
44048         cls: 'x-html-editor-tip'
44049     },
44050     italic : {
44051         title: 'Italic (Ctrl+I)',
44052         text: 'Make the selected text italic.',
44053         cls: 'x-html-editor-tip'
44054     },
44055     ...
44056 </code></pre>
44057     * @type Object
44058      */
44059     buttonTips : {
44060         bold : {
44061             title: 'Bold (Ctrl+B)',
44062             text: 'Make the selected text bold.',
44063             cls: 'x-html-editor-tip'
44064         },
44065         italic : {
44066             title: 'Italic (Ctrl+I)',
44067             text: 'Make the selected text italic.',
44068             cls: 'x-html-editor-tip'
44069         },
44070         underline : {
44071             title: 'Underline (Ctrl+U)',
44072             text: 'Underline the selected text.',
44073             cls: 'x-html-editor-tip'
44074         },
44075         increasefontsize : {
44076             title: 'Grow Text',
44077             text: 'Increase the font size.',
44078             cls: 'x-html-editor-tip'
44079         },
44080         decreasefontsize : {
44081             title: 'Shrink Text',
44082             text: 'Decrease the font size.',
44083             cls: 'x-html-editor-tip'
44084         },
44085         backcolor : {
44086             title: 'Text Highlight Color',
44087             text: 'Change the background color of the selected text.',
44088             cls: 'x-html-editor-tip'
44089         },
44090         forecolor : {
44091             title: 'Font Color',
44092             text: 'Change the color of the selected text.',
44093             cls: 'x-html-editor-tip'
44094         },
44095         justifyleft : {
44096             title: 'Align Text Left',
44097             text: 'Align text to the left.',
44098             cls: 'x-html-editor-tip'
44099         },
44100         justifycenter : {
44101             title: 'Center Text',
44102             text: 'Center text in the editor.',
44103             cls: 'x-html-editor-tip'
44104         },
44105         justifyright : {
44106             title: 'Align Text Right',
44107             text: 'Align text to the right.',
44108             cls: 'x-html-editor-tip'
44109         },
44110         insertunorderedlist : {
44111             title: 'Bullet List',
44112             text: 'Start a bulleted list.',
44113             cls: 'x-html-editor-tip'
44114         },
44115         insertorderedlist : {
44116             title: 'Numbered List',
44117             text: 'Start a numbered list.',
44118             cls: 'x-html-editor-tip'
44119         },
44120         createlink : {
44121             title: 'Hyperlink',
44122             text: 'Make the selected text a hyperlink.',
44123             cls: 'x-html-editor-tip'
44124         },
44125         sourceedit : {
44126             title: 'Source Edit',
44127             text: 'Switch to source editing mode.',
44128             cls: 'x-html-editor-tip'
44129         }
44130     },
44131     // private
44132     onDestroy : function(){
44133         if(this.rendered){
44134             
44135             this.tb.items.each(function(item){
44136                 if(item.menu){
44137                     item.menu.removeAll();
44138                     if(item.menu.el){
44139                         item.menu.el.destroy();
44140                     }
44141                 }
44142                 item.destroy();
44143             });
44144              
44145         }
44146     },
44147     onFirstFocus: function() {
44148         this.tb.items.each(function(item){
44149            item.enable();
44150         });
44151     }
44152 });
44153
44154
44155
44156
44157 // <script type="text/javascript">
44158 /*
44159  * Based on
44160  * Ext JS Library 1.1.1
44161  * Copyright(c) 2006-2007, Ext JS, LLC.
44162  *  
44163  
44164  */
44165
44166  
44167 /**
44168  * @class Roo.form.HtmlEditor.ToolbarContext
44169  * Context Toolbar
44170  * 
44171  * Usage:
44172  *
44173  new Roo.form.HtmlEditor({
44174     ....
44175     toolbars : [
44176         { xtype: 'ToolbarStandard', styles : {} }
44177         { xtype: 'ToolbarContext', disable : {} }
44178     ]
44179 })
44180
44181      
44182  * 
44183  * @config : {Object} disable List of elements to disable.. (not done yet.)
44184  * @config : {Object} styles  Map of styles available.
44185  * 
44186  */
44187
44188 Roo.form.HtmlEditor.ToolbarContext = function(config)
44189 {
44190     
44191     Roo.apply(this, config);
44192     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
44193     // dont call parent... till later.
44194     this.styles = this.styles || {};
44195 }
44196
44197  
44198
44199 Roo.form.HtmlEditor.ToolbarContext.types = {
44200     'IMG' : {
44201         width : {
44202             title: "Width",
44203             width: 40
44204         },
44205         height:  {
44206             title: "Height",
44207             width: 40
44208         },
44209         align: {
44210             title: "Align",
44211             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
44212             width : 80
44213             
44214         },
44215         border: {
44216             title: "Border",
44217             width: 40
44218         },
44219         alt: {
44220             title: "Alt",
44221             width: 120
44222         },
44223         src : {
44224             title: "Src",
44225             width: 220
44226         }
44227         
44228     },
44229     'A' : {
44230         name : {
44231             title: "Name",
44232             width: 50
44233         },
44234         target:  {
44235             title: "Target",
44236             width: 120
44237         },
44238         href:  {
44239             title: "Href",
44240             width: 220
44241         } // border?
44242         
44243     },
44244     'TABLE' : {
44245         rows : {
44246             title: "Rows",
44247             width: 20
44248         },
44249         cols : {
44250             title: "Cols",
44251             width: 20
44252         },
44253         width : {
44254             title: "Width",
44255             width: 40
44256         },
44257         height : {
44258             title: "Height",
44259             width: 40
44260         },
44261         border : {
44262             title: "Border",
44263             width: 20
44264         }
44265     },
44266     'TD' : {
44267         width : {
44268             title: "Width",
44269             width: 40
44270         },
44271         height : {
44272             title: "Height",
44273             width: 40
44274         },   
44275         align: {
44276             title: "Align",
44277             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
44278             width: 80
44279         },
44280         valign: {
44281             title: "Valign",
44282             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
44283             width: 80
44284         },
44285         colspan: {
44286             title: "Colspan",
44287             width: 20
44288             
44289         },
44290          'font-family'  : {
44291             title : "Font",
44292             style : 'fontFamily',
44293             displayField: 'display',
44294             optname : 'font-family',
44295             width: 140
44296         }
44297     },
44298     'INPUT' : {
44299         name : {
44300             title: "name",
44301             width: 120
44302         },
44303         value : {
44304             title: "Value",
44305             width: 120
44306         },
44307         width : {
44308             title: "Width",
44309             width: 40
44310         }
44311     },
44312     'LABEL' : {
44313         'for' : {
44314             title: "For",
44315             width: 120
44316         }
44317     },
44318     'TEXTAREA' : {
44319           name : {
44320             title: "name",
44321             width: 120
44322         },
44323         rows : {
44324             title: "Rows",
44325             width: 20
44326         },
44327         cols : {
44328             title: "Cols",
44329             width: 20
44330         }
44331     },
44332     'SELECT' : {
44333         name : {
44334             title: "name",
44335             width: 120
44336         },
44337         selectoptions : {
44338             title: "Options",
44339             width: 200
44340         }
44341     },
44342     
44343     // should we really allow this??
44344     // should this just be 
44345     'BODY' : {
44346         title : {
44347             title: "Title",
44348             width: 200,
44349             disabled : true
44350         }
44351     },
44352     'SPAN' : {
44353         'font-family'  : {
44354             title : "Font",
44355             style : 'fontFamily',
44356             displayField: 'display',
44357             optname : 'font-family',
44358             width: 140
44359         }
44360     },
44361     'DIV' : {
44362         'font-family'  : {
44363             title : "Font",
44364             style : 'fontFamily',
44365             displayField: 'display',
44366             optname : 'font-family',
44367             width: 140
44368         }
44369     },
44370      'P' : {
44371         'font-family'  : {
44372             title : "Font",
44373             style : 'fontFamily',
44374             displayField: 'display',
44375             optname : 'font-family',
44376             width: 140
44377         }
44378     },
44379     
44380     '*' : {
44381         // empty..
44382     }
44383
44384 };
44385
44386 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
44387 Roo.form.HtmlEditor.ToolbarContext.stores = false;
44388
44389 Roo.form.HtmlEditor.ToolbarContext.options = {
44390         'font-family'  : [ 
44391                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
44392                 [ 'Courier New', 'Courier New'],
44393                 [ 'Tahoma', 'Tahoma'],
44394                 [ 'Times New Roman,serif', 'Times'],
44395                 [ 'Verdana','Verdana' ]
44396         ]
44397 };
44398
44399 // fixme - these need to be configurable..
44400  
44401
44402 Roo.form.HtmlEditor.ToolbarContext.types
44403
44404
44405 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
44406     
44407     tb: false,
44408     
44409     rendered: false,
44410     
44411     editor : false,
44412     editorcore : false,
44413     /**
44414      * @cfg {Object} disable  List of toolbar elements to disable
44415          
44416      */
44417     disable : false,
44418     /**
44419      * @cfg {Object} styles List of styles 
44420      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
44421      *
44422      * These must be defined in the page, so they get rendered correctly..
44423      * .headline { }
44424      * TD.underline { }
44425      * 
44426      */
44427     styles : false,
44428     
44429     options: false,
44430     
44431     toolbars : false,
44432     
44433     init : function(editor)
44434     {
44435         this.editor = editor;
44436         this.editorcore = editor.editorcore ? editor.editorcore : editor;
44437         var editorcore = this.editorcore;
44438         
44439         var fid = editorcore.frameId;
44440         var etb = this;
44441         function btn(id, toggle, handler){
44442             var xid = fid + '-'+ id ;
44443             return {
44444                 id : xid,
44445                 cmd : id,
44446                 cls : 'x-btn-icon x-edit-'+id,
44447                 enableToggle:toggle !== false,
44448                 scope: editorcore, // was editor...
44449                 handler:handler||editorcore.relayBtnCmd,
44450                 clickEvent:'mousedown',
44451                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
44452                 tabIndex:-1
44453             };
44454         }
44455         // create a new element.
44456         var wdiv = editor.wrap.createChild({
44457                 tag: 'div'
44458             }, editor.wrap.dom.firstChild.nextSibling, true);
44459         
44460         // can we do this more than once??
44461         
44462          // stop form submits
44463       
44464  
44465         // disable everything...
44466         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44467         this.toolbars = {};
44468            
44469         for (var i in  ty) {
44470           
44471             this.toolbars[i] = this.buildToolbar(ty[i],i);
44472         }
44473         this.tb = this.toolbars.BODY;
44474         this.tb.el.show();
44475         this.buildFooter();
44476         this.footer.show();
44477         editor.on('hide', function( ) { this.footer.hide() }, this);
44478         editor.on('show', function( ) { this.footer.show() }, this);
44479         
44480          
44481         this.rendered = true;
44482         
44483         // the all the btns;
44484         editor.on('editorevent', this.updateToolbar, this);
44485         // other toolbars need to implement this..
44486         //editor.on('editmodechange', this.updateToolbar, this);
44487     },
44488     
44489     
44490     
44491     /**
44492      * Protected method that will not generally be called directly. It triggers
44493      * a toolbar update by reading the markup state of the current selection in the editor.
44494      *
44495      * Note you can force an update by calling on('editorevent', scope, false)
44496      */
44497     updateToolbar: function(editor,ev,sel){
44498
44499         //Roo.log(ev);
44500         // capture mouse up - this is handy for selecting images..
44501         // perhaps should go somewhere else...
44502         if(!this.editorcore.activated){
44503              this.editor.onFirstFocus();
44504             return;
44505         }
44506         
44507         
44508         
44509         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
44510         // selectNode - might want to handle IE?
44511         if (ev &&
44512             (ev.type == 'mouseup' || ev.type == 'click' ) &&
44513             ev.target && ev.target.tagName == 'IMG') {
44514             // they have click on an image...
44515             // let's see if we can change the selection...
44516             sel = ev.target;
44517          
44518               var nodeRange = sel.ownerDocument.createRange();
44519             try {
44520                 nodeRange.selectNode(sel);
44521             } catch (e) {
44522                 nodeRange.selectNodeContents(sel);
44523             }
44524             //nodeRange.collapse(true);
44525             var s = this.editorcore.win.getSelection();
44526             s.removeAllRanges();
44527             s.addRange(nodeRange);
44528         }  
44529         
44530       
44531         var updateFooter = sel ? false : true;
44532         
44533         
44534         var ans = this.editorcore.getAllAncestors();
44535         
44536         // pick
44537         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44538         
44539         if (!sel) { 
44540             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
44541             sel = sel ? sel : this.editorcore.doc.body;
44542             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
44543             
44544         }
44545         // pick a menu that exists..
44546         var tn = sel.tagName.toUpperCase();
44547         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
44548         
44549         tn = sel.tagName.toUpperCase();
44550         
44551         var lastSel = this.tb.selectedNode
44552         
44553         this.tb.selectedNode = sel;
44554         
44555         // if current menu does not match..
44556         
44557         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
44558                 
44559             this.tb.el.hide();
44560             ///console.log("show: " + tn);
44561             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
44562             this.tb.el.show();
44563             // update name
44564             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
44565             
44566             
44567             // update attributes
44568             if (this.tb.fields) {
44569                 this.tb.fields.each(function(e) {
44570                     if (e.stylename) {
44571                         e.setValue(sel.style[e.stylename]);
44572                         return;
44573                     } 
44574                    e.setValue(sel.getAttribute(e.attrname));
44575                 });
44576             }
44577             
44578             var hasStyles = false;
44579             for(var i in this.styles) {
44580                 hasStyles = true;
44581                 break;
44582             }
44583             
44584             // update styles
44585             if (hasStyles) { 
44586                 var st = this.tb.fields.item(0);
44587                 
44588                 st.store.removeAll();
44589                
44590                 
44591                 var cn = sel.className.split(/\s+/);
44592                 
44593                 var avs = [];
44594                 if (this.styles['*']) {
44595                     
44596                     Roo.each(this.styles['*'], function(v) {
44597                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44598                     });
44599                 }
44600                 if (this.styles[tn]) { 
44601                     Roo.each(this.styles[tn], function(v) {
44602                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44603                     });
44604                 }
44605                 
44606                 st.store.loadData(avs);
44607                 st.collapse();
44608                 st.setValue(cn);
44609             }
44610             // flag our selected Node.
44611             this.tb.selectedNode = sel;
44612            
44613            
44614             Roo.menu.MenuMgr.hideAll();
44615
44616         }
44617         
44618         if (!updateFooter) {
44619             //this.footDisp.dom.innerHTML = ''; 
44620             return;
44621         }
44622         // update the footer
44623         //
44624         var html = '';
44625         
44626         this.footerEls = ans.reverse();
44627         Roo.each(this.footerEls, function(a,i) {
44628             if (!a) { return; }
44629             html += html.length ? ' &gt; '  :  '';
44630             
44631             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44632             
44633         });
44634        
44635         // 
44636         var sz = this.footDisp.up('td').getSize();
44637         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44638         this.footDisp.dom.style.marginLeft = '5px';
44639         
44640         this.footDisp.dom.style.overflow = 'hidden';
44641         
44642         this.footDisp.dom.innerHTML = html;
44643             
44644         //this.editorsyncValue();
44645     },
44646      
44647     
44648    
44649        
44650     // private
44651     onDestroy : function(){
44652         if(this.rendered){
44653             
44654             this.tb.items.each(function(item){
44655                 if(item.menu){
44656                     item.menu.removeAll();
44657                     if(item.menu.el){
44658                         item.menu.el.destroy();
44659                     }
44660                 }
44661                 item.destroy();
44662             });
44663              
44664         }
44665     },
44666     onFirstFocus: function() {
44667         // need to do this for all the toolbars..
44668         this.tb.items.each(function(item){
44669            item.enable();
44670         });
44671     },
44672     buildToolbar: function(tlist, nm)
44673     {
44674         var editor = this.editor;
44675         var editorcore = this.editorcore;
44676          // create a new element.
44677         var wdiv = editor.wrap.createChild({
44678                 tag: 'div'
44679             }, editor.wrap.dom.firstChild.nextSibling, true);
44680         
44681        
44682         var tb = new Roo.Toolbar(wdiv);
44683         // add the name..
44684         
44685         tb.add(nm+ ":&nbsp;");
44686         
44687         var styles = [];
44688         for(var i in this.styles) {
44689             styles.push(i);
44690         }
44691         
44692         // styles...
44693         if (styles && styles.length) {
44694             
44695             // this needs a multi-select checkbox...
44696             tb.addField( new Roo.form.ComboBox({
44697                 store: new Roo.data.SimpleStore({
44698                     id : 'val',
44699                     fields: ['val', 'selected'],
44700                     data : [] 
44701                 }),
44702                 name : '-roo-edit-className',
44703                 attrname : 'className',
44704                 displayField: 'val',
44705                 typeAhead: false,
44706                 mode: 'local',
44707                 editable : false,
44708                 triggerAction: 'all',
44709                 emptyText:'Select Style',
44710                 selectOnFocus:true,
44711                 width: 130,
44712                 listeners : {
44713                     'select': function(c, r, i) {
44714                         // initial support only for on class per el..
44715                         tb.selectedNode.className =  r ? r.get('val') : '';
44716                         editorcore.syncValue();
44717                     }
44718                 }
44719     
44720             }));
44721         }
44722         
44723         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44724         var tbops = tbc.options;
44725         
44726         for (var i in tlist) {
44727             
44728             var item = tlist[i];
44729             tb.add(item.title + ":&nbsp;");
44730             
44731             
44732             //optname == used so you can configure the options available..
44733             var opts = item.opts ? item.opts : false;
44734             if (item.optname) {
44735                 opts = tbops[item.optname];
44736            
44737             }
44738             
44739             if (opts) {
44740                 // opts == pulldown..
44741                 tb.addField( new Roo.form.ComboBox({
44742                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44743                         id : 'val',
44744                         fields: ['val', 'display'],
44745                         data : opts  
44746                     }),
44747                     name : '-roo-edit-' + i,
44748                     attrname : i,
44749                     stylename : item.style ? item.style : false,
44750                     displayField: item.displayField ? item.displayField : 'val',
44751                     valueField :  'val',
44752                     typeAhead: false,
44753                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44754                     editable : false,
44755                     triggerAction: 'all',
44756                     emptyText:'Select',
44757                     selectOnFocus:true,
44758                     width: item.width ? item.width  : 130,
44759                     listeners : {
44760                         'select': function(c, r, i) {
44761                             if (c.stylename) {
44762                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44763                                 return;
44764                             }
44765                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44766                         }
44767                     }
44768
44769                 }));
44770                 continue;
44771                     
44772                  
44773                 
44774                 tb.addField( new Roo.form.TextField({
44775                     name: i,
44776                     width: 100,
44777                     //allowBlank:false,
44778                     value: ''
44779                 }));
44780                 continue;
44781             }
44782             tb.addField( new Roo.form.TextField({
44783                 name: '-roo-edit-' + i,
44784                 attrname : i,
44785                 
44786                 width: item.width,
44787                 //allowBlank:true,
44788                 value: '',
44789                 listeners: {
44790                     'change' : function(f, nv, ov) {
44791                         tb.selectedNode.setAttribute(f.attrname, nv);
44792                     }
44793                 }
44794             }));
44795              
44796         }
44797         
44798         var _this = this;
44799         
44800         if(nm == 'BODY'){
44801             tb.addSeparator();
44802         
44803             tb.addButton( {
44804                 text: 'Stylesheets',
44805
44806                 listeners : {
44807                     click : function ()
44808                     {
44809                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
44810                     }
44811                 }
44812             });
44813         }
44814         
44815         tb.addFill();
44816         tb.addButton( {
44817             text: 'Remove Tag',
44818     
44819             listeners : {
44820                 click : function ()
44821                 {
44822                     // remove
44823                     // undo does not work.
44824                      
44825                     var sn = tb.selectedNode;
44826                     
44827                     var pn = sn.parentNode;
44828                     
44829                     var stn =  sn.childNodes[0];
44830                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44831                     while (sn.childNodes.length) {
44832                         var node = sn.childNodes[0];
44833                         sn.removeChild(node);
44834                         //Roo.log(node);
44835                         pn.insertBefore(node, sn);
44836                         
44837                     }
44838                     pn.removeChild(sn);
44839                     var range = editorcore.createRange();
44840         
44841                     range.setStart(stn,0);
44842                     range.setEnd(en,0); //????
44843                     //range.selectNode(sel);
44844                     
44845                     
44846                     var selection = editorcore.getSelection();
44847                     selection.removeAllRanges();
44848                     selection.addRange(range);
44849                     
44850                     
44851                     
44852                     //_this.updateToolbar(null, null, pn);
44853                     _this.updateToolbar(null, null, null);
44854                     _this.footDisp.dom.innerHTML = ''; 
44855                 }
44856             }
44857             
44858                     
44859                 
44860             
44861         });
44862         
44863         
44864         tb.el.on('click', function(e){
44865             e.preventDefault(); // what does this do?
44866         });
44867         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44868         tb.el.hide();
44869         tb.name = nm;
44870         // dont need to disable them... as they will get hidden
44871         return tb;
44872          
44873         
44874     },
44875     buildFooter : function()
44876     {
44877         
44878         var fel = this.editor.wrap.createChild();
44879         this.footer = new Roo.Toolbar(fel);
44880         // toolbar has scrolly on left / right?
44881         var footDisp= new Roo.Toolbar.Fill();
44882         var _t = this;
44883         this.footer.add(
44884             {
44885                 text : '&lt;',
44886                 xtype: 'Button',
44887                 handler : function() {
44888                     _t.footDisp.scrollTo('left',0,true)
44889                 }
44890             }
44891         );
44892         this.footer.add( footDisp );
44893         this.footer.add( 
44894             {
44895                 text : '&gt;',
44896                 xtype: 'Button',
44897                 handler : function() {
44898                     // no animation..
44899                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44900                 }
44901             }
44902         );
44903         var fel = Roo.get(footDisp.el);
44904         fel.addClass('x-editor-context');
44905         this.footDispWrap = fel; 
44906         this.footDispWrap.overflow  = 'hidden';
44907         
44908         this.footDisp = fel.createChild();
44909         this.footDispWrap.on('click', this.onContextClick, this)
44910         
44911         
44912     },
44913     onContextClick : function (ev,dom)
44914     {
44915         ev.preventDefault();
44916         var  cn = dom.className;
44917         //Roo.log(cn);
44918         if (!cn.match(/x-ed-loc-/)) {
44919             return;
44920         }
44921         var n = cn.split('-').pop();
44922         var ans = this.footerEls;
44923         var sel = ans[n];
44924         
44925          // pick
44926         var range = this.editorcore.createRange();
44927         
44928         range.selectNodeContents(sel);
44929         //range.selectNode(sel);
44930         
44931         
44932         var selection = this.editorcore.getSelection();
44933         selection.removeAllRanges();
44934         selection.addRange(range);
44935         
44936         
44937         
44938         this.updateToolbar(null, null, sel);
44939         
44940         
44941     }
44942     
44943     
44944     
44945     
44946     
44947 });
44948
44949
44950
44951
44952
44953 /*
44954  * Based on:
44955  * Ext JS Library 1.1.1
44956  * Copyright(c) 2006-2007, Ext JS, LLC.
44957  *
44958  * Originally Released Under LGPL - original licence link has changed is not relivant.
44959  *
44960  * Fork - LGPL
44961  * <script type="text/javascript">
44962  */
44963  
44964 /**
44965  * @class Roo.form.BasicForm
44966  * @extends Roo.util.Observable
44967  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44968  * @constructor
44969  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44970  * @param {Object} config Configuration options
44971  */
44972 Roo.form.BasicForm = function(el, config){
44973     this.allItems = [];
44974     this.childForms = [];
44975     Roo.apply(this, config);
44976     /*
44977      * The Roo.form.Field items in this form.
44978      * @type MixedCollection
44979      */
44980      
44981      
44982     this.items = new Roo.util.MixedCollection(false, function(o){
44983         return o.id || (o.id = Roo.id());
44984     });
44985     this.addEvents({
44986         /**
44987          * @event beforeaction
44988          * Fires before any action is performed. Return false to cancel the action.
44989          * @param {Form} this
44990          * @param {Action} action The action to be performed
44991          */
44992         beforeaction: true,
44993         /**
44994          * @event actionfailed
44995          * Fires when an action fails.
44996          * @param {Form} this
44997          * @param {Action} action The action that failed
44998          */
44999         actionfailed : true,
45000         /**
45001          * @event actioncomplete
45002          * Fires when an action is completed.
45003          * @param {Form} this
45004          * @param {Action} action The action that completed
45005          */
45006         actioncomplete : true
45007     });
45008     if(el){
45009         this.initEl(el);
45010     }
45011     Roo.form.BasicForm.superclass.constructor.call(this);
45012 };
45013
45014 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
45015     /**
45016      * @cfg {String} method
45017      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
45018      */
45019     /**
45020      * @cfg {DataReader} reader
45021      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
45022      * This is optional as there is built-in support for processing JSON.
45023      */
45024     /**
45025      * @cfg {DataReader} errorReader
45026      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
45027      * This is completely optional as there is built-in support for processing JSON.
45028      */
45029     /**
45030      * @cfg {String} url
45031      * The URL to use for form actions if one isn't supplied in the action options.
45032      */
45033     /**
45034      * @cfg {Boolean} fileUpload
45035      * Set to true if this form is a file upload.
45036      */
45037      
45038     /**
45039      * @cfg {Object} baseParams
45040      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
45041      */
45042      /**
45043      
45044     /**
45045      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
45046      */
45047     timeout: 30,
45048
45049     // private
45050     activeAction : null,
45051
45052     /**
45053      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
45054      * or setValues() data instead of when the form was first created.
45055      */
45056     trackResetOnLoad : false,
45057     
45058     
45059     /**
45060      * childForms - used for multi-tab forms
45061      * @type {Array}
45062      */
45063     childForms : false,
45064     
45065     /**
45066      * allItems - full list of fields.
45067      * @type {Array}
45068      */
45069     allItems : false,
45070     
45071     /**
45072      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
45073      * element by passing it or its id or mask the form itself by passing in true.
45074      * @type Mixed
45075      */
45076     waitMsgTarget : false,
45077
45078     // private
45079     initEl : function(el){
45080         this.el = Roo.get(el);
45081         this.id = this.el.id || Roo.id();
45082         this.el.on('submit', this.onSubmit, this);
45083         this.el.addClass('x-form');
45084     },
45085
45086     // private
45087     onSubmit : function(e){
45088         e.stopEvent();
45089     },
45090
45091     /**
45092      * Returns true if client-side validation on the form is successful.
45093      * @return Boolean
45094      */
45095     isValid : function(){
45096         var valid = true;
45097         this.items.each(function(f){
45098            if(!f.validate()){
45099                valid = false;
45100            }
45101         });
45102         return valid;
45103     },
45104
45105     /**
45106      * Returns true if any fields in this form have changed since their original load.
45107      * @return Boolean
45108      */
45109     isDirty : function(){
45110         var dirty = false;
45111         this.items.each(function(f){
45112            if(f.isDirty()){
45113                dirty = true;
45114                return false;
45115            }
45116         });
45117         return dirty;
45118     },
45119
45120     /**
45121      * Performs a predefined action (submit or load) or custom actions you define on this form.
45122      * @param {String} actionName The name of the action type
45123      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
45124      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
45125      * accept other config options):
45126      * <pre>
45127 Property          Type             Description
45128 ----------------  ---------------  ----------------------------------------------------------------------------------
45129 url               String           The url for the action (defaults to the form's url)
45130 method            String           The form method to use (defaults to the form's method, or POST if not defined)
45131 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
45132 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
45133                                    validate the form on the client (defaults to false)
45134      * </pre>
45135      * @return {BasicForm} this
45136      */
45137     doAction : function(action, options){
45138         if(typeof action == 'string'){
45139             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
45140         }
45141         if(this.fireEvent('beforeaction', this, action) !== false){
45142             this.beforeAction(action);
45143             action.run.defer(100, action);
45144         }
45145         return this;
45146     },
45147
45148     /**
45149      * Shortcut to do a submit action.
45150      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45151      * @return {BasicForm} this
45152      */
45153     submit : function(options){
45154         this.doAction('submit', options);
45155         return this;
45156     },
45157
45158     /**
45159      * Shortcut to do a load action.
45160      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45161      * @return {BasicForm} this
45162      */
45163     load : function(options){
45164         this.doAction('load', options);
45165         return this;
45166     },
45167
45168     /**
45169      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
45170      * @param {Record} record The record to edit
45171      * @return {BasicForm} this
45172      */
45173     updateRecord : function(record){
45174         record.beginEdit();
45175         var fs = record.fields;
45176         fs.each(function(f){
45177             var field = this.findField(f.name);
45178             if(field){
45179                 record.set(f.name, field.getValue());
45180             }
45181         }, this);
45182         record.endEdit();
45183         return this;
45184     },
45185
45186     /**
45187      * Loads an Roo.data.Record into this form.
45188      * @param {Record} record The record to load
45189      * @return {BasicForm} this
45190      */
45191     loadRecord : function(record){
45192         this.setValues(record.data);
45193         return this;
45194     },
45195
45196     // private
45197     beforeAction : function(action){
45198         var o = action.options;
45199         
45200        
45201         if(this.waitMsgTarget === true){
45202             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
45203         }else if(this.waitMsgTarget){
45204             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
45205             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
45206         }else {
45207             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
45208         }
45209          
45210     },
45211
45212     // private
45213     afterAction : function(action, success){
45214         this.activeAction = null;
45215         var o = action.options;
45216         
45217         if(this.waitMsgTarget === true){
45218             this.el.unmask();
45219         }else if(this.waitMsgTarget){
45220             this.waitMsgTarget.unmask();
45221         }else{
45222             Roo.MessageBox.updateProgress(1);
45223             Roo.MessageBox.hide();
45224         }
45225          
45226         if(success){
45227             if(o.reset){
45228                 this.reset();
45229             }
45230             Roo.callback(o.success, o.scope, [this, action]);
45231             this.fireEvent('actioncomplete', this, action);
45232             
45233         }else{
45234             
45235             // failure condition..
45236             // we have a scenario where updates need confirming.
45237             // eg. if a locking scenario exists..
45238             // we look for { errors : { needs_confirm : true }} in the response.
45239             if (
45240                 (typeof(action.result) != 'undefined')  &&
45241                 (typeof(action.result.errors) != 'undefined')  &&
45242                 (typeof(action.result.errors.needs_confirm) != 'undefined')
45243            ){
45244                 var _t = this;
45245                 Roo.MessageBox.confirm(
45246                     "Change requires confirmation",
45247                     action.result.errorMsg,
45248                     function(r) {
45249                         if (r != 'yes') {
45250                             return;
45251                         }
45252                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
45253                     }
45254                     
45255                 );
45256                 
45257                 
45258                 
45259                 return;
45260             }
45261             
45262             Roo.callback(o.failure, o.scope, [this, action]);
45263             // show an error message if no failed handler is set..
45264             if (!this.hasListener('actionfailed')) {
45265                 Roo.MessageBox.alert("Error",
45266                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
45267                         action.result.errorMsg :
45268                         "Saving Failed, please check your entries or try again"
45269                 );
45270             }
45271             
45272             this.fireEvent('actionfailed', this, action);
45273         }
45274         
45275     },
45276
45277     /**
45278      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
45279      * @param {String} id The value to search for
45280      * @return Field
45281      */
45282     findField : function(id){
45283         var field = this.items.get(id);
45284         if(!field){
45285             this.items.each(function(f){
45286                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
45287                     field = f;
45288                     return false;
45289                 }
45290             });
45291         }
45292         return field || null;
45293     },
45294
45295     /**
45296      * Add a secondary form to this one, 
45297      * Used to provide tabbed forms. One form is primary, with hidden values 
45298      * which mirror the elements from the other forms.
45299      * 
45300      * @param {Roo.form.Form} form to add.
45301      * 
45302      */
45303     addForm : function(form)
45304     {
45305        
45306         if (this.childForms.indexOf(form) > -1) {
45307             // already added..
45308             return;
45309         }
45310         this.childForms.push(form);
45311         var n = '';
45312         Roo.each(form.allItems, function (fe) {
45313             
45314             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
45315             if (this.findField(n)) { // already added..
45316                 return;
45317             }
45318             var add = new Roo.form.Hidden({
45319                 name : n
45320             });
45321             add.render(this.el);
45322             
45323             this.add( add );
45324         }, this);
45325         
45326     },
45327     /**
45328      * Mark fields in this form invalid in bulk.
45329      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
45330      * @return {BasicForm} this
45331      */
45332     markInvalid : function(errors){
45333         if(errors instanceof Array){
45334             for(var i = 0, len = errors.length; i < len; i++){
45335                 var fieldError = errors[i];
45336                 var f = this.findField(fieldError.id);
45337                 if(f){
45338                     f.markInvalid(fieldError.msg);
45339                 }
45340             }
45341         }else{
45342             var field, id;
45343             for(id in errors){
45344                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
45345                     field.markInvalid(errors[id]);
45346                 }
45347             }
45348         }
45349         Roo.each(this.childForms || [], function (f) {
45350             f.markInvalid(errors);
45351         });
45352         
45353         return this;
45354     },
45355
45356     /**
45357      * Set values for fields in this form in bulk.
45358      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
45359      * @return {BasicForm} this
45360      */
45361     setValues : function(values){
45362         if(values instanceof Array){ // array of objects
45363             for(var i = 0, len = values.length; i < len; i++){
45364                 var v = values[i];
45365                 var f = this.findField(v.id);
45366                 if(f){
45367                     f.setValue(v.value);
45368                     if(this.trackResetOnLoad){
45369                         f.originalValue = f.getValue();
45370                     }
45371                 }
45372             }
45373         }else{ // object hash
45374             var field, id;
45375             for(id in values){
45376                 if(typeof values[id] != 'function' && (field = this.findField(id))){
45377                     
45378                     if (field.setFromData && 
45379                         field.valueField && 
45380                         field.displayField &&
45381                         // combos' with local stores can 
45382                         // be queried via setValue()
45383                         // to set their value..
45384                         (field.store && !field.store.isLocal)
45385                         ) {
45386                         // it's a combo
45387                         var sd = { };
45388                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
45389                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
45390                         field.setFromData(sd);
45391                         
45392                     } else {
45393                         field.setValue(values[id]);
45394                     }
45395                     
45396                     
45397                     if(this.trackResetOnLoad){
45398                         field.originalValue = field.getValue();
45399                     }
45400                 }
45401             }
45402         }
45403          
45404         Roo.each(this.childForms || [], function (f) {
45405             f.setValues(values);
45406         });
45407                 
45408         return this;
45409     },
45410
45411     /**
45412      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
45413      * they are returned as an array.
45414      * @param {Boolean} asString
45415      * @return {Object}
45416      */
45417     getValues : function(asString){
45418         if (this.childForms) {
45419             // copy values from the child forms
45420             Roo.each(this.childForms, function (f) {
45421                 this.setValues(f.getValues());
45422             }, this);
45423         }
45424         
45425         
45426         
45427         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
45428         if(asString === true){
45429             return fs;
45430         }
45431         return Roo.urlDecode(fs);
45432     },
45433     
45434     /**
45435      * Returns the fields in this form as an object with key/value pairs. 
45436      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
45437      * @return {Object}
45438      */
45439     getFieldValues : function(with_hidden)
45440     {
45441         if (this.childForms) {
45442             // copy values from the child forms
45443             // should this call getFieldValues - probably not as we do not currently copy
45444             // hidden fields when we generate..
45445             Roo.each(this.childForms, function (f) {
45446                 this.setValues(f.getValues());
45447             }, this);
45448         }
45449         
45450         var ret = {};
45451         this.items.each(function(f){
45452             if (!f.getName()) {
45453                 return;
45454             }
45455             var v = f.getValue();
45456             if (f.inputType =='radio') {
45457                 if (typeof(ret[f.getName()]) == 'undefined') {
45458                     ret[f.getName()] = ''; // empty..
45459                 }
45460                 
45461                 if (!f.el.dom.checked) {
45462                     return;
45463                     
45464                 }
45465                 v = f.el.dom.value;
45466                 
45467             }
45468             
45469             // not sure if this supported any more..
45470             if ((typeof(v) == 'object') && f.getRawValue) {
45471                 v = f.getRawValue() ; // dates..
45472             }
45473             // combo boxes where name != hiddenName...
45474             if (f.name != f.getName()) {
45475                 ret[f.name] = f.getRawValue();
45476             }
45477             ret[f.getName()] = v;
45478         });
45479         
45480         return ret;
45481     },
45482
45483     /**
45484      * Clears all invalid messages in this form.
45485      * @return {BasicForm} this
45486      */
45487     clearInvalid : function(){
45488         this.items.each(function(f){
45489            f.clearInvalid();
45490         });
45491         
45492         Roo.each(this.childForms || [], function (f) {
45493             f.clearInvalid();
45494         });
45495         
45496         
45497         return this;
45498     },
45499
45500     /**
45501      * Resets this form.
45502      * @return {BasicForm} this
45503      */
45504     reset : function(){
45505         this.items.each(function(f){
45506             f.reset();
45507         });
45508         
45509         Roo.each(this.childForms || [], function (f) {
45510             f.reset();
45511         });
45512        
45513         
45514         return this;
45515     },
45516
45517     /**
45518      * Add Roo.form components to this form.
45519      * @param {Field} field1
45520      * @param {Field} field2 (optional)
45521      * @param {Field} etc (optional)
45522      * @return {BasicForm} this
45523      */
45524     add : function(){
45525         this.items.addAll(Array.prototype.slice.call(arguments, 0));
45526         return this;
45527     },
45528
45529
45530     /**
45531      * Removes a field from the items collection (does NOT remove its markup).
45532      * @param {Field} field
45533      * @return {BasicForm} this
45534      */
45535     remove : function(field){
45536         this.items.remove(field);
45537         return this;
45538     },
45539
45540     /**
45541      * Looks at the fields in this form, checks them for an id attribute,
45542      * and calls applyTo on the existing dom element with that id.
45543      * @return {BasicForm} this
45544      */
45545     render : function(){
45546         this.items.each(function(f){
45547             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
45548                 f.applyTo(f.id);
45549             }
45550         });
45551         return this;
45552     },
45553
45554     /**
45555      * Calls {@link Ext#apply} for all fields in this form with the passed object.
45556      * @param {Object} values
45557      * @return {BasicForm} this
45558      */
45559     applyToFields : function(o){
45560         this.items.each(function(f){
45561            Roo.apply(f, o);
45562         });
45563         return this;
45564     },
45565
45566     /**
45567      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
45568      * @param {Object} values
45569      * @return {BasicForm} this
45570      */
45571     applyIfToFields : function(o){
45572         this.items.each(function(f){
45573            Roo.applyIf(f, o);
45574         });
45575         return this;
45576     }
45577 });
45578
45579 // back compat
45580 Roo.BasicForm = Roo.form.BasicForm;/*
45581  * Based on:
45582  * Ext JS Library 1.1.1
45583  * Copyright(c) 2006-2007, Ext JS, LLC.
45584  *
45585  * Originally Released Under LGPL - original licence link has changed is not relivant.
45586  *
45587  * Fork - LGPL
45588  * <script type="text/javascript">
45589  */
45590
45591 /**
45592  * @class Roo.form.Form
45593  * @extends Roo.form.BasicForm
45594  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
45595  * @constructor
45596  * @param {Object} config Configuration options
45597  */
45598 Roo.form.Form = function(config){
45599     var xitems =  [];
45600     if (config.items) {
45601         xitems = config.items;
45602         delete config.items;
45603     }
45604    
45605     
45606     Roo.form.Form.superclass.constructor.call(this, null, config);
45607     this.url = this.url || this.action;
45608     if(!this.root){
45609         this.root = new Roo.form.Layout(Roo.applyIf({
45610             id: Roo.id()
45611         }, config));
45612     }
45613     this.active = this.root;
45614     /**
45615      * Array of all the buttons that have been added to this form via {@link addButton}
45616      * @type Array
45617      */
45618     this.buttons = [];
45619     this.allItems = [];
45620     this.addEvents({
45621         /**
45622          * @event clientvalidation
45623          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45624          * @param {Form} this
45625          * @param {Boolean} valid true if the form has passed client-side validation
45626          */
45627         clientvalidation: true,
45628         /**
45629          * @event rendered
45630          * Fires when the form is rendered
45631          * @param {Roo.form.Form} form
45632          */
45633         rendered : true
45634     });
45635     
45636     if (this.progressUrl) {
45637             // push a hidden field onto the list of fields..
45638             this.addxtype( {
45639                     xns: Roo.form, 
45640                     xtype : 'Hidden', 
45641                     name : 'UPLOAD_IDENTIFIER' 
45642             });
45643         }
45644         
45645     
45646     Roo.each(xitems, this.addxtype, this);
45647     
45648     
45649     
45650 };
45651
45652 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45653     /**
45654      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45655      */
45656     /**
45657      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45658      */
45659     /**
45660      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45661      */
45662     buttonAlign:'center',
45663
45664     /**
45665      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45666      */
45667     minButtonWidth:75,
45668
45669     /**
45670      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45671      * This property cascades to child containers if not set.
45672      */
45673     labelAlign:'left',
45674
45675     /**
45676      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45677      * fires a looping event with that state. This is required to bind buttons to the valid
45678      * state using the config value formBind:true on the button.
45679      */
45680     monitorValid : false,
45681
45682     /**
45683      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45684      */
45685     monitorPoll : 200,
45686     
45687     /**
45688      * @cfg {String} progressUrl - Url to return progress data 
45689      */
45690     
45691     progressUrl : false,
45692   
45693     /**
45694      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45695      * fields are added and the column is closed. If no fields are passed the column remains open
45696      * until end() is called.
45697      * @param {Object} config The config to pass to the column
45698      * @param {Field} field1 (optional)
45699      * @param {Field} field2 (optional)
45700      * @param {Field} etc (optional)
45701      * @return Column The column container object
45702      */
45703     column : function(c){
45704         var col = new Roo.form.Column(c);
45705         this.start(col);
45706         if(arguments.length > 1){ // duplicate code required because of Opera
45707             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45708             this.end();
45709         }
45710         return col;
45711     },
45712
45713     /**
45714      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45715      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45716      * until end() is called.
45717      * @param {Object} config The config to pass to the fieldset
45718      * @param {Field} field1 (optional)
45719      * @param {Field} field2 (optional)
45720      * @param {Field} etc (optional)
45721      * @return FieldSet The fieldset container object
45722      */
45723     fieldset : function(c){
45724         var fs = new Roo.form.FieldSet(c);
45725         this.start(fs);
45726         if(arguments.length > 1){ // duplicate code required because of Opera
45727             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45728             this.end();
45729         }
45730         return fs;
45731     },
45732
45733     /**
45734      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45735      * fields are added and the container is closed. If no fields are passed the container remains open
45736      * until end() is called.
45737      * @param {Object} config The config to pass to the Layout
45738      * @param {Field} field1 (optional)
45739      * @param {Field} field2 (optional)
45740      * @param {Field} etc (optional)
45741      * @return Layout The container object
45742      */
45743     container : function(c){
45744         var l = new Roo.form.Layout(c);
45745         this.start(l);
45746         if(arguments.length > 1){ // duplicate code required because of Opera
45747             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45748             this.end();
45749         }
45750         return l;
45751     },
45752
45753     /**
45754      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45755      * @param {Object} container A Roo.form.Layout or subclass of Layout
45756      * @return {Form} this
45757      */
45758     start : function(c){
45759         // cascade label info
45760         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45761         this.active.stack.push(c);
45762         c.ownerCt = this.active;
45763         this.active = c;
45764         return this;
45765     },
45766
45767     /**
45768      * Closes the current open container
45769      * @return {Form} this
45770      */
45771     end : function(){
45772         if(this.active == this.root){
45773             return this;
45774         }
45775         this.active = this.active.ownerCt;
45776         return this;
45777     },
45778
45779     /**
45780      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45781      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45782      * as the label of the field.
45783      * @param {Field} field1
45784      * @param {Field} field2 (optional)
45785      * @param {Field} etc. (optional)
45786      * @return {Form} this
45787      */
45788     add : function(){
45789         this.active.stack.push.apply(this.active.stack, arguments);
45790         this.allItems.push.apply(this.allItems,arguments);
45791         var r = [];
45792         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45793             if(a[i].isFormField){
45794                 r.push(a[i]);
45795             }
45796         }
45797         if(r.length > 0){
45798             Roo.form.Form.superclass.add.apply(this, r);
45799         }
45800         return this;
45801     },
45802     
45803
45804     
45805     
45806     
45807      /**
45808      * Find any element that has been added to a form, using it's ID or name
45809      * This can include framesets, columns etc. along with regular fields..
45810      * @param {String} id - id or name to find.
45811      
45812      * @return {Element} e - or false if nothing found.
45813      */
45814     findbyId : function(id)
45815     {
45816         var ret = false;
45817         if (!id) {
45818             return ret;
45819         }
45820         Roo.each(this.allItems, function(f){
45821             if (f.id == id || f.name == id ){
45822                 ret = f;
45823                 return false;
45824             }
45825         });
45826         return ret;
45827     },
45828
45829     
45830     
45831     /**
45832      * Render this form into the passed container. This should only be called once!
45833      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45834      * @return {Form} this
45835      */
45836     render : function(ct)
45837     {
45838         
45839         
45840         
45841         ct = Roo.get(ct);
45842         var o = this.autoCreate || {
45843             tag: 'form',
45844             method : this.method || 'POST',
45845             id : this.id || Roo.id()
45846         };
45847         this.initEl(ct.createChild(o));
45848
45849         this.root.render(this.el);
45850         
45851        
45852              
45853         this.items.each(function(f){
45854             f.render('x-form-el-'+f.id);
45855         });
45856
45857         if(this.buttons.length > 0){
45858             // tables are required to maintain order and for correct IE layout
45859             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45860                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45861                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45862             }}, null, true);
45863             var tr = tb.getElementsByTagName('tr')[0];
45864             for(var i = 0, len = this.buttons.length; i < len; i++) {
45865                 var b = this.buttons[i];
45866                 var td = document.createElement('td');
45867                 td.className = 'x-form-btn-td';
45868                 b.render(tr.appendChild(td));
45869             }
45870         }
45871         if(this.monitorValid){ // initialize after render
45872             this.startMonitoring();
45873         }
45874         this.fireEvent('rendered', this);
45875         return this;
45876     },
45877
45878     /**
45879      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45880      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45881      * object or a valid Roo.DomHelper element config
45882      * @param {Function} handler The function called when the button is clicked
45883      * @param {Object} scope (optional) The scope of the handler function
45884      * @return {Roo.Button}
45885      */
45886     addButton : function(config, handler, scope){
45887         var bc = {
45888             handler: handler,
45889             scope: scope,
45890             minWidth: this.minButtonWidth,
45891             hideParent:true
45892         };
45893         if(typeof config == "string"){
45894             bc.text = config;
45895         }else{
45896             Roo.apply(bc, config);
45897         }
45898         var btn = new Roo.Button(null, bc);
45899         this.buttons.push(btn);
45900         return btn;
45901     },
45902
45903      /**
45904      * Adds a series of form elements (using the xtype property as the factory method.
45905      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45906      * @param {Object} config 
45907      */
45908     
45909     addxtype : function()
45910     {
45911         var ar = Array.prototype.slice.call(arguments, 0);
45912         var ret = false;
45913         for(var i = 0; i < ar.length; i++) {
45914             if (!ar[i]) {
45915                 continue; // skip -- if this happends something invalid got sent, we 
45916                 // should ignore it, as basically that interface element will not show up
45917                 // and that should be pretty obvious!!
45918             }
45919             
45920             if (Roo.form[ar[i].xtype]) {
45921                 ar[i].form = this;
45922                 var fe = Roo.factory(ar[i], Roo.form);
45923                 if (!ret) {
45924                     ret = fe;
45925                 }
45926                 fe.form = this;
45927                 if (fe.store) {
45928                     fe.store.form = this;
45929                 }
45930                 if (fe.isLayout) {  
45931                          
45932                     this.start(fe);
45933                     this.allItems.push(fe);
45934                     if (fe.items && fe.addxtype) {
45935                         fe.addxtype.apply(fe, fe.items);
45936                         delete fe.items;
45937                     }
45938                      this.end();
45939                     continue;
45940                 }
45941                 
45942                 
45943                  
45944                 this.add(fe);
45945               //  console.log('adding ' + ar[i].xtype);
45946             }
45947             if (ar[i].xtype == 'Button') {  
45948                 //console.log('adding button');
45949                 //console.log(ar[i]);
45950                 this.addButton(ar[i]);
45951                 this.allItems.push(fe);
45952                 continue;
45953             }
45954             
45955             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45956                 alert('end is not supported on xtype any more, use items');
45957             //    this.end();
45958             //    //console.log('adding end');
45959             }
45960             
45961         }
45962         return ret;
45963     },
45964     
45965     /**
45966      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45967      * option "monitorValid"
45968      */
45969     startMonitoring : function(){
45970         if(!this.bound){
45971             this.bound = true;
45972             Roo.TaskMgr.start({
45973                 run : this.bindHandler,
45974                 interval : this.monitorPoll || 200,
45975                 scope: this
45976             });
45977         }
45978     },
45979
45980     /**
45981      * Stops monitoring of the valid state of this form
45982      */
45983     stopMonitoring : function(){
45984         this.bound = false;
45985     },
45986
45987     // private
45988     bindHandler : function(){
45989         if(!this.bound){
45990             return false; // stops binding
45991         }
45992         var valid = true;
45993         this.items.each(function(f){
45994             if(!f.isValid(true)){
45995                 valid = false;
45996                 return false;
45997             }
45998         });
45999         for(var i = 0, len = this.buttons.length; i < len; i++){
46000             var btn = this.buttons[i];
46001             if(btn.formBind === true && btn.disabled === valid){
46002                 btn.setDisabled(!valid);
46003             }
46004         }
46005         this.fireEvent('clientvalidation', this, valid);
46006     }
46007     
46008     
46009     
46010     
46011     
46012     
46013     
46014     
46015 });
46016
46017
46018 // back compat
46019 Roo.Form = Roo.form.Form;
46020 /*
46021  * Based on:
46022  * Ext JS Library 1.1.1
46023  * Copyright(c) 2006-2007, Ext JS, LLC.
46024  *
46025  * Originally Released Under LGPL - original licence link has changed is not relivant.
46026  *
46027  * Fork - LGPL
46028  * <script type="text/javascript">
46029  */
46030
46031 // as we use this in bootstrap.
46032 Roo.namespace('Roo.form');
46033  /**
46034  * @class Roo.form.Action
46035  * Internal Class used to handle form actions
46036  * @constructor
46037  * @param {Roo.form.BasicForm} el The form element or its id
46038  * @param {Object} config Configuration options
46039  */
46040
46041  
46042  
46043 // define the action interface
46044 Roo.form.Action = function(form, options){
46045     this.form = form;
46046     this.options = options || {};
46047 };
46048 /**
46049  * Client Validation Failed
46050  * @const 
46051  */
46052 Roo.form.Action.CLIENT_INVALID = 'client';
46053 /**
46054  * Server Validation Failed
46055  * @const 
46056  */
46057 Roo.form.Action.SERVER_INVALID = 'server';
46058  /**
46059  * Connect to Server Failed
46060  * @const 
46061  */
46062 Roo.form.Action.CONNECT_FAILURE = 'connect';
46063 /**
46064  * Reading Data from Server Failed
46065  * @const 
46066  */
46067 Roo.form.Action.LOAD_FAILURE = 'load';
46068
46069 Roo.form.Action.prototype = {
46070     type : 'default',
46071     failureType : undefined,
46072     response : undefined,
46073     result : undefined,
46074
46075     // interface method
46076     run : function(options){
46077
46078     },
46079
46080     // interface method
46081     success : function(response){
46082
46083     },
46084
46085     // interface method
46086     handleResponse : function(response){
46087
46088     },
46089
46090     // default connection failure
46091     failure : function(response){
46092         
46093         this.response = response;
46094         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46095         this.form.afterAction(this, false);
46096     },
46097
46098     processResponse : function(response){
46099         this.response = response;
46100         if(!response.responseText){
46101             return true;
46102         }
46103         this.result = this.handleResponse(response);
46104         return this.result;
46105     },
46106
46107     // utility functions used internally
46108     getUrl : function(appendParams){
46109         var url = this.options.url || this.form.url || this.form.el.dom.action;
46110         if(appendParams){
46111             var p = this.getParams();
46112             if(p){
46113                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
46114             }
46115         }
46116         return url;
46117     },
46118
46119     getMethod : function(){
46120         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
46121     },
46122
46123     getParams : function(){
46124         var bp = this.form.baseParams;
46125         var p = this.options.params;
46126         if(p){
46127             if(typeof p == "object"){
46128                 p = Roo.urlEncode(Roo.applyIf(p, bp));
46129             }else if(typeof p == 'string' && bp){
46130                 p += '&' + Roo.urlEncode(bp);
46131             }
46132         }else if(bp){
46133             p = Roo.urlEncode(bp);
46134         }
46135         return p;
46136     },
46137
46138     createCallback : function(){
46139         return {
46140             success: this.success,
46141             failure: this.failure,
46142             scope: this,
46143             timeout: (this.form.timeout*1000),
46144             upload: this.form.fileUpload ? this.success : undefined
46145         };
46146     }
46147 };
46148
46149 Roo.form.Action.Submit = function(form, options){
46150     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
46151 };
46152
46153 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
46154     type : 'submit',
46155
46156     haveProgress : false,
46157     uploadComplete : false,
46158     
46159     // uploadProgress indicator.
46160     uploadProgress : function()
46161     {
46162         if (!this.form.progressUrl) {
46163             return;
46164         }
46165         
46166         if (!this.haveProgress) {
46167             Roo.MessageBox.progress("Uploading", "Uploading");
46168         }
46169         if (this.uploadComplete) {
46170            Roo.MessageBox.hide();
46171            return;
46172         }
46173         
46174         this.haveProgress = true;
46175    
46176         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
46177         
46178         var c = new Roo.data.Connection();
46179         c.request({
46180             url : this.form.progressUrl,
46181             params: {
46182                 id : uid
46183             },
46184             method: 'GET',
46185             success : function(req){
46186                //console.log(data);
46187                 var rdata = false;
46188                 var edata;
46189                 try  {
46190                    rdata = Roo.decode(req.responseText)
46191                 } catch (e) {
46192                     Roo.log("Invalid data from server..");
46193                     Roo.log(edata);
46194                     return;
46195                 }
46196                 if (!rdata || !rdata.success) {
46197                     Roo.log(rdata);
46198                     Roo.MessageBox.alert(Roo.encode(rdata));
46199                     return;
46200                 }
46201                 var data = rdata.data;
46202                 
46203                 if (this.uploadComplete) {
46204                    Roo.MessageBox.hide();
46205                    return;
46206                 }
46207                    
46208                 if (data){
46209                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
46210                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
46211                     );
46212                 }
46213                 this.uploadProgress.defer(2000,this);
46214             },
46215        
46216             failure: function(data) {
46217                 Roo.log('progress url failed ');
46218                 Roo.log(data);
46219             },
46220             scope : this
46221         });
46222            
46223     },
46224     
46225     
46226     run : function()
46227     {
46228         // run get Values on the form, so it syncs any secondary forms.
46229         this.form.getValues();
46230         
46231         var o = this.options;
46232         var method = this.getMethod();
46233         var isPost = method == 'POST';
46234         if(o.clientValidation === false || this.form.isValid()){
46235             
46236             if (this.form.progressUrl) {
46237                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
46238                     (new Date() * 1) + '' + Math.random());
46239                     
46240             } 
46241             
46242             
46243             Roo.Ajax.request(Roo.apply(this.createCallback(), {
46244                 form:this.form.el.dom,
46245                 url:this.getUrl(!isPost),
46246                 method: method,
46247                 params:isPost ? this.getParams() : null,
46248                 isUpload: this.form.fileUpload
46249             }));
46250             
46251             this.uploadProgress();
46252
46253         }else if (o.clientValidation !== false){ // client validation failed
46254             this.failureType = Roo.form.Action.CLIENT_INVALID;
46255             this.form.afterAction(this, false);
46256         }
46257     },
46258
46259     success : function(response)
46260     {
46261         this.uploadComplete= true;
46262         if (this.haveProgress) {
46263             Roo.MessageBox.hide();
46264         }
46265         
46266         
46267         var result = this.processResponse(response);
46268         if(result === true || result.success){
46269             this.form.afterAction(this, true);
46270             return;
46271         }
46272         if(result.errors){
46273             this.form.markInvalid(result.errors);
46274             this.failureType = Roo.form.Action.SERVER_INVALID;
46275         }
46276         this.form.afterAction(this, false);
46277     },
46278     failure : function(response)
46279     {
46280         this.uploadComplete= true;
46281         if (this.haveProgress) {
46282             Roo.MessageBox.hide();
46283         }
46284         
46285         this.response = response;
46286         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46287         this.form.afterAction(this, false);
46288     },
46289     
46290     handleResponse : function(response){
46291         if(this.form.errorReader){
46292             var rs = this.form.errorReader.read(response);
46293             var errors = [];
46294             if(rs.records){
46295                 for(var i = 0, len = rs.records.length; i < len; i++) {
46296                     var r = rs.records[i];
46297                     errors[i] = r.data;
46298                 }
46299             }
46300             if(errors.length < 1){
46301                 errors = null;
46302             }
46303             return {
46304                 success : rs.success,
46305                 errors : errors
46306             };
46307         }
46308         var ret = false;
46309         try {
46310             ret = Roo.decode(response.responseText);
46311         } catch (e) {
46312             ret = {
46313                 success: false,
46314                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
46315                 errors : []
46316             };
46317         }
46318         return ret;
46319         
46320     }
46321 });
46322
46323
46324 Roo.form.Action.Load = function(form, options){
46325     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
46326     this.reader = this.form.reader;
46327 };
46328
46329 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
46330     type : 'load',
46331
46332     run : function(){
46333         
46334         Roo.Ajax.request(Roo.apply(
46335                 this.createCallback(), {
46336                     method:this.getMethod(),
46337                     url:this.getUrl(false),
46338                     params:this.getParams()
46339         }));
46340     },
46341
46342     success : function(response){
46343         
46344         var result = this.processResponse(response);
46345         if(result === true || !result.success || !result.data){
46346             this.failureType = Roo.form.Action.LOAD_FAILURE;
46347             this.form.afterAction(this, false);
46348             return;
46349         }
46350         this.form.clearInvalid();
46351         this.form.setValues(result.data);
46352         this.form.afterAction(this, true);
46353     },
46354
46355     handleResponse : function(response){
46356         if(this.form.reader){
46357             var rs = this.form.reader.read(response);
46358             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
46359             return {
46360                 success : rs.success,
46361                 data : data
46362             };
46363         }
46364         return Roo.decode(response.responseText);
46365     }
46366 });
46367
46368 Roo.form.Action.ACTION_TYPES = {
46369     'load' : Roo.form.Action.Load,
46370     'submit' : Roo.form.Action.Submit
46371 };/*
46372  * Based on:
46373  * Ext JS Library 1.1.1
46374  * Copyright(c) 2006-2007, Ext JS, LLC.
46375  *
46376  * Originally Released Under LGPL - original licence link has changed is not relivant.
46377  *
46378  * Fork - LGPL
46379  * <script type="text/javascript">
46380  */
46381  
46382 /**
46383  * @class Roo.form.Layout
46384  * @extends Roo.Component
46385  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
46386  * @constructor
46387  * @param {Object} config Configuration options
46388  */
46389 Roo.form.Layout = function(config){
46390     var xitems = [];
46391     if (config.items) {
46392         xitems = config.items;
46393         delete config.items;
46394     }
46395     Roo.form.Layout.superclass.constructor.call(this, config);
46396     this.stack = [];
46397     Roo.each(xitems, this.addxtype, this);
46398      
46399 };
46400
46401 Roo.extend(Roo.form.Layout, Roo.Component, {
46402     /**
46403      * @cfg {String/Object} autoCreate
46404      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
46405      */
46406     /**
46407      * @cfg {String/Object/Function} style
46408      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
46409      * a function which returns such a specification.
46410      */
46411     /**
46412      * @cfg {String} labelAlign
46413      * Valid values are "left," "top" and "right" (defaults to "left")
46414      */
46415     /**
46416      * @cfg {Number} labelWidth
46417      * Fixed width in pixels of all field labels (defaults to undefined)
46418      */
46419     /**
46420      * @cfg {Boolean} clear
46421      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
46422      */
46423     clear : true,
46424     /**
46425      * @cfg {String} labelSeparator
46426      * The separator to use after field labels (defaults to ':')
46427      */
46428     labelSeparator : ':',
46429     /**
46430      * @cfg {Boolean} hideLabels
46431      * True to suppress the display of field labels in this layout (defaults to false)
46432      */
46433     hideLabels : false,
46434
46435     // private
46436     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
46437     
46438     isLayout : true,
46439     
46440     // private
46441     onRender : function(ct, position){
46442         if(this.el){ // from markup
46443             this.el = Roo.get(this.el);
46444         }else {  // generate
46445             var cfg = this.getAutoCreate();
46446             this.el = ct.createChild(cfg, position);
46447         }
46448         if(this.style){
46449             this.el.applyStyles(this.style);
46450         }
46451         if(this.labelAlign){
46452             this.el.addClass('x-form-label-'+this.labelAlign);
46453         }
46454         if(this.hideLabels){
46455             this.labelStyle = "display:none";
46456             this.elementStyle = "padding-left:0;";
46457         }else{
46458             if(typeof this.labelWidth == 'number'){
46459                 this.labelStyle = "width:"+this.labelWidth+"px;";
46460                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
46461             }
46462             if(this.labelAlign == 'top'){
46463                 this.labelStyle = "width:auto;";
46464                 this.elementStyle = "padding-left:0;";
46465             }
46466         }
46467         var stack = this.stack;
46468         var slen = stack.length;
46469         if(slen > 0){
46470             if(!this.fieldTpl){
46471                 var t = new Roo.Template(
46472                     '<div class="x-form-item {5}">',
46473                         '<label for="{0}" style="{2}">{1}{4}</label>',
46474                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46475                         '</div>',
46476                     '</div><div class="x-form-clear-left"></div>'
46477                 );
46478                 t.disableFormats = true;
46479                 t.compile();
46480                 Roo.form.Layout.prototype.fieldTpl = t;
46481             }
46482             for(var i = 0; i < slen; i++) {
46483                 if(stack[i].isFormField){
46484                     this.renderField(stack[i]);
46485                 }else{
46486                     this.renderComponent(stack[i]);
46487                 }
46488             }
46489         }
46490         if(this.clear){
46491             this.el.createChild({cls:'x-form-clear'});
46492         }
46493     },
46494
46495     // private
46496     renderField : function(f){
46497         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
46498                f.id, //0
46499                f.fieldLabel, //1
46500                f.labelStyle||this.labelStyle||'', //2
46501                this.elementStyle||'', //3
46502                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
46503                f.itemCls||this.itemCls||''  //5
46504        ], true).getPrevSibling());
46505     },
46506
46507     // private
46508     renderComponent : function(c){
46509         c.render(c.isLayout ? this.el : this.el.createChild());    
46510     },
46511     /**
46512      * Adds a object form elements (using the xtype property as the factory method.)
46513      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
46514      * @param {Object} config 
46515      */
46516     addxtype : function(o)
46517     {
46518         // create the lement.
46519         o.form = this.form;
46520         var fe = Roo.factory(o, Roo.form);
46521         this.form.allItems.push(fe);
46522         this.stack.push(fe);
46523         
46524         if (fe.isFormField) {
46525             this.form.items.add(fe);
46526         }
46527          
46528         return fe;
46529     }
46530 });
46531
46532 /**
46533  * @class Roo.form.Column
46534  * @extends Roo.form.Layout
46535  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
46536  * @constructor
46537  * @param {Object} config Configuration options
46538  */
46539 Roo.form.Column = function(config){
46540     Roo.form.Column.superclass.constructor.call(this, config);
46541 };
46542
46543 Roo.extend(Roo.form.Column, Roo.form.Layout, {
46544     /**
46545      * @cfg {Number/String} width
46546      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46547      */
46548     /**
46549      * @cfg {String/Object} autoCreate
46550      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
46551      */
46552
46553     // private
46554     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
46555
46556     // private
46557     onRender : function(ct, position){
46558         Roo.form.Column.superclass.onRender.call(this, ct, position);
46559         if(this.width){
46560             this.el.setWidth(this.width);
46561         }
46562     }
46563 });
46564
46565
46566 /**
46567  * @class Roo.form.Row
46568  * @extends Roo.form.Layout
46569  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
46570  * @constructor
46571  * @param {Object} config Configuration options
46572  */
46573
46574  
46575 Roo.form.Row = function(config){
46576     Roo.form.Row.superclass.constructor.call(this, config);
46577 };
46578  
46579 Roo.extend(Roo.form.Row, Roo.form.Layout, {
46580       /**
46581      * @cfg {Number/String} width
46582      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46583      */
46584     /**
46585      * @cfg {Number/String} height
46586      * The fixed height of the column in pixels or CSS value (defaults to "auto")
46587      */
46588     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
46589     
46590     padWidth : 20,
46591     // private
46592     onRender : function(ct, position){
46593         //console.log('row render');
46594         if(!this.rowTpl){
46595             var t = new Roo.Template(
46596                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
46597                     '<label for="{0}" style="{2}">{1}{4}</label>',
46598                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46599                     '</div>',
46600                 '</div>'
46601             );
46602             t.disableFormats = true;
46603             t.compile();
46604             Roo.form.Layout.prototype.rowTpl = t;
46605         }
46606         this.fieldTpl = this.rowTpl;
46607         
46608         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46609         var labelWidth = 100;
46610         
46611         if ((this.labelAlign != 'top')) {
46612             if (typeof this.labelWidth == 'number') {
46613                 labelWidth = this.labelWidth
46614             }
46615             this.padWidth =  20 + labelWidth;
46616             
46617         }
46618         
46619         Roo.form.Column.superclass.onRender.call(this, ct, position);
46620         if(this.width){
46621             this.el.setWidth(this.width);
46622         }
46623         if(this.height){
46624             this.el.setHeight(this.height);
46625         }
46626     },
46627     
46628     // private
46629     renderField : function(f){
46630         f.fieldEl = this.fieldTpl.append(this.el, [
46631                f.id, f.fieldLabel,
46632                f.labelStyle||this.labelStyle||'',
46633                this.elementStyle||'',
46634                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46635                f.itemCls||this.itemCls||'',
46636                f.width ? f.width + this.padWidth : 160 + this.padWidth
46637        ],true);
46638     }
46639 });
46640  
46641
46642 /**
46643  * @class Roo.form.FieldSet
46644  * @extends Roo.form.Layout
46645  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46646  * @constructor
46647  * @param {Object} config Configuration options
46648  */
46649 Roo.form.FieldSet = function(config){
46650     Roo.form.FieldSet.superclass.constructor.call(this, config);
46651 };
46652
46653 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46654     /**
46655      * @cfg {String} legend
46656      * The text to display as the legend for the FieldSet (defaults to '')
46657      */
46658     /**
46659      * @cfg {String/Object} autoCreate
46660      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46661      */
46662
46663     // private
46664     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46665
46666     // private
46667     onRender : function(ct, position){
46668         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46669         if(this.legend){
46670             this.setLegend(this.legend);
46671         }
46672     },
46673
46674     // private
46675     setLegend : function(text){
46676         if(this.rendered){
46677             this.el.child('legend').update(text);
46678         }
46679     }
46680 });/*
46681  * Based on:
46682  * Ext JS Library 1.1.1
46683  * Copyright(c) 2006-2007, Ext JS, LLC.
46684  *
46685  * Originally Released Under LGPL - original licence link has changed is not relivant.
46686  *
46687  * Fork - LGPL
46688  * <script type="text/javascript">
46689  */
46690 /**
46691  * @class Roo.form.VTypes
46692  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46693  * @singleton
46694  */
46695 Roo.form.VTypes = function(){
46696     // closure these in so they are only created once.
46697     var alpha = /^[a-zA-Z_]+$/;
46698     var alphanum = /^[a-zA-Z0-9_]+$/;
46699     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46700     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46701
46702     // All these messages and functions are configurable
46703     return {
46704         /**
46705          * The function used to validate email addresses
46706          * @param {String} value The email address
46707          */
46708         'email' : function(v){
46709             return email.test(v);
46710         },
46711         /**
46712          * The error text to display when the email validation function returns false
46713          * @type String
46714          */
46715         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46716         /**
46717          * The keystroke filter mask to be applied on email input
46718          * @type RegExp
46719          */
46720         'emailMask' : /[a-z0-9_\.\-@]/i,
46721
46722         /**
46723          * The function used to validate URLs
46724          * @param {String} value The URL
46725          */
46726         'url' : function(v){
46727             return url.test(v);
46728         },
46729         /**
46730          * The error text to display when the url validation function returns false
46731          * @type String
46732          */
46733         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46734         
46735         /**
46736          * The function used to validate alpha values
46737          * @param {String} value The value
46738          */
46739         'alpha' : function(v){
46740             return alpha.test(v);
46741         },
46742         /**
46743          * The error text to display when the alpha validation function returns false
46744          * @type String
46745          */
46746         'alphaText' : 'This field should only contain letters and _',
46747         /**
46748          * The keystroke filter mask to be applied on alpha input
46749          * @type RegExp
46750          */
46751         'alphaMask' : /[a-z_]/i,
46752
46753         /**
46754          * The function used to validate alphanumeric values
46755          * @param {String} value The value
46756          */
46757         'alphanum' : function(v){
46758             return alphanum.test(v);
46759         },
46760         /**
46761          * The error text to display when the alphanumeric validation function returns false
46762          * @type String
46763          */
46764         'alphanumText' : 'This field should only contain letters, numbers and _',
46765         /**
46766          * The keystroke filter mask to be applied on alphanumeric input
46767          * @type RegExp
46768          */
46769         'alphanumMask' : /[a-z0-9_]/i
46770     };
46771 }();//<script type="text/javascript">
46772
46773 /**
46774  * @class Roo.form.FCKeditor
46775  * @extends Roo.form.TextArea
46776  * Wrapper around the FCKEditor http://www.fckeditor.net
46777  * @constructor
46778  * Creates a new FCKeditor
46779  * @param {Object} config Configuration options
46780  */
46781 Roo.form.FCKeditor = function(config){
46782     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46783     this.addEvents({
46784          /**
46785          * @event editorinit
46786          * Fired when the editor is initialized - you can add extra handlers here..
46787          * @param {FCKeditor} this
46788          * @param {Object} the FCK object.
46789          */
46790         editorinit : true
46791     });
46792     
46793     
46794 };
46795 Roo.form.FCKeditor.editors = { };
46796 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46797 {
46798     //defaultAutoCreate : {
46799     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46800     //},
46801     // private
46802     /**
46803      * @cfg {Object} fck options - see fck manual for details.
46804      */
46805     fckconfig : false,
46806     
46807     /**
46808      * @cfg {Object} fck toolbar set (Basic or Default)
46809      */
46810     toolbarSet : 'Basic',
46811     /**
46812      * @cfg {Object} fck BasePath
46813      */ 
46814     basePath : '/fckeditor/',
46815     
46816     
46817     frame : false,
46818     
46819     value : '',
46820     
46821    
46822     onRender : function(ct, position)
46823     {
46824         if(!this.el){
46825             this.defaultAutoCreate = {
46826                 tag: "textarea",
46827                 style:"width:300px;height:60px;",
46828                 autocomplete: "new-password"
46829             };
46830         }
46831         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46832         /*
46833         if(this.grow){
46834             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46835             if(this.preventScrollbars){
46836                 this.el.setStyle("overflow", "hidden");
46837             }
46838             this.el.setHeight(this.growMin);
46839         }
46840         */
46841         //console.log('onrender' + this.getId() );
46842         Roo.form.FCKeditor.editors[this.getId()] = this;
46843          
46844
46845         this.replaceTextarea() ;
46846         
46847     },
46848     
46849     getEditor : function() {
46850         return this.fckEditor;
46851     },
46852     /**
46853      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46854      * @param {Mixed} value The value to set
46855      */
46856     
46857     
46858     setValue : function(value)
46859     {
46860         //console.log('setValue: ' + value);
46861         
46862         if(typeof(value) == 'undefined') { // not sure why this is happending...
46863             return;
46864         }
46865         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46866         
46867         //if(!this.el || !this.getEditor()) {
46868         //    this.value = value;
46869             //this.setValue.defer(100,this,[value]);    
46870         //    return;
46871         //} 
46872         
46873         if(!this.getEditor()) {
46874             return;
46875         }
46876         
46877         this.getEditor().SetData(value);
46878         
46879         //
46880
46881     },
46882
46883     /**
46884      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46885      * @return {Mixed} value The field value
46886      */
46887     getValue : function()
46888     {
46889         
46890         if (this.frame && this.frame.dom.style.display == 'none') {
46891             return Roo.form.FCKeditor.superclass.getValue.call(this);
46892         }
46893         
46894         if(!this.el || !this.getEditor()) {
46895            
46896            // this.getValue.defer(100,this); 
46897             return this.value;
46898         }
46899        
46900         
46901         var value=this.getEditor().GetData();
46902         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46903         return Roo.form.FCKeditor.superclass.getValue.call(this);
46904         
46905
46906     },
46907
46908     /**
46909      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46910      * @return {Mixed} value The field value
46911      */
46912     getRawValue : function()
46913     {
46914         if (this.frame && this.frame.dom.style.display == 'none') {
46915             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46916         }
46917         
46918         if(!this.el || !this.getEditor()) {
46919             //this.getRawValue.defer(100,this); 
46920             return this.value;
46921             return;
46922         }
46923         
46924         
46925         
46926         var value=this.getEditor().GetData();
46927         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46928         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46929          
46930     },
46931     
46932     setSize : function(w,h) {
46933         
46934         
46935         
46936         //if (this.frame && this.frame.dom.style.display == 'none') {
46937         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46938         //    return;
46939         //}
46940         //if(!this.el || !this.getEditor()) {
46941         //    this.setSize.defer(100,this, [w,h]); 
46942         //    return;
46943         //}
46944         
46945         
46946         
46947         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46948         
46949         this.frame.dom.setAttribute('width', w);
46950         this.frame.dom.setAttribute('height', h);
46951         this.frame.setSize(w,h);
46952         
46953     },
46954     
46955     toggleSourceEdit : function(value) {
46956         
46957       
46958          
46959         this.el.dom.style.display = value ? '' : 'none';
46960         this.frame.dom.style.display = value ?  'none' : '';
46961         
46962     },
46963     
46964     
46965     focus: function(tag)
46966     {
46967         if (this.frame.dom.style.display == 'none') {
46968             return Roo.form.FCKeditor.superclass.focus.call(this);
46969         }
46970         if(!this.el || !this.getEditor()) {
46971             this.focus.defer(100,this, [tag]); 
46972             return;
46973         }
46974         
46975         
46976         
46977         
46978         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46979         this.getEditor().Focus();
46980         if (tgs.length) {
46981             if (!this.getEditor().Selection.GetSelection()) {
46982                 this.focus.defer(100,this, [tag]); 
46983                 return;
46984             }
46985             
46986             
46987             var r = this.getEditor().EditorDocument.createRange();
46988             r.setStart(tgs[0],0);
46989             r.setEnd(tgs[0],0);
46990             this.getEditor().Selection.GetSelection().removeAllRanges();
46991             this.getEditor().Selection.GetSelection().addRange(r);
46992             this.getEditor().Focus();
46993         }
46994         
46995     },
46996     
46997     
46998     
46999     replaceTextarea : function()
47000     {
47001         if ( document.getElementById( this.getId() + '___Frame' ) )
47002             return ;
47003         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
47004         //{
47005             // We must check the elements firstly using the Id and then the name.
47006         var oTextarea = document.getElementById( this.getId() );
47007         
47008         var colElementsByName = document.getElementsByName( this.getId() ) ;
47009          
47010         oTextarea.style.display = 'none' ;
47011
47012         if ( oTextarea.tabIndex ) {            
47013             this.TabIndex = oTextarea.tabIndex ;
47014         }
47015         
47016         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
47017         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
47018         this.frame = Roo.get(this.getId() + '___Frame')
47019     },
47020     
47021     _getConfigHtml : function()
47022     {
47023         var sConfig = '' ;
47024
47025         for ( var o in this.fckconfig ) {
47026             sConfig += sConfig.length > 0  ? '&amp;' : '';
47027             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
47028         }
47029
47030         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
47031     },
47032     
47033     
47034     _getIFrameHtml : function()
47035     {
47036         var sFile = 'fckeditor.html' ;
47037         /* no idea what this is about..
47038         try
47039         {
47040             if ( (/fcksource=true/i).test( window.top.location.search ) )
47041                 sFile = 'fckeditor.original.html' ;
47042         }
47043         catch (e) { 
47044         */
47045
47046         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
47047         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
47048         
47049         
47050         var html = '<iframe id="' + this.getId() +
47051             '___Frame" src="' + sLink +
47052             '" width="' + this.width +
47053             '" height="' + this.height + '"' +
47054             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
47055             ' frameborder="0" scrolling="no"></iframe>' ;
47056
47057         return html ;
47058     },
47059     
47060     _insertHtmlBefore : function( html, element )
47061     {
47062         if ( element.insertAdjacentHTML )       {
47063             // IE
47064             element.insertAdjacentHTML( 'beforeBegin', html ) ;
47065         } else { // Gecko
47066             var oRange = document.createRange() ;
47067             oRange.setStartBefore( element ) ;
47068             var oFragment = oRange.createContextualFragment( html );
47069             element.parentNode.insertBefore( oFragment, element ) ;
47070         }
47071     }
47072     
47073     
47074   
47075     
47076     
47077     
47078     
47079
47080 });
47081
47082 //Roo.reg('fckeditor', Roo.form.FCKeditor);
47083
47084 function FCKeditor_OnComplete(editorInstance){
47085     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
47086     f.fckEditor = editorInstance;
47087     //console.log("loaded");
47088     f.fireEvent('editorinit', f, editorInstance);
47089
47090   
47091
47092  
47093
47094
47095
47096
47097
47098
47099
47100
47101
47102
47103
47104
47105
47106
47107
47108 //<script type="text/javascript">
47109 /**
47110  * @class Roo.form.GridField
47111  * @extends Roo.form.Field
47112  * Embed a grid (or editable grid into a form)
47113  * STATUS ALPHA
47114  * 
47115  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
47116  * it needs 
47117  * xgrid.store = Roo.data.Store
47118  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
47119  * xgrid.store.reader = Roo.data.JsonReader 
47120  * 
47121  * 
47122  * @constructor
47123  * Creates a new GridField
47124  * @param {Object} config Configuration options
47125  */
47126 Roo.form.GridField = function(config){
47127     Roo.form.GridField.superclass.constructor.call(this, config);
47128      
47129 };
47130
47131 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
47132     /**
47133      * @cfg {Number} width  - used to restrict width of grid..
47134      */
47135     width : 100,
47136     /**
47137      * @cfg {Number} height - used to restrict height of grid..
47138      */
47139     height : 50,
47140      /**
47141      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
47142          * 
47143          *}
47144      */
47145     xgrid : false, 
47146     /**
47147      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47148      * {tag: "input", type: "checkbox", autocomplete: "off"})
47149      */
47150    // defaultAutoCreate : { tag: 'div' },
47151     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
47152     /**
47153      * @cfg {String} addTitle Text to include for adding a title.
47154      */
47155     addTitle : false,
47156     //
47157     onResize : function(){
47158         Roo.form.Field.superclass.onResize.apply(this, arguments);
47159     },
47160
47161     initEvents : function(){
47162         // Roo.form.Checkbox.superclass.initEvents.call(this);
47163         // has no events...
47164        
47165     },
47166
47167
47168     getResizeEl : function(){
47169         return this.wrap;
47170     },
47171
47172     getPositionEl : function(){
47173         return this.wrap;
47174     },
47175
47176     // private
47177     onRender : function(ct, position){
47178         
47179         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
47180         var style = this.style;
47181         delete this.style;
47182         
47183         Roo.form.GridField.superclass.onRender.call(this, ct, position);
47184         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
47185         this.viewEl = this.wrap.createChild({ tag: 'div' });
47186         if (style) {
47187             this.viewEl.applyStyles(style);
47188         }
47189         if (this.width) {
47190             this.viewEl.setWidth(this.width);
47191         }
47192         if (this.height) {
47193             this.viewEl.setHeight(this.height);
47194         }
47195         //if(this.inputValue !== undefined){
47196         //this.setValue(this.value);
47197         
47198         
47199         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
47200         
47201         
47202         this.grid.render();
47203         this.grid.getDataSource().on('remove', this.refreshValue, this);
47204         this.grid.getDataSource().on('update', this.refreshValue, this);
47205         this.grid.on('afteredit', this.refreshValue, this);
47206  
47207     },
47208      
47209     
47210     /**
47211      * Sets the value of the item. 
47212      * @param {String} either an object  or a string..
47213      */
47214     setValue : function(v){
47215         //this.value = v;
47216         v = v || []; // empty set..
47217         // this does not seem smart - it really only affects memoryproxy grids..
47218         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
47219             var ds = this.grid.getDataSource();
47220             // assumes a json reader..
47221             var data = {}
47222             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
47223             ds.loadData( data);
47224         }
47225         // clear selection so it does not get stale.
47226         if (this.grid.sm) { 
47227             this.grid.sm.clearSelections();
47228         }
47229         
47230         Roo.form.GridField.superclass.setValue.call(this, v);
47231         this.refreshValue();
47232         // should load data in the grid really....
47233     },
47234     
47235     // private
47236     refreshValue: function() {
47237          var val = [];
47238         this.grid.getDataSource().each(function(r) {
47239             val.push(r.data);
47240         });
47241         this.el.dom.value = Roo.encode(val);
47242     }
47243     
47244      
47245     
47246     
47247 });/*
47248  * Based on:
47249  * Ext JS Library 1.1.1
47250  * Copyright(c) 2006-2007, Ext JS, LLC.
47251  *
47252  * Originally Released Under LGPL - original licence link has changed is not relivant.
47253  *
47254  * Fork - LGPL
47255  * <script type="text/javascript">
47256  */
47257 /**
47258  * @class Roo.form.DisplayField
47259  * @extends Roo.form.Field
47260  * A generic Field to display non-editable data.
47261  * @constructor
47262  * Creates a new Display Field item.
47263  * @param {Object} config Configuration options
47264  */
47265 Roo.form.DisplayField = function(config){
47266     Roo.form.DisplayField.superclass.constructor.call(this, config);
47267     
47268 };
47269
47270 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
47271     inputType:      'hidden',
47272     allowBlank:     true,
47273     readOnly:         true,
47274     
47275  
47276     /**
47277      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47278      */
47279     focusClass : undefined,
47280     /**
47281      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47282      */
47283     fieldClass: 'x-form-field',
47284     
47285      /**
47286      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
47287      */
47288     valueRenderer: undefined,
47289     
47290     width: 100,
47291     /**
47292      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47293      * {tag: "input", type: "checkbox", autocomplete: "off"})
47294      */
47295      
47296  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
47297
47298     onResize : function(){
47299         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
47300         
47301     },
47302
47303     initEvents : function(){
47304         // Roo.form.Checkbox.superclass.initEvents.call(this);
47305         // has no events...
47306        
47307     },
47308
47309
47310     getResizeEl : function(){
47311         return this.wrap;
47312     },
47313
47314     getPositionEl : function(){
47315         return this.wrap;
47316     },
47317
47318     // private
47319     onRender : function(ct, position){
47320         
47321         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
47322         //if(this.inputValue !== undefined){
47323         this.wrap = this.el.wrap();
47324         
47325         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
47326         
47327         if (this.bodyStyle) {
47328             this.viewEl.applyStyles(this.bodyStyle);
47329         }
47330         //this.viewEl.setStyle('padding', '2px');
47331         
47332         this.setValue(this.value);
47333         
47334     },
47335 /*
47336     // private
47337     initValue : Roo.emptyFn,
47338
47339   */
47340
47341         // private
47342     onClick : function(){
47343         
47344     },
47345
47346     /**
47347      * Sets the checked state of the checkbox.
47348      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
47349      */
47350     setValue : function(v){
47351         this.value = v;
47352         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
47353         // this might be called before we have a dom element..
47354         if (!this.viewEl) {
47355             return;
47356         }
47357         this.viewEl.dom.innerHTML = html;
47358         Roo.form.DisplayField.superclass.setValue.call(this, v);
47359
47360     }
47361 });/*
47362  * 
47363  * Licence- LGPL
47364  * 
47365  */
47366
47367 /**
47368  * @class Roo.form.DayPicker
47369  * @extends Roo.form.Field
47370  * A Day picker show [M] [T] [W] ....
47371  * @constructor
47372  * Creates a new Day Picker
47373  * @param {Object} config Configuration options
47374  */
47375 Roo.form.DayPicker= function(config){
47376     Roo.form.DayPicker.superclass.constructor.call(this, config);
47377      
47378 };
47379
47380 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
47381     /**
47382      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47383      */
47384     focusClass : undefined,
47385     /**
47386      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47387      */
47388     fieldClass: "x-form-field",
47389    
47390     /**
47391      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47392      * {tag: "input", type: "checkbox", autocomplete: "off"})
47393      */
47394     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
47395     
47396    
47397     actionMode : 'viewEl', 
47398     //
47399     // private
47400  
47401     inputType : 'hidden',
47402     
47403      
47404     inputElement: false, // real input element?
47405     basedOn: false, // ????
47406     
47407     isFormField: true, // not sure where this is needed!!!!
47408
47409     onResize : function(){
47410         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
47411         if(!this.boxLabel){
47412             this.el.alignTo(this.wrap, 'c-c');
47413         }
47414     },
47415
47416     initEvents : function(){
47417         Roo.form.Checkbox.superclass.initEvents.call(this);
47418         this.el.on("click", this.onClick,  this);
47419         this.el.on("change", this.onClick,  this);
47420     },
47421
47422
47423     getResizeEl : function(){
47424         return this.wrap;
47425     },
47426
47427     getPositionEl : function(){
47428         return this.wrap;
47429     },
47430
47431     
47432     // private
47433     onRender : function(ct, position){
47434         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
47435        
47436         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
47437         
47438         var r1 = '<table><tr>';
47439         var r2 = '<tr class="x-form-daypick-icons">';
47440         for (var i=0; i < 7; i++) {
47441             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
47442             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
47443         }
47444         
47445         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
47446         viewEl.select('img').on('click', this.onClick, this);
47447         this.viewEl = viewEl;   
47448         
47449         
47450         // this will not work on Chrome!!!
47451         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
47452         this.el.on('propertychange', this.setFromHidden,  this);  //ie
47453         
47454         
47455           
47456
47457     },
47458
47459     // private
47460     initValue : Roo.emptyFn,
47461
47462     /**
47463      * Returns the checked state of the checkbox.
47464      * @return {Boolean} True if checked, else false
47465      */
47466     getValue : function(){
47467         return this.el.dom.value;
47468         
47469     },
47470
47471         // private
47472     onClick : function(e){ 
47473         //this.setChecked(!this.checked);
47474         Roo.get(e.target).toggleClass('x-menu-item-checked');
47475         this.refreshValue();
47476         //if(this.el.dom.checked != this.checked){
47477         //    this.setValue(this.el.dom.checked);
47478        // }
47479     },
47480     
47481     // private
47482     refreshValue : function()
47483     {
47484         var val = '';
47485         this.viewEl.select('img',true).each(function(e,i,n)  {
47486             val += e.is(".x-menu-item-checked") ? String(n) : '';
47487         });
47488         this.setValue(val, true);
47489     },
47490
47491     /**
47492      * Sets the checked state of the checkbox.
47493      * On is always based on a string comparison between inputValue and the param.
47494      * @param {Boolean/String} value - the value to set 
47495      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47496      */
47497     setValue : function(v,suppressEvent){
47498         if (!this.el.dom) {
47499             return;
47500         }
47501         var old = this.el.dom.value ;
47502         this.el.dom.value = v;
47503         if (suppressEvent) {
47504             return ;
47505         }
47506          
47507         // update display..
47508         this.viewEl.select('img',true).each(function(e,i,n)  {
47509             
47510             var on = e.is(".x-menu-item-checked");
47511             var newv = v.indexOf(String(n)) > -1;
47512             if (on != newv) {
47513                 e.toggleClass('x-menu-item-checked');
47514             }
47515             
47516         });
47517         
47518         
47519         this.fireEvent('change', this, v, old);
47520         
47521         
47522     },
47523    
47524     // handle setting of hidden value by some other method!!?!?
47525     setFromHidden: function()
47526     {
47527         if(!this.el){
47528             return;
47529         }
47530         //console.log("SET FROM HIDDEN");
47531         //alert('setFrom hidden');
47532         this.setValue(this.el.dom.value);
47533     },
47534     
47535     onDestroy : function()
47536     {
47537         if(this.viewEl){
47538             Roo.get(this.viewEl).remove();
47539         }
47540          
47541         Roo.form.DayPicker.superclass.onDestroy.call(this);
47542     }
47543
47544 });/*
47545  * RooJS Library 1.1.1
47546  * Copyright(c) 2008-2011  Alan Knowles
47547  *
47548  * License - LGPL
47549  */
47550  
47551
47552 /**
47553  * @class Roo.form.ComboCheck
47554  * @extends Roo.form.ComboBox
47555  * A combobox for multiple select items.
47556  *
47557  * FIXME - could do with a reset button..
47558  * 
47559  * @constructor
47560  * Create a new ComboCheck
47561  * @param {Object} config Configuration options
47562  */
47563 Roo.form.ComboCheck = function(config){
47564     Roo.form.ComboCheck.superclass.constructor.call(this, config);
47565     // should verify some data...
47566     // like
47567     // hiddenName = required..
47568     // displayField = required
47569     // valudField == required
47570     var req= [ 'hiddenName', 'displayField', 'valueField' ];
47571     var _t = this;
47572     Roo.each(req, function(e) {
47573         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
47574             throw "Roo.form.ComboCheck : missing value for: " + e;
47575         }
47576     });
47577     
47578     
47579 };
47580
47581 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
47582      
47583      
47584     editable : false,
47585      
47586     selectedClass: 'x-menu-item-checked', 
47587     
47588     // private
47589     onRender : function(ct, position){
47590         var _t = this;
47591         
47592         
47593         
47594         if(!this.tpl){
47595             var cls = 'x-combo-list';
47596
47597             
47598             this.tpl =  new Roo.Template({
47599                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
47600                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47601                    '<span>{' + this.displayField + '}</span>' +
47602                     '</div>' 
47603                 
47604             });
47605         }
47606  
47607         
47608         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47609         this.view.singleSelect = false;
47610         this.view.multiSelect = true;
47611         this.view.toggleSelect = true;
47612         this.pageTb.add(new Roo.Toolbar.Fill(), {
47613             
47614             text: 'Done',
47615             handler: function()
47616             {
47617                 _t.collapse();
47618             }
47619         });
47620     },
47621     
47622     onViewOver : function(e, t){
47623         // do nothing...
47624         return;
47625         
47626     },
47627     
47628     onViewClick : function(doFocus,index){
47629         return;
47630         
47631     },
47632     select: function () {
47633         //Roo.log("SELECT CALLED");
47634     },
47635      
47636     selectByValue : function(xv, scrollIntoView){
47637         var ar = this.getValueArray();
47638         var sels = [];
47639         
47640         Roo.each(ar, function(v) {
47641             if(v === undefined || v === null){
47642                 return;
47643             }
47644             var r = this.findRecord(this.valueField, v);
47645             if(r){
47646                 sels.push(this.store.indexOf(r))
47647                 
47648             }
47649         },this);
47650         this.view.select(sels);
47651         return false;
47652     },
47653     
47654     
47655     
47656     onSelect : function(record, index){
47657        // Roo.log("onselect Called");
47658        // this is only called by the clear button now..
47659         this.view.clearSelections();
47660         this.setValue('[]');
47661         if (this.value != this.valueBefore) {
47662             this.fireEvent('change', this, this.value, this.valueBefore);
47663             this.valueBefore = this.value;
47664         }
47665     },
47666     getValueArray : function()
47667     {
47668         var ar = [] ;
47669         
47670         try {
47671             //Roo.log(this.value);
47672             if (typeof(this.value) == 'undefined') {
47673                 return [];
47674             }
47675             var ar = Roo.decode(this.value);
47676             return  ar instanceof Array ? ar : []; //?? valid?
47677             
47678         } catch(e) {
47679             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47680             return [];
47681         }
47682          
47683     },
47684     expand : function ()
47685     {
47686         
47687         Roo.form.ComboCheck.superclass.expand.call(this);
47688         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47689         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47690         
47691
47692     },
47693     
47694     collapse : function(){
47695         Roo.form.ComboCheck.superclass.collapse.call(this);
47696         var sl = this.view.getSelectedIndexes();
47697         var st = this.store;
47698         var nv = [];
47699         var tv = [];
47700         var r;
47701         Roo.each(sl, function(i) {
47702             r = st.getAt(i);
47703             nv.push(r.get(this.valueField));
47704         },this);
47705         this.setValue(Roo.encode(nv));
47706         if (this.value != this.valueBefore) {
47707
47708             this.fireEvent('change', this, this.value, this.valueBefore);
47709             this.valueBefore = this.value;
47710         }
47711         
47712     },
47713     
47714     setValue : function(v){
47715         // Roo.log(v);
47716         this.value = v;
47717         
47718         var vals = this.getValueArray();
47719         var tv = [];
47720         Roo.each(vals, function(k) {
47721             var r = this.findRecord(this.valueField, k);
47722             if(r){
47723                 tv.push(r.data[this.displayField]);
47724             }else if(this.valueNotFoundText !== undefined){
47725                 tv.push( this.valueNotFoundText );
47726             }
47727         },this);
47728        // Roo.log(tv);
47729         
47730         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47731         this.hiddenField.value = v;
47732         this.value = v;
47733     }
47734     
47735 });/*
47736  * Based on:
47737  * Ext JS Library 1.1.1
47738  * Copyright(c) 2006-2007, Ext JS, LLC.
47739  *
47740  * Originally Released Under LGPL - original licence link has changed is not relivant.
47741  *
47742  * Fork - LGPL
47743  * <script type="text/javascript">
47744  */
47745  
47746 /**
47747  * @class Roo.form.Signature
47748  * @extends Roo.form.Field
47749  * Signature field.  
47750  * @constructor
47751  * 
47752  * @param {Object} config Configuration options
47753  */
47754
47755 Roo.form.Signature = function(config){
47756     Roo.form.Signature.superclass.constructor.call(this, config);
47757     
47758     this.addEvents({// not in used??
47759          /**
47760          * @event confirm
47761          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47762              * @param {Roo.form.Signature} combo This combo box
47763              */
47764         'confirm' : true,
47765         /**
47766          * @event reset
47767          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47768              * @param {Roo.form.ComboBox} combo This combo box
47769              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47770              */
47771         'reset' : true
47772     });
47773 };
47774
47775 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47776     /**
47777      * @cfg {Object} labels Label to use when rendering a form.
47778      * defaults to 
47779      * labels : { 
47780      *      clear : "Clear",
47781      *      confirm : "Confirm"
47782      *  }
47783      */
47784     labels : { 
47785         clear : "Clear",
47786         confirm : "Confirm"
47787     },
47788     /**
47789      * @cfg {Number} width The signature panel width (defaults to 300)
47790      */
47791     width: 300,
47792     /**
47793      * @cfg {Number} height The signature panel height (defaults to 100)
47794      */
47795     height : 100,
47796     /**
47797      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47798      */
47799     allowBlank : false,
47800     
47801     //private
47802     // {Object} signPanel The signature SVG panel element (defaults to {})
47803     signPanel : {},
47804     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47805     isMouseDown : false,
47806     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47807     isConfirmed : false,
47808     // {String} signatureTmp SVG mapping string (defaults to empty string)
47809     signatureTmp : '',
47810     
47811     
47812     defaultAutoCreate : { // modified by initCompnoent..
47813         tag: "input",
47814         type:"hidden"
47815     },
47816
47817     // private
47818     onRender : function(ct, position){
47819         
47820         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47821         
47822         this.wrap = this.el.wrap({
47823             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47824         });
47825         
47826         this.createToolbar(this);
47827         this.signPanel = this.wrap.createChild({
47828                 tag: 'div',
47829                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47830             }, this.el
47831         );
47832             
47833         this.svgID = Roo.id();
47834         this.svgEl = this.signPanel.createChild({
47835               xmlns : 'http://www.w3.org/2000/svg',
47836               tag : 'svg',
47837               id : this.svgID + "-svg",
47838               width: this.width,
47839               height: this.height,
47840               viewBox: '0 0 '+this.width+' '+this.height,
47841               cn : [
47842                 {
47843                     tag: "rect",
47844                     id: this.svgID + "-svg-r",
47845                     width: this.width,
47846                     height: this.height,
47847                     fill: "#ffa"
47848                 },
47849                 {
47850                     tag: "line",
47851                     id: this.svgID + "-svg-l",
47852                     x1: "0", // start
47853                     y1: (this.height*0.8), // start set the line in 80% of height
47854                     x2: this.width, // end
47855                     y2: (this.height*0.8), // end set the line in 80% of height
47856                     'stroke': "#666",
47857                     'stroke-width': "1",
47858                     'stroke-dasharray': "3",
47859                     'shape-rendering': "crispEdges",
47860                     'pointer-events': "none"
47861                 },
47862                 {
47863                     tag: "path",
47864                     id: this.svgID + "-svg-p",
47865                     'stroke': "navy",
47866                     'stroke-width': "3",
47867                     'fill': "none",
47868                     'pointer-events': 'none'
47869                 }
47870               ]
47871         });
47872         this.createSVG();
47873         this.svgBox = this.svgEl.dom.getScreenCTM();
47874     },
47875     createSVG : function(){ 
47876         var svg = this.signPanel;
47877         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47878         var t = this;
47879
47880         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47881         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47882         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47883         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47884         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47885         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47886         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47887         
47888     },
47889     isTouchEvent : function(e){
47890         return e.type.match(/^touch/);
47891     },
47892     getCoords : function (e) {
47893         var pt    = this.svgEl.dom.createSVGPoint();
47894         pt.x = e.clientX; 
47895         pt.y = e.clientY;
47896         if (this.isTouchEvent(e)) {
47897             pt.x =  e.targetTouches[0].clientX 
47898             pt.y = e.targetTouches[0].clientY;
47899         }
47900         var a = this.svgEl.dom.getScreenCTM();
47901         var b = a.inverse();
47902         var mx = pt.matrixTransform(b);
47903         return mx.x + ',' + mx.y;
47904     },
47905     //mouse event headler 
47906     down : function (e) {
47907         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47908         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47909         
47910         this.isMouseDown = true;
47911         
47912         e.preventDefault();
47913     },
47914     move : function (e) {
47915         if (this.isMouseDown) {
47916             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
47917             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
47918         }
47919         
47920         e.preventDefault();
47921     },
47922     up : function (e) {
47923         this.isMouseDown = false;
47924         var sp = this.signatureTmp.split(' ');
47925         
47926         if(sp.length > 1){
47927             if(!sp[sp.length-2].match(/^L/)){
47928                 sp.pop();
47929                 sp.pop();
47930                 sp.push("");
47931                 this.signatureTmp = sp.join(" ");
47932             }
47933         }
47934         if(this.getValue() != this.signatureTmp){
47935             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47936             this.isConfirmed = false;
47937         }
47938         e.preventDefault();
47939     },
47940     
47941     /**
47942      * Protected method that will not generally be called directly. It
47943      * is called when the editor creates its toolbar. Override this method if you need to
47944      * add custom toolbar buttons.
47945      * @param {HtmlEditor} editor
47946      */
47947     createToolbar : function(editor){
47948          function btn(id, toggle, handler){
47949             var xid = fid + '-'+ id ;
47950             return {
47951                 id : xid,
47952                 cmd : id,
47953                 cls : 'x-btn-icon x-edit-'+id,
47954                 enableToggle:toggle !== false,
47955                 scope: editor, // was editor...
47956                 handler:handler||editor.relayBtnCmd,
47957                 clickEvent:'mousedown',
47958                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47959                 tabIndex:-1
47960             };
47961         }
47962         
47963         
47964         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47965         this.tb = tb;
47966         this.tb.add(
47967            {
47968                 cls : ' x-signature-btn x-signature-'+id,
47969                 scope: editor, // was editor...
47970                 handler: this.reset,
47971                 clickEvent:'mousedown',
47972                 text: this.labels.clear
47973             },
47974             {
47975                  xtype : 'Fill',
47976                  xns: Roo.Toolbar
47977             }, 
47978             {
47979                 cls : '  x-signature-btn x-signature-'+id,
47980                 scope: editor, // was editor...
47981                 handler: this.confirmHandler,
47982                 clickEvent:'mousedown',
47983                 text: this.labels.confirm
47984             }
47985         );
47986     
47987     },
47988     //public
47989     /**
47990      * when user is clicked confirm then show this image.....
47991      * 
47992      * @return {String} Image Data URI
47993      */
47994     getImageDataURI : function(){
47995         var svg = this.svgEl.dom.parentNode.innerHTML;
47996         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
47997         return src; 
47998     },
47999     /**
48000      * 
48001      * @return {Boolean} this.isConfirmed
48002      */
48003     getConfirmed : function(){
48004         return this.isConfirmed;
48005     },
48006     /**
48007      * 
48008      * @return {Number} this.width
48009      */
48010     getWidth : function(){
48011         return this.width;
48012     },
48013     /**
48014      * 
48015      * @return {Number} this.height
48016      */
48017     getHeight : function(){
48018         return this.height;
48019     },
48020     // private
48021     getSignature : function(){
48022         return this.signatureTmp;
48023     },
48024     // private
48025     reset : function(){
48026         this.signatureTmp = '';
48027         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48028         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
48029         this.isConfirmed = false;
48030         Roo.form.Signature.superclass.reset.call(this);
48031     },
48032     setSignature : function(s){
48033         this.signatureTmp = s;
48034         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48035         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
48036         this.setValue(s);
48037         this.isConfirmed = false;
48038         Roo.form.Signature.superclass.reset.call(this);
48039     }, 
48040     test : function(){
48041 //        Roo.log(this.signPanel.dom.contentWindow.up())
48042     },
48043     //private
48044     setConfirmed : function(){
48045         
48046         
48047         
48048 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
48049     },
48050     // private
48051     confirmHandler : function(){
48052         if(!this.getSignature()){
48053             return;
48054         }
48055         
48056         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
48057         this.setValue(this.getSignature());
48058         this.isConfirmed = true;
48059         
48060         this.fireEvent('confirm', this);
48061     },
48062     // private
48063     // Subclasses should provide the validation implementation by overriding this
48064     validateValue : function(value){
48065         if(this.allowBlank){
48066             return true;
48067         }
48068         
48069         if(this.isConfirmed){
48070             return true;
48071         }
48072         return false;
48073     }
48074 });/*
48075  * Based on:
48076  * Ext JS Library 1.1.1
48077  * Copyright(c) 2006-2007, Ext JS, LLC.
48078  *
48079  * Originally Released Under LGPL - original licence link has changed is not relivant.
48080  *
48081  * Fork - LGPL
48082  * <script type="text/javascript">
48083  */
48084  
48085
48086 /**
48087  * @class Roo.form.ComboBox
48088  * @extends Roo.form.TriggerField
48089  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
48090  * @constructor
48091  * Create a new ComboBox.
48092  * @param {Object} config Configuration options
48093  */
48094 Roo.form.Select = function(config){
48095     Roo.form.Select.superclass.constructor.call(this, config);
48096      
48097 };
48098
48099 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
48100     /**
48101      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
48102      */
48103     /**
48104      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
48105      * rendering into an Roo.Editor, defaults to false)
48106      */
48107     /**
48108      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
48109      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
48110      */
48111     /**
48112      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
48113      */
48114     /**
48115      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
48116      * the dropdown list (defaults to undefined, with no header element)
48117      */
48118
48119      /**
48120      * @cfg {String/Roo.Template} tpl The template to use to render the output
48121      */
48122      
48123     // private
48124     defaultAutoCreate : {tag: "select"  },
48125     /**
48126      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
48127      */
48128     listWidth: undefined,
48129     /**
48130      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
48131      * mode = 'remote' or 'text' if mode = 'local')
48132      */
48133     displayField: undefined,
48134     /**
48135      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
48136      * mode = 'remote' or 'value' if mode = 'local'). 
48137      * Note: use of a valueField requires the user make a selection
48138      * in order for a value to be mapped.
48139      */
48140     valueField: undefined,
48141     
48142     
48143     /**
48144      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
48145      * field's data value (defaults to the underlying DOM element's name)
48146      */
48147     hiddenName: undefined,
48148     /**
48149      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
48150      */
48151     listClass: '',
48152     /**
48153      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
48154      */
48155     selectedClass: 'x-combo-selected',
48156     /**
48157      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
48158      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
48159      * which displays a downward arrow icon).
48160      */
48161     triggerClass : 'x-form-arrow-trigger',
48162     /**
48163      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
48164      */
48165     shadow:'sides',
48166     /**
48167      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
48168      * anchor positions (defaults to 'tl-bl')
48169      */
48170     listAlign: 'tl-bl?',
48171     /**
48172      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
48173      */
48174     maxHeight: 300,
48175     /**
48176      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
48177      * query specified by the allQuery config option (defaults to 'query')
48178      */
48179     triggerAction: 'query',
48180     /**
48181      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
48182      * (defaults to 4, does not apply if editable = false)
48183      */
48184     minChars : 4,
48185     /**
48186      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
48187      * delay (typeAheadDelay) if it matches a known value (defaults to false)
48188      */
48189     typeAhead: false,
48190     /**
48191      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
48192      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
48193      */
48194     queryDelay: 500,
48195     /**
48196      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
48197      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
48198      */
48199     pageSize: 0,
48200     /**
48201      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
48202      * when editable = true (defaults to false)
48203      */
48204     selectOnFocus:false,
48205     /**
48206      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
48207      */
48208     queryParam: 'query',
48209     /**
48210      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
48211      * when mode = 'remote' (defaults to 'Loading...')
48212      */
48213     loadingText: 'Loading...',
48214     /**
48215      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
48216      */
48217     resizable: false,
48218     /**
48219      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
48220      */
48221     handleHeight : 8,
48222     /**
48223      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
48224      * traditional select (defaults to true)
48225      */
48226     editable: true,
48227     /**
48228      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
48229      */
48230     allQuery: '',
48231     /**
48232      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
48233      */
48234     mode: 'remote',
48235     /**
48236      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
48237      * listWidth has a higher value)
48238      */
48239     minListWidth : 70,
48240     /**
48241      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
48242      * allow the user to set arbitrary text into the field (defaults to false)
48243      */
48244     forceSelection:false,
48245     /**
48246      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
48247      * if typeAhead = true (defaults to 250)
48248      */
48249     typeAheadDelay : 250,
48250     /**
48251      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
48252      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
48253      */
48254     valueNotFoundText : undefined,
48255     
48256     /**
48257      * @cfg {String} defaultValue The value displayed after loading the store.
48258      */
48259     defaultValue: '',
48260     
48261     /**
48262      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
48263      */
48264     blockFocus : false,
48265     
48266     /**
48267      * @cfg {Boolean} disableClear Disable showing of clear button.
48268      */
48269     disableClear : false,
48270     /**
48271      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
48272      */
48273     alwaysQuery : false,
48274     
48275     //private
48276     addicon : false,
48277     editicon: false,
48278     
48279     // element that contains real text value.. (when hidden is used..)
48280      
48281     // private
48282     onRender : function(ct, position){
48283         Roo.form.Field.prototype.onRender.call(this, ct, position);
48284         
48285         if(this.store){
48286             this.store.on('beforeload', this.onBeforeLoad, this);
48287             this.store.on('load', this.onLoad, this);
48288             this.store.on('loadexception', this.onLoadException, this);
48289             this.store.load({});
48290         }
48291         
48292         
48293         
48294     },
48295
48296     // private
48297     initEvents : function(){
48298         //Roo.form.ComboBox.superclass.initEvents.call(this);
48299  
48300     },
48301
48302     onDestroy : function(){
48303        
48304         if(this.store){
48305             this.store.un('beforeload', this.onBeforeLoad, this);
48306             this.store.un('load', this.onLoad, this);
48307             this.store.un('loadexception', this.onLoadException, this);
48308         }
48309         //Roo.form.ComboBox.superclass.onDestroy.call(this);
48310     },
48311
48312     // private
48313     fireKey : function(e){
48314         if(e.isNavKeyPress() && !this.list.isVisible()){
48315             this.fireEvent("specialkey", this, e);
48316         }
48317     },
48318
48319     // private
48320     onResize: function(w, h){
48321         
48322         return; 
48323     
48324         
48325     },
48326
48327     /**
48328      * Allow or prevent the user from directly editing the field text.  If false is passed,
48329      * the user will only be able to select from the items defined in the dropdown list.  This method
48330      * is the runtime equivalent of setting the 'editable' config option at config time.
48331      * @param {Boolean} value True to allow the user to directly edit the field text
48332      */
48333     setEditable : function(value){
48334          
48335     },
48336
48337     // private
48338     onBeforeLoad : function(){
48339         
48340         Roo.log("Select before load");
48341         return;
48342     
48343         this.innerList.update(this.loadingText ?
48344                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
48345         //this.restrictHeight();
48346         this.selectedIndex = -1;
48347     },
48348
48349     // private
48350     onLoad : function(){
48351
48352     
48353         var dom = this.el.dom;
48354         dom.innerHTML = '';
48355          var od = dom.ownerDocument;
48356          
48357         if (this.emptyText) {
48358             var op = od.createElement('option');
48359             op.setAttribute('value', '');
48360             op.innerHTML = String.format('{0}', this.emptyText);
48361             dom.appendChild(op);
48362         }
48363         if(this.store.getCount() > 0){
48364            
48365             var vf = this.valueField;
48366             var df = this.displayField;
48367             this.store.data.each(function(r) {
48368                 // which colmsn to use... testing - cdoe / title..
48369                 var op = od.createElement('option');
48370                 op.setAttribute('value', r.data[vf]);
48371                 op.innerHTML = String.format('{0}', r.data[df]);
48372                 dom.appendChild(op);
48373             });
48374             if (typeof(this.defaultValue != 'undefined')) {
48375                 this.setValue(this.defaultValue);
48376             }
48377             
48378              
48379         }else{
48380             //this.onEmptyResults();
48381         }
48382         //this.el.focus();
48383     },
48384     // private
48385     onLoadException : function()
48386     {
48387         dom.innerHTML = '';
48388             
48389         Roo.log("Select on load exception");
48390         return;
48391     
48392         this.collapse();
48393         Roo.log(this.store.reader.jsonData);
48394         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
48395             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
48396         }
48397         
48398         
48399     },
48400     // private
48401     onTypeAhead : function(){
48402          
48403     },
48404
48405     // private
48406     onSelect : function(record, index){
48407         Roo.log('on select?');
48408         return;
48409         if(this.fireEvent('beforeselect', this, record, index) !== false){
48410             this.setFromData(index > -1 ? record.data : false);
48411             this.collapse();
48412             this.fireEvent('select', this, record, index);
48413         }
48414     },
48415
48416     /**
48417      * Returns the currently selected field value or empty string if no value is set.
48418      * @return {String} value The selected value
48419      */
48420     getValue : function(){
48421         var dom = this.el.dom;
48422         this.value = dom.options[dom.selectedIndex].value;
48423         return this.value;
48424         
48425     },
48426
48427     /**
48428      * Clears any text/value currently set in the field
48429      */
48430     clearValue : function(){
48431         this.value = '';
48432         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
48433         
48434     },
48435
48436     /**
48437      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
48438      * will be displayed in the field.  If the value does not match the data value of an existing item,
48439      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
48440      * Otherwise the field will be blank (although the value will still be set).
48441      * @param {String} value The value to match
48442      */
48443     setValue : function(v){
48444         var d = this.el.dom;
48445         for (var i =0; i < d.options.length;i++) {
48446             if (v == d.options[i].value) {
48447                 d.selectedIndex = i;
48448                 this.value = v;
48449                 return;
48450             }
48451         }
48452         this.clearValue();
48453     },
48454     /**
48455      * @property {Object} the last set data for the element
48456      */
48457     
48458     lastData : false,
48459     /**
48460      * Sets the value of the field based on a object which is related to the record format for the store.
48461      * @param {Object} value the value to set as. or false on reset?
48462      */
48463     setFromData : function(o){
48464         Roo.log('setfrom data?');
48465          
48466         
48467         
48468     },
48469     // private
48470     reset : function(){
48471         this.clearValue();
48472     },
48473     // private
48474     findRecord : function(prop, value){
48475         
48476         return false;
48477     
48478         var record;
48479         if(this.store.getCount() > 0){
48480             this.store.each(function(r){
48481                 if(r.data[prop] == value){
48482                     record = r;
48483                     return false;
48484                 }
48485                 return true;
48486             });
48487         }
48488         return record;
48489     },
48490     
48491     getName: function()
48492     {
48493         // returns hidden if it's set..
48494         if (!this.rendered) {return ''};
48495         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
48496         
48497     },
48498      
48499
48500     
48501
48502     // private
48503     onEmptyResults : function(){
48504         Roo.log('empty results');
48505         //this.collapse();
48506     },
48507
48508     /**
48509      * Returns true if the dropdown list is expanded, else false.
48510      */
48511     isExpanded : function(){
48512         return false;
48513     },
48514
48515     /**
48516      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
48517      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48518      * @param {String} value The data value of the item to select
48519      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48520      * selected item if it is not currently in view (defaults to true)
48521      * @return {Boolean} True if the value matched an item in the list, else false
48522      */
48523     selectByValue : function(v, scrollIntoView){
48524         Roo.log('select By Value');
48525         return false;
48526     
48527         if(v !== undefined && v !== null){
48528             var r = this.findRecord(this.valueField || this.displayField, v);
48529             if(r){
48530                 this.select(this.store.indexOf(r), scrollIntoView);
48531                 return true;
48532             }
48533         }
48534         return false;
48535     },
48536
48537     /**
48538      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
48539      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48540      * @param {Number} index The zero-based index of the list item to select
48541      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48542      * selected item if it is not currently in view (defaults to true)
48543      */
48544     select : function(index, scrollIntoView){
48545         Roo.log('select ');
48546         return  ;
48547         
48548         this.selectedIndex = index;
48549         this.view.select(index);
48550         if(scrollIntoView !== false){
48551             var el = this.view.getNode(index);
48552             if(el){
48553                 this.innerList.scrollChildIntoView(el, false);
48554             }
48555         }
48556     },
48557
48558       
48559
48560     // private
48561     validateBlur : function(){
48562         
48563         return;
48564         
48565     },
48566
48567     // private
48568     initQuery : function(){
48569         this.doQuery(this.getRawValue());
48570     },
48571
48572     // private
48573     doForce : function(){
48574         if(this.el.dom.value.length > 0){
48575             this.el.dom.value =
48576                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
48577              
48578         }
48579     },
48580
48581     /**
48582      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
48583      * query allowing the query action to be canceled if needed.
48584      * @param {String} query The SQL query to execute
48585      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
48586      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
48587      * saved in the current store (defaults to false)
48588      */
48589     doQuery : function(q, forceAll){
48590         
48591         Roo.log('doQuery?');
48592         if(q === undefined || q === null){
48593             q = '';
48594         }
48595         var qe = {
48596             query: q,
48597             forceAll: forceAll,
48598             combo: this,
48599             cancel:false
48600         };
48601         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48602             return false;
48603         }
48604         q = qe.query;
48605         forceAll = qe.forceAll;
48606         if(forceAll === true || (q.length >= this.minChars)){
48607             if(this.lastQuery != q || this.alwaysQuery){
48608                 this.lastQuery = q;
48609                 if(this.mode == 'local'){
48610                     this.selectedIndex = -1;
48611                     if(forceAll){
48612                         this.store.clearFilter();
48613                     }else{
48614                         this.store.filter(this.displayField, q);
48615                     }
48616                     this.onLoad();
48617                 }else{
48618                     this.store.baseParams[this.queryParam] = q;
48619                     this.store.load({
48620                         params: this.getParams(q)
48621                     });
48622                     this.expand();
48623                 }
48624             }else{
48625                 this.selectedIndex = -1;
48626                 this.onLoad();   
48627             }
48628         }
48629     },
48630
48631     // private
48632     getParams : function(q){
48633         var p = {};
48634         //p[this.queryParam] = q;
48635         if(this.pageSize){
48636             p.start = 0;
48637             p.limit = this.pageSize;
48638         }
48639         return p;
48640     },
48641
48642     /**
48643      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48644      */
48645     collapse : function(){
48646         
48647     },
48648
48649     // private
48650     collapseIf : function(e){
48651         
48652     },
48653
48654     /**
48655      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48656      */
48657     expand : function(){
48658         
48659     } ,
48660
48661     // private
48662      
48663
48664     /** 
48665     * @cfg {Boolean} grow 
48666     * @hide 
48667     */
48668     /** 
48669     * @cfg {Number} growMin 
48670     * @hide 
48671     */
48672     /** 
48673     * @cfg {Number} growMax 
48674     * @hide 
48675     */
48676     /**
48677      * @hide
48678      * @method autoSize
48679      */
48680     
48681     setWidth : function()
48682     {
48683         
48684     },
48685     getResizeEl : function(){
48686         return this.el;
48687     }
48688 });//<script type="text/javasscript">
48689  
48690
48691 /**
48692  * @class Roo.DDView
48693  * A DnD enabled version of Roo.View.
48694  * @param {Element/String} container The Element in which to create the View.
48695  * @param {String} tpl The template string used to create the markup for each element of the View
48696  * @param {Object} config The configuration properties. These include all the config options of
48697  * {@link Roo.View} plus some specific to this class.<br>
48698  * <p>
48699  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48700  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48701  * <p>
48702  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48703 .x-view-drag-insert-above {
48704         border-top:1px dotted #3366cc;
48705 }
48706 .x-view-drag-insert-below {
48707         border-bottom:1px dotted #3366cc;
48708 }
48709 </code></pre>
48710  * 
48711  */
48712  
48713 Roo.DDView = function(container, tpl, config) {
48714     Roo.DDView.superclass.constructor.apply(this, arguments);
48715     this.getEl().setStyle("outline", "0px none");
48716     this.getEl().unselectable();
48717     if (this.dragGroup) {
48718                 this.setDraggable(this.dragGroup.split(","));
48719     }
48720     if (this.dropGroup) {
48721                 this.setDroppable(this.dropGroup.split(","));
48722     }
48723     if (this.deletable) {
48724         this.setDeletable();
48725     }
48726     this.isDirtyFlag = false;
48727         this.addEvents({
48728                 "drop" : true
48729         });
48730 };
48731
48732 Roo.extend(Roo.DDView, Roo.View, {
48733 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48734 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48735 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48736 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48737
48738         isFormField: true,
48739
48740         reset: Roo.emptyFn,
48741         
48742         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48743
48744         validate: function() {
48745                 return true;
48746         },
48747         
48748         destroy: function() {
48749                 this.purgeListeners();
48750                 this.getEl.removeAllListeners();
48751                 this.getEl().remove();
48752                 if (this.dragZone) {
48753                         if (this.dragZone.destroy) {
48754                                 this.dragZone.destroy();
48755                         }
48756                 }
48757                 if (this.dropZone) {
48758                         if (this.dropZone.destroy) {
48759                                 this.dropZone.destroy();
48760                         }
48761                 }
48762         },
48763
48764 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48765         getName: function() {
48766                 return this.name;
48767         },
48768
48769 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48770         setValue: function(v) {
48771                 if (!this.store) {
48772                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48773                 }
48774                 var data = {};
48775                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48776                 this.store.proxy = new Roo.data.MemoryProxy(data);
48777                 this.store.load();
48778         },
48779
48780 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48781         getValue: function() {
48782                 var result = '(';
48783                 this.store.each(function(rec) {
48784                         result += rec.id + ',';
48785                 });
48786                 return result.substr(0, result.length - 1) + ')';
48787         },
48788         
48789         getIds: function() {
48790                 var i = 0, result = new Array(this.store.getCount());
48791                 this.store.each(function(rec) {
48792                         result[i++] = rec.id;
48793                 });
48794                 return result;
48795         },
48796         
48797         isDirty: function() {
48798                 return this.isDirtyFlag;
48799         },
48800
48801 /**
48802  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48803  *      whole Element becomes the target, and this causes the drop gesture to append.
48804  */
48805     getTargetFromEvent : function(e) {
48806                 var target = e.getTarget();
48807                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48808                 target = target.parentNode;
48809                 }
48810                 if (!target) {
48811                         target = this.el.dom.lastChild || this.el.dom;
48812                 }
48813                 return target;
48814     },
48815
48816 /**
48817  *      Create the drag data which consists of an object which has the property "ddel" as
48818  *      the drag proxy element. 
48819  */
48820     getDragData : function(e) {
48821         var target = this.findItemFromChild(e.getTarget());
48822                 if(target) {
48823                         this.handleSelection(e);
48824                         var selNodes = this.getSelectedNodes();
48825             var dragData = {
48826                 source: this,
48827                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48828                 nodes: selNodes,
48829                 records: []
48830                         };
48831                         var selectedIndices = this.getSelectedIndexes();
48832                         for (var i = 0; i < selectedIndices.length; i++) {
48833                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48834                         }
48835                         if (selNodes.length == 1) {
48836                                 dragData.ddel = target.cloneNode(true); // the div element
48837                         } else {
48838                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48839                                 div.className = 'multi-proxy';
48840                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48841                                         div.appendChild(selNodes[i].cloneNode(true));
48842                                 }
48843                                 dragData.ddel = div;
48844                         }
48845             //console.log(dragData)
48846             //console.log(dragData.ddel.innerHTML)
48847                         return dragData;
48848                 }
48849         //console.log('nodragData')
48850                 return false;
48851     },
48852     
48853 /**     Specify to which ddGroup items in this DDView may be dragged. */
48854     setDraggable: function(ddGroup) {
48855         if (ddGroup instanceof Array) {
48856                 Roo.each(ddGroup, this.setDraggable, this);
48857                 return;
48858         }
48859         if (this.dragZone) {
48860                 this.dragZone.addToGroup(ddGroup);
48861         } else {
48862                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48863                                 containerScroll: true,
48864                                 ddGroup: ddGroup 
48865
48866                         });
48867 //                      Draggability implies selection. DragZone's mousedown selects the element.
48868                         if (!this.multiSelect) { this.singleSelect = true; }
48869
48870 //                      Wire the DragZone's handlers up to methods in *this*
48871                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48872                 }
48873     },
48874
48875 /**     Specify from which ddGroup this DDView accepts drops. */
48876     setDroppable: function(ddGroup) {
48877         if (ddGroup instanceof Array) {
48878                 Roo.each(ddGroup, this.setDroppable, this);
48879                 return;
48880         }
48881         if (this.dropZone) {
48882                 this.dropZone.addToGroup(ddGroup);
48883         } else {
48884                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48885                                 containerScroll: true,
48886                                 ddGroup: ddGroup
48887                         });
48888
48889 //                      Wire the DropZone's handlers up to methods in *this*
48890                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48891                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48892                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48893                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48894                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48895                 }
48896     },
48897
48898 /**     Decide whether to drop above or below a View node. */
48899     getDropPoint : function(e, n, dd){
48900         if (n == this.el.dom) { return "above"; }
48901                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48902                 var c = t + (b - t) / 2;
48903                 var y = Roo.lib.Event.getPageY(e);
48904                 if(y <= c) {
48905                         return "above";
48906                 }else{
48907                         return "below";
48908                 }
48909     },
48910
48911     onNodeEnter : function(n, dd, e, data){
48912                 return false;
48913     },
48914     
48915     onNodeOver : function(n, dd, e, data){
48916                 var pt = this.getDropPoint(e, n, dd);
48917                 // set the insert point style on the target node
48918                 var dragElClass = this.dropNotAllowed;
48919                 if (pt) {
48920                         var targetElClass;
48921                         if (pt == "above"){
48922                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
48923                                 targetElClass = "x-view-drag-insert-above";
48924                         } else {
48925                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
48926                                 targetElClass = "x-view-drag-insert-below";
48927                         }
48928                         if (this.lastInsertClass != targetElClass){
48929                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
48930                                 this.lastInsertClass = targetElClass;
48931                         }
48932                 }
48933                 return dragElClass;
48934         },
48935
48936     onNodeOut : function(n, dd, e, data){
48937                 this.removeDropIndicators(n);
48938     },
48939
48940     onNodeDrop : function(n, dd, e, data){
48941         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
48942                 return false;
48943         }
48944         var pt = this.getDropPoint(e, n, dd);
48945                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
48946                 if (pt == "below") { insertAt++; }
48947                 for (var i = 0; i < data.records.length; i++) {
48948                         var r = data.records[i];
48949                         var dup = this.store.getById(r.id);
48950                         if (dup && (dd != this.dragZone)) {
48951                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48952                         } else {
48953                                 if (data.copy) {
48954                                         this.store.insert(insertAt++, r.copy());
48955                                 } else {
48956                                         data.source.isDirtyFlag = true;
48957                                         r.store.remove(r);
48958                                         this.store.insert(insertAt++, r);
48959                                 }
48960                                 this.isDirtyFlag = true;
48961                         }
48962                 }
48963                 this.dragZone.cachedTarget = null;
48964                 return true;
48965     },
48966
48967     removeDropIndicators : function(n){
48968                 if(n){
48969                         Roo.fly(n).removeClass([
48970                                 "x-view-drag-insert-above",
48971                                 "x-view-drag-insert-below"]);
48972                         this.lastInsertClass = "_noclass";
48973                 }
48974     },
48975
48976 /**
48977  *      Utility method. Add a delete option to the DDView's context menu.
48978  *      @param {String} imageUrl The URL of the "delete" icon image.
48979  */
48980         setDeletable: function(imageUrl) {
48981                 if (!this.singleSelect && !this.multiSelect) {
48982                         this.singleSelect = true;
48983                 }
48984                 var c = this.getContextMenu();
48985                 this.contextMenu.on("itemclick", function(item) {
48986                         switch (item.id) {
48987                                 case "delete":
48988                                         this.remove(this.getSelectedIndexes());
48989                                         break;
48990                         }
48991                 }, this);
48992                 this.contextMenu.add({
48993                         icon: imageUrl,
48994                         id: "delete",
48995                         text: 'Delete'
48996                 });
48997         },
48998         
48999 /**     Return the context menu for this DDView. */
49000         getContextMenu: function() {
49001                 if (!this.contextMenu) {
49002 //                      Create the View's context menu
49003                         this.contextMenu = new Roo.menu.Menu({
49004                                 id: this.id + "-contextmenu"
49005                         });
49006                         this.el.on("contextmenu", this.showContextMenu, this);
49007                 }
49008                 return this.contextMenu;
49009         },
49010         
49011         disableContextMenu: function() {
49012                 if (this.contextMenu) {
49013                         this.el.un("contextmenu", this.showContextMenu, this);
49014                 }
49015         },
49016
49017         showContextMenu: function(e, item) {
49018         item = this.findItemFromChild(e.getTarget());
49019                 if (item) {
49020                         e.stopEvent();
49021                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
49022                         this.contextMenu.showAt(e.getXY());
49023             }
49024     },
49025
49026 /**
49027  *      Remove {@link Roo.data.Record}s at the specified indices.
49028  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
49029  */
49030     remove: function(selectedIndices) {
49031                 selectedIndices = [].concat(selectedIndices);
49032                 for (var i = 0; i < selectedIndices.length; i++) {
49033                         var rec = this.store.getAt(selectedIndices[i]);
49034                         this.store.remove(rec);
49035                 }
49036     },
49037
49038 /**
49039  *      Double click fires the event, but also, if this is draggable, and there is only one other
49040  *      related DropZone, it transfers the selected node.
49041  */
49042     onDblClick : function(e){
49043         var item = this.findItemFromChild(e.getTarget());
49044         if(item){
49045             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
49046                 return false;
49047             }
49048             if (this.dragGroup) {
49049                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
49050                     while (targets.indexOf(this.dropZone) > -1) {
49051                             targets.remove(this.dropZone);
49052                                 }
49053                     if (targets.length == 1) {
49054                                         this.dragZone.cachedTarget = null;
49055                         var el = Roo.get(targets[0].getEl());
49056                         var box = el.getBox(true);
49057                         targets[0].onNodeDrop(el.dom, {
49058                                 target: el.dom,
49059                                 xy: [box.x, box.y + box.height - 1]
49060                         }, null, this.getDragData(e));
49061                     }
49062                 }
49063         }
49064     },
49065     
49066     handleSelection: function(e) {
49067                 this.dragZone.cachedTarget = null;
49068         var item = this.findItemFromChild(e.getTarget());
49069         if (!item) {
49070                 this.clearSelections(true);
49071                 return;
49072         }
49073                 if (item && (this.multiSelect || this.singleSelect)){
49074                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
49075                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
49076                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
49077                                 this.unselect(item);
49078                         } else {
49079                                 this.select(item, this.multiSelect && e.ctrlKey);
49080                                 this.lastSelection = item;
49081                         }
49082                 }
49083     },
49084
49085     onItemClick : function(item, index, e){
49086                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
49087                         return false;
49088                 }
49089                 return true;
49090     },
49091
49092     unselect : function(nodeInfo, suppressEvent){
49093                 var node = this.getNode(nodeInfo);
49094                 if(node && this.isSelected(node)){
49095                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
49096                                 Roo.fly(node).removeClass(this.selectedClass);
49097                                 this.selections.remove(node);
49098                                 if(!suppressEvent){
49099                                         this.fireEvent("selectionchange", this, this.selections);
49100                                 }
49101                         }
49102                 }
49103     }
49104 });
49105 /*
49106  * Based on:
49107  * Ext JS Library 1.1.1
49108  * Copyright(c) 2006-2007, Ext JS, LLC.
49109  *
49110  * Originally Released Under LGPL - original licence link has changed is not relivant.
49111  *
49112  * Fork - LGPL
49113  * <script type="text/javascript">
49114  */
49115  
49116 /**
49117  * @class Roo.LayoutManager
49118  * @extends Roo.util.Observable
49119  * Base class for layout managers.
49120  */
49121 Roo.LayoutManager = function(container, config){
49122     Roo.LayoutManager.superclass.constructor.call(this);
49123     this.el = Roo.get(container);
49124     // ie scrollbar fix
49125     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
49126         document.body.scroll = "no";
49127     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
49128         this.el.position('relative');
49129     }
49130     this.id = this.el.id;
49131     this.el.addClass("x-layout-container");
49132     /** false to disable window resize monitoring @type Boolean */
49133     this.monitorWindowResize = true;
49134     this.regions = {};
49135     this.addEvents({
49136         /**
49137          * @event layout
49138          * Fires when a layout is performed. 
49139          * @param {Roo.LayoutManager} this
49140          */
49141         "layout" : true,
49142         /**
49143          * @event regionresized
49144          * Fires when the user resizes a region. 
49145          * @param {Roo.LayoutRegion} region The resized region
49146          * @param {Number} newSize The new size (width for east/west, height for north/south)
49147          */
49148         "regionresized" : true,
49149         /**
49150          * @event regioncollapsed
49151          * Fires when a region is collapsed. 
49152          * @param {Roo.LayoutRegion} region The collapsed region
49153          */
49154         "regioncollapsed" : true,
49155         /**
49156          * @event regionexpanded
49157          * Fires when a region is expanded.  
49158          * @param {Roo.LayoutRegion} region The expanded region
49159          */
49160         "regionexpanded" : true
49161     });
49162     this.updating = false;
49163     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49164 };
49165
49166 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
49167     /**
49168      * Returns true if this layout is currently being updated
49169      * @return {Boolean}
49170      */
49171     isUpdating : function(){
49172         return this.updating; 
49173     },
49174     
49175     /**
49176      * Suspend the LayoutManager from doing auto-layouts while
49177      * making multiple add or remove calls
49178      */
49179     beginUpdate : function(){
49180         this.updating = true;    
49181     },
49182     
49183     /**
49184      * Restore auto-layouts and optionally disable the manager from performing a layout
49185      * @param {Boolean} noLayout true to disable a layout update 
49186      */
49187     endUpdate : function(noLayout){
49188         this.updating = false;
49189         if(!noLayout){
49190             this.layout();
49191         }    
49192     },
49193     
49194     layout: function(){
49195         
49196     },
49197     
49198     onRegionResized : function(region, newSize){
49199         this.fireEvent("regionresized", region, newSize);
49200         this.layout();
49201     },
49202     
49203     onRegionCollapsed : function(region){
49204         this.fireEvent("regioncollapsed", region);
49205     },
49206     
49207     onRegionExpanded : function(region){
49208         this.fireEvent("regionexpanded", region);
49209     },
49210         
49211     /**
49212      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
49213      * performs box-model adjustments.
49214      * @return {Object} The size as an object {width: (the width), height: (the height)}
49215      */
49216     getViewSize : function(){
49217         var size;
49218         if(this.el.dom != document.body){
49219             size = this.el.getSize();
49220         }else{
49221             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
49222         }
49223         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
49224         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
49225         return size;
49226     },
49227     
49228     /**
49229      * Returns the Element this layout is bound to.
49230      * @return {Roo.Element}
49231      */
49232     getEl : function(){
49233         return this.el;
49234     },
49235     
49236     /**
49237      * Returns the specified region.
49238      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
49239      * @return {Roo.LayoutRegion}
49240      */
49241     getRegion : function(target){
49242         return this.regions[target.toLowerCase()];
49243     },
49244     
49245     onWindowResize : function(){
49246         if(this.monitorWindowResize){
49247             this.layout();
49248         }
49249     }
49250 });/*
49251  * Based on:
49252  * Ext JS Library 1.1.1
49253  * Copyright(c) 2006-2007, Ext JS, LLC.
49254  *
49255  * Originally Released Under LGPL - original licence link has changed is not relivant.
49256  *
49257  * Fork - LGPL
49258  * <script type="text/javascript">
49259  */
49260 /**
49261  * @class Roo.BorderLayout
49262  * @extends Roo.LayoutManager
49263  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
49264  * please see: <br><br>
49265  * <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>
49266  * <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>
49267  * Example:
49268  <pre><code>
49269  var layout = new Roo.BorderLayout(document.body, {
49270     north: {
49271         initialSize: 25,
49272         titlebar: false
49273     },
49274     west: {
49275         split:true,
49276         initialSize: 200,
49277         minSize: 175,
49278         maxSize: 400,
49279         titlebar: true,
49280         collapsible: true
49281     },
49282     east: {
49283         split:true,
49284         initialSize: 202,
49285         minSize: 175,
49286         maxSize: 400,
49287         titlebar: true,
49288         collapsible: true
49289     },
49290     south: {
49291         split:true,
49292         initialSize: 100,
49293         minSize: 100,
49294         maxSize: 200,
49295         titlebar: true,
49296         collapsible: true
49297     },
49298     center: {
49299         titlebar: true,
49300         autoScroll:true,
49301         resizeTabs: true,
49302         minTabWidth: 50,
49303         preferredTabWidth: 150
49304     }
49305 });
49306
49307 // shorthand
49308 var CP = Roo.ContentPanel;
49309
49310 layout.beginUpdate();
49311 layout.add("north", new CP("north", "North"));
49312 layout.add("south", new CP("south", {title: "South", closable: true}));
49313 layout.add("west", new CP("west", {title: "West"}));
49314 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
49315 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
49316 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
49317 layout.getRegion("center").showPanel("center1");
49318 layout.endUpdate();
49319 </code></pre>
49320
49321 <b>The container the layout is rendered into can be either the body element or any other element.
49322 If it is not the body element, the container needs to either be an absolute positioned element,
49323 or you will need to add "position:relative" to the css of the container.  You will also need to specify
49324 the container size if it is not the body element.</b>
49325
49326 * @constructor
49327 * Create a new BorderLayout
49328 * @param {String/HTMLElement/Element} container The container this layout is bound to
49329 * @param {Object} config Configuration options
49330  */
49331 Roo.BorderLayout = function(container, config){
49332     config = config || {};
49333     Roo.BorderLayout.superclass.constructor.call(this, container, config);
49334     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
49335     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
49336         var target = this.factory.validRegions[i];
49337         if(config[target]){
49338             this.addRegion(target, config[target]);
49339         }
49340     }
49341 };
49342
49343 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
49344     /**
49345      * Creates and adds a new region if it doesn't already exist.
49346      * @param {String} target The target region key (north, south, east, west or center).
49347      * @param {Object} config The regions config object
49348      * @return {BorderLayoutRegion} The new region
49349      */
49350     addRegion : function(target, config){
49351         if(!this.regions[target]){
49352             var r = this.factory.create(target, this, config);
49353             this.bindRegion(target, r);
49354         }
49355         return this.regions[target];
49356     },
49357
49358     // private (kinda)
49359     bindRegion : function(name, r){
49360         this.regions[name] = r;
49361         r.on("visibilitychange", this.layout, this);
49362         r.on("paneladded", this.layout, this);
49363         r.on("panelremoved", this.layout, this);
49364         r.on("invalidated", this.layout, this);
49365         r.on("resized", this.onRegionResized, this);
49366         r.on("collapsed", this.onRegionCollapsed, this);
49367         r.on("expanded", this.onRegionExpanded, this);
49368     },
49369
49370     /**
49371      * Performs a layout update.
49372      */
49373     layout : function(){
49374         if(this.updating) return;
49375         var size = this.getViewSize();
49376         var w = size.width;
49377         var h = size.height;
49378         var centerW = w;
49379         var centerH = h;
49380         var centerY = 0;
49381         var centerX = 0;
49382         //var x = 0, y = 0;
49383
49384         var rs = this.regions;
49385         var north = rs["north"];
49386         var south = rs["south"]; 
49387         var west = rs["west"];
49388         var east = rs["east"];
49389         var center = rs["center"];
49390         //if(this.hideOnLayout){ // not supported anymore
49391             //c.el.setStyle("display", "none");
49392         //}
49393         if(north && north.isVisible()){
49394             var b = north.getBox();
49395             var m = north.getMargins();
49396             b.width = w - (m.left+m.right);
49397             b.x = m.left;
49398             b.y = m.top;
49399             centerY = b.height + b.y + m.bottom;
49400             centerH -= centerY;
49401             north.updateBox(this.safeBox(b));
49402         }
49403         if(south && south.isVisible()){
49404             var b = south.getBox();
49405             var m = south.getMargins();
49406             b.width = w - (m.left+m.right);
49407             b.x = m.left;
49408             var totalHeight = (b.height + m.top + m.bottom);
49409             b.y = h - totalHeight + m.top;
49410             centerH -= totalHeight;
49411             south.updateBox(this.safeBox(b));
49412         }
49413         if(west && west.isVisible()){
49414             var b = west.getBox();
49415             var m = west.getMargins();
49416             b.height = centerH - (m.top+m.bottom);
49417             b.x = m.left;
49418             b.y = centerY + m.top;
49419             var totalWidth = (b.width + m.left + m.right);
49420             centerX += totalWidth;
49421             centerW -= totalWidth;
49422             west.updateBox(this.safeBox(b));
49423         }
49424         if(east && east.isVisible()){
49425             var b = east.getBox();
49426             var m = east.getMargins();
49427             b.height = centerH - (m.top+m.bottom);
49428             var totalWidth = (b.width + m.left + m.right);
49429             b.x = w - totalWidth + m.left;
49430             b.y = centerY + m.top;
49431             centerW -= totalWidth;
49432             east.updateBox(this.safeBox(b));
49433         }
49434         if(center){
49435             var m = center.getMargins();
49436             var centerBox = {
49437                 x: centerX + m.left,
49438                 y: centerY + m.top,
49439                 width: centerW - (m.left+m.right),
49440                 height: centerH - (m.top+m.bottom)
49441             };
49442             //if(this.hideOnLayout){
49443                 //center.el.setStyle("display", "block");
49444             //}
49445             center.updateBox(this.safeBox(centerBox));
49446         }
49447         this.el.repaint();
49448         this.fireEvent("layout", this);
49449     },
49450
49451     // private
49452     safeBox : function(box){
49453         box.width = Math.max(0, box.width);
49454         box.height = Math.max(0, box.height);
49455         return box;
49456     },
49457
49458     /**
49459      * Adds a ContentPanel (or subclass) to this layout.
49460      * @param {String} target The target region key (north, south, east, west or center).
49461      * @param {Roo.ContentPanel} panel The panel to add
49462      * @return {Roo.ContentPanel} The added panel
49463      */
49464     add : function(target, panel){
49465          
49466         target = target.toLowerCase();
49467         return this.regions[target].add(panel);
49468     },
49469
49470     /**
49471      * Remove a ContentPanel (or subclass) to this layout.
49472      * @param {String} target The target region key (north, south, east, west or center).
49473      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
49474      * @return {Roo.ContentPanel} The removed panel
49475      */
49476     remove : function(target, panel){
49477         target = target.toLowerCase();
49478         return this.regions[target].remove(panel);
49479     },
49480
49481     /**
49482      * Searches all regions for a panel with the specified id
49483      * @param {String} panelId
49484      * @return {Roo.ContentPanel} The panel or null if it wasn't found
49485      */
49486     findPanel : function(panelId){
49487         var rs = this.regions;
49488         for(var target in rs){
49489             if(typeof rs[target] != "function"){
49490                 var p = rs[target].getPanel(panelId);
49491                 if(p){
49492                     return p;
49493                 }
49494             }
49495         }
49496         return null;
49497     },
49498
49499     /**
49500      * Searches all regions for a panel with the specified id and activates (shows) it.
49501      * @param {String/ContentPanel} panelId The panels id or the panel itself
49502      * @return {Roo.ContentPanel} The shown panel or null
49503      */
49504     showPanel : function(panelId) {
49505       var rs = this.regions;
49506       for(var target in rs){
49507          var r = rs[target];
49508          if(typeof r != "function"){
49509             if(r.hasPanel(panelId)){
49510                return r.showPanel(panelId);
49511             }
49512          }
49513       }
49514       return null;
49515    },
49516
49517    /**
49518      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
49519      * @param {Roo.state.Provider} provider (optional) An alternate state provider
49520      */
49521     restoreState : function(provider){
49522         if(!provider){
49523             provider = Roo.state.Manager;
49524         }
49525         var sm = new Roo.LayoutStateManager();
49526         sm.init(this, provider);
49527     },
49528
49529     /**
49530      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
49531      * object should contain properties for each region to add ContentPanels to, and each property's value should be
49532      * a valid ContentPanel config object.  Example:
49533      * <pre><code>
49534 // Create the main layout
49535 var layout = new Roo.BorderLayout('main-ct', {
49536     west: {
49537         split:true,
49538         minSize: 175,
49539         titlebar: true
49540     },
49541     center: {
49542         title:'Components'
49543     }
49544 }, 'main-ct');
49545
49546 // Create and add multiple ContentPanels at once via configs
49547 layout.batchAdd({
49548    west: {
49549        id: 'source-files',
49550        autoCreate:true,
49551        title:'Ext Source Files',
49552        autoScroll:true,
49553        fitToFrame:true
49554    },
49555    center : {
49556        el: cview,
49557        autoScroll:true,
49558        fitToFrame:true,
49559        toolbar: tb,
49560        resizeEl:'cbody'
49561    }
49562 });
49563 </code></pre>
49564      * @param {Object} regions An object containing ContentPanel configs by region name
49565      */
49566     batchAdd : function(regions){
49567         this.beginUpdate();
49568         for(var rname in regions){
49569             var lr = this.regions[rname];
49570             if(lr){
49571                 this.addTypedPanels(lr, regions[rname]);
49572             }
49573         }
49574         this.endUpdate();
49575     },
49576
49577     // private
49578     addTypedPanels : function(lr, ps){
49579         if(typeof ps == 'string'){
49580             lr.add(new Roo.ContentPanel(ps));
49581         }
49582         else if(ps instanceof Array){
49583             for(var i =0, len = ps.length; i < len; i++){
49584                 this.addTypedPanels(lr, ps[i]);
49585             }
49586         }
49587         else if(!ps.events){ // raw config?
49588             var el = ps.el;
49589             delete ps.el; // prevent conflict
49590             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
49591         }
49592         else {  // panel object assumed!
49593             lr.add(ps);
49594         }
49595     },
49596     /**
49597      * Adds a xtype elements to the layout.
49598      * <pre><code>
49599
49600 layout.addxtype({
49601        xtype : 'ContentPanel',
49602        region: 'west',
49603        items: [ .... ]
49604    }
49605 );
49606
49607 layout.addxtype({
49608         xtype : 'NestedLayoutPanel',
49609         region: 'west',
49610         layout: {
49611            center: { },
49612            west: { }   
49613         },
49614         items : [ ... list of content panels or nested layout panels.. ]
49615    }
49616 );
49617 </code></pre>
49618      * @param {Object} cfg Xtype definition of item to add.
49619      */
49620     addxtype : function(cfg)
49621     {
49622         // basically accepts a pannel...
49623         // can accept a layout region..!?!?
49624         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49625         
49626         if (!cfg.xtype.match(/Panel$/)) {
49627             return false;
49628         }
49629         var ret = false;
49630         
49631         if (typeof(cfg.region) == 'undefined') {
49632             Roo.log("Failed to add Panel, region was not set");
49633             Roo.log(cfg);
49634             return false;
49635         }
49636         var region = cfg.region;
49637         delete cfg.region;
49638         
49639           
49640         var xitems = [];
49641         if (cfg.items) {
49642             xitems = cfg.items;
49643             delete cfg.items;
49644         }
49645         var nb = false;
49646         
49647         switch(cfg.xtype) 
49648         {
49649             case 'ContentPanel':  // ContentPanel (el, cfg)
49650             case 'ScrollPanel':  // ContentPanel (el, cfg)
49651             case 'ViewPanel': 
49652                 if(cfg.autoCreate) {
49653                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49654                 } else {
49655                     var el = this.el.createChild();
49656                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49657                 }
49658                 
49659                 this.add(region, ret);
49660                 break;
49661             
49662             
49663             case 'TreePanel': // our new panel!
49664                 cfg.el = this.el.createChild();
49665                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49666                 this.add(region, ret);
49667                 break;
49668             
49669             case 'NestedLayoutPanel': 
49670                 // create a new Layout (which is  a Border Layout...
49671                 var el = this.el.createChild();
49672                 var clayout = cfg.layout;
49673                 delete cfg.layout;
49674                 clayout.items   = clayout.items  || [];
49675                 // replace this exitems with the clayout ones..
49676                 xitems = clayout.items;
49677                  
49678                 
49679                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49680                     cfg.background = false;
49681                 }
49682                 var layout = new Roo.BorderLayout(el, clayout);
49683                 
49684                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49685                 //console.log('adding nested layout panel '  + cfg.toSource());
49686                 this.add(region, ret);
49687                 nb = {}; /// find first...
49688                 break;
49689                 
49690             case 'GridPanel': 
49691             
49692                 // needs grid and region
49693                 
49694                 //var el = this.getRegion(region).el.createChild();
49695                 var el = this.el.createChild();
49696                 // create the grid first...
49697                 
49698                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49699                 delete cfg.grid;
49700                 if (region == 'center' && this.active ) {
49701                     cfg.background = false;
49702                 }
49703                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49704                 
49705                 this.add(region, ret);
49706                 if (cfg.background) {
49707                     ret.on('activate', function(gp) {
49708                         if (!gp.grid.rendered) {
49709                             gp.grid.render();
49710                         }
49711                     });
49712                 } else {
49713                     grid.render();
49714                 }
49715                 break;
49716            
49717            
49718            
49719                 
49720                 
49721                 
49722             default:
49723                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49724                     
49725                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49726                     this.add(region, ret);
49727                 } else {
49728                 
49729                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49730                     return null;
49731                 }
49732                 
49733              // GridPanel (grid, cfg)
49734             
49735         }
49736         this.beginUpdate();
49737         // add children..
49738         var region = '';
49739         var abn = {};
49740         Roo.each(xitems, function(i)  {
49741             region = nb && i.region ? i.region : false;
49742             
49743             var add = ret.addxtype(i);
49744            
49745             if (region) {
49746                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49747                 if (!i.background) {
49748                     abn[region] = nb[region] ;
49749                 }
49750             }
49751             
49752         });
49753         this.endUpdate();
49754
49755         // make the last non-background panel active..
49756         //if (nb) { Roo.log(abn); }
49757         if (nb) {
49758             
49759             for(var r in abn) {
49760                 region = this.getRegion(r);
49761                 if (region) {
49762                     // tried using nb[r], but it does not work..
49763                      
49764                     region.showPanel(abn[r]);
49765                    
49766                 }
49767             }
49768         }
49769         return ret;
49770         
49771     }
49772 });
49773
49774 /**
49775  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49776  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49777  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49778  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49779  * <pre><code>
49780 // shorthand
49781 var CP = Roo.ContentPanel;
49782
49783 var layout = Roo.BorderLayout.create({
49784     north: {
49785         initialSize: 25,
49786         titlebar: false,
49787         panels: [new CP("north", "North")]
49788     },
49789     west: {
49790         split:true,
49791         initialSize: 200,
49792         minSize: 175,
49793         maxSize: 400,
49794         titlebar: true,
49795         collapsible: true,
49796         panels: [new CP("west", {title: "West"})]
49797     },
49798     east: {
49799         split:true,
49800         initialSize: 202,
49801         minSize: 175,
49802         maxSize: 400,
49803         titlebar: true,
49804         collapsible: true,
49805         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49806     },
49807     south: {
49808         split:true,
49809         initialSize: 100,
49810         minSize: 100,
49811         maxSize: 200,
49812         titlebar: true,
49813         collapsible: true,
49814         panels: [new CP("south", {title: "South", closable: true})]
49815     },
49816     center: {
49817         titlebar: true,
49818         autoScroll:true,
49819         resizeTabs: true,
49820         minTabWidth: 50,
49821         preferredTabWidth: 150,
49822         panels: [
49823             new CP("center1", {title: "Close Me", closable: true}),
49824             new CP("center2", {title: "Center Panel", closable: false})
49825         ]
49826     }
49827 }, document.body);
49828
49829 layout.getRegion("center").showPanel("center1");
49830 </code></pre>
49831  * @param config
49832  * @param targetEl
49833  */
49834 Roo.BorderLayout.create = function(config, targetEl){
49835     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49836     layout.beginUpdate();
49837     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49838     for(var j = 0, jlen = regions.length; j < jlen; j++){
49839         var lr = regions[j];
49840         if(layout.regions[lr] && config[lr].panels){
49841             var r = layout.regions[lr];
49842             var ps = config[lr].panels;
49843             layout.addTypedPanels(r, ps);
49844         }
49845     }
49846     layout.endUpdate();
49847     return layout;
49848 };
49849
49850 // private
49851 Roo.BorderLayout.RegionFactory = {
49852     // private
49853     validRegions : ["north","south","east","west","center"],
49854
49855     // private
49856     create : function(target, mgr, config){
49857         target = target.toLowerCase();
49858         if(config.lightweight || config.basic){
49859             return new Roo.BasicLayoutRegion(mgr, config, target);
49860         }
49861         switch(target){
49862             case "north":
49863                 return new Roo.NorthLayoutRegion(mgr, config);
49864             case "south":
49865                 return new Roo.SouthLayoutRegion(mgr, config);
49866             case "east":
49867                 return new Roo.EastLayoutRegion(mgr, config);
49868             case "west":
49869                 return new Roo.WestLayoutRegion(mgr, config);
49870             case "center":
49871                 return new Roo.CenterLayoutRegion(mgr, config);
49872         }
49873         throw 'Layout region "'+target+'" not supported.';
49874     }
49875 };/*
49876  * Based on:
49877  * Ext JS Library 1.1.1
49878  * Copyright(c) 2006-2007, Ext JS, LLC.
49879  *
49880  * Originally Released Under LGPL - original licence link has changed is not relivant.
49881  *
49882  * Fork - LGPL
49883  * <script type="text/javascript">
49884  */
49885  
49886 /**
49887  * @class Roo.BasicLayoutRegion
49888  * @extends Roo.util.Observable
49889  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49890  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49891  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49892  */
49893 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49894     this.mgr = mgr;
49895     this.position  = pos;
49896     this.events = {
49897         /**
49898          * @scope Roo.BasicLayoutRegion
49899          */
49900         
49901         /**
49902          * @event beforeremove
49903          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49904          * @param {Roo.LayoutRegion} this
49905          * @param {Roo.ContentPanel} panel The panel
49906          * @param {Object} e The cancel event object
49907          */
49908         "beforeremove" : true,
49909         /**
49910          * @event invalidated
49911          * Fires when the layout for this region is changed.
49912          * @param {Roo.LayoutRegion} this
49913          */
49914         "invalidated" : true,
49915         /**
49916          * @event visibilitychange
49917          * Fires when this region is shown or hidden 
49918          * @param {Roo.LayoutRegion} this
49919          * @param {Boolean} visibility true or false
49920          */
49921         "visibilitychange" : true,
49922         /**
49923          * @event paneladded
49924          * Fires when a panel is added. 
49925          * @param {Roo.LayoutRegion} this
49926          * @param {Roo.ContentPanel} panel The panel
49927          */
49928         "paneladded" : true,
49929         /**
49930          * @event panelremoved
49931          * Fires when a panel is removed. 
49932          * @param {Roo.LayoutRegion} this
49933          * @param {Roo.ContentPanel} panel The panel
49934          */
49935         "panelremoved" : true,
49936         /**
49937          * @event collapsed
49938          * Fires when this region is collapsed.
49939          * @param {Roo.LayoutRegion} this
49940          */
49941         "collapsed" : true,
49942         /**
49943          * @event expanded
49944          * Fires when this region is expanded.
49945          * @param {Roo.LayoutRegion} this
49946          */
49947         "expanded" : true,
49948         /**
49949          * @event slideshow
49950          * Fires when this region is slid into view.
49951          * @param {Roo.LayoutRegion} this
49952          */
49953         "slideshow" : true,
49954         /**
49955          * @event slidehide
49956          * Fires when this region slides out of view. 
49957          * @param {Roo.LayoutRegion} this
49958          */
49959         "slidehide" : true,
49960         /**
49961          * @event panelactivated
49962          * Fires when a panel is activated. 
49963          * @param {Roo.LayoutRegion} this
49964          * @param {Roo.ContentPanel} panel The activated panel
49965          */
49966         "panelactivated" : true,
49967         /**
49968          * @event resized
49969          * Fires when the user resizes this region. 
49970          * @param {Roo.LayoutRegion} this
49971          * @param {Number} newSize The new size (width for east/west, height for north/south)
49972          */
49973         "resized" : true
49974     };
49975     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49976     this.panels = new Roo.util.MixedCollection();
49977     this.panels.getKey = this.getPanelId.createDelegate(this);
49978     this.box = null;
49979     this.activePanel = null;
49980     // ensure listeners are added...
49981     
49982     if (config.listeners || config.events) {
49983         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
49984             listeners : config.listeners || {},
49985             events : config.events || {}
49986         });
49987     }
49988     
49989     if(skipConfig !== true){
49990         this.applyConfig(config);
49991     }
49992 };
49993
49994 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
49995     getPanelId : function(p){
49996         return p.getId();
49997     },
49998     
49999     applyConfig : function(config){
50000         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50001         this.config = config;
50002         
50003     },
50004     
50005     /**
50006      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
50007      * the width, for horizontal (north, south) the height.
50008      * @param {Number} newSize The new width or height
50009      */
50010     resizeTo : function(newSize){
50011         var el = this.el ? this.el :
50012                  (this.activePanel ? this.activePanel.getEl() : null);
50013         if(el){
50014             switch(this.position){
50015                 case "east":
50016                 case "west":
50017                     el.setWidth(newSize);
50018                     this.fireEvent("resized", this, newSize);
50019                 break;
50020                 case "north":
50021                 case "south":
50022                     el.setHeight(newSize);
50023                     this.fireEvent("resized", this, newSize);
50024                 break;                
50025             }
50026         }
50027     },
50028     
50029     getBox : function(){
50030         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
50031     },
50032     
50033     getMargins : function(){
50034         return this.margins;
50035     },
50036     
50037     updateBox : function(box){
50038         this.box = box;
50039         var el = this.activePanel.getEl();
50040         el.dom.style.left = box.x + "px";
50041         el.dom.style.top = box.y + "px";
50042         this.activePanel.setSize(box.width, box.height);
50043     },
50044     
50045     /**
50046      * Returns the container element for this region.
50047      * @return {Roo.Element}
50048      */
50049     getEl : function(){
50050         return this.activePanel;
50051     },
50052     
50053     /**
50054      * Returns true if this region is currently visible.
50055      * @return {Boolean}
50056      */
50057     isVisible : function(){
50058         return this.activePanel ? true : false;
50059     },
50060     
50061     setActivePanel : function(panel){
50062         panel = this.getPanel(panel);
50063         if(this.activePanel && this.activePanel != panel){
50064             this.activePanel.setActiveState(false);
50065             this.activePanel.getEl().setLeftTop(-10000,-10000);
50066         }
50067         this.activePanel = panel;
50068         panel.setActiveState(true);
50069         if(this.box){
50070             panel.setSize(this.box.width, this.box.height);
50071         }
50072         this.fireEvent("panelactivated", this, panel);
50073         this.fireEvent("invalidated");
50074     },
50075     
50076     /**
50077      * Show the specified panel.
50078      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
50079      * @return {Roo.ContentPanel} The shown panel or null
50080      */
50081     showPanel : function(panel){
50082         if(panel = this.getPanel(panel)){
50083             this.setActivePanel(panel);
50084         }
50085         return panel;
50086     },
50087     
50088     /**
50089      * Get the active panel for this region.
50090      * @return {Roo.ContentPanel} The active panel or null
50091      */
50092     getActivePanel : function(){
50093         return this.activePanel;
50094     },
50095     
50096     /**
50097      * Add the passed ContentPanel(s)
50098      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50099      * @return {Roo.ContentPanel} The panel added (if only one was added)
50100      */
50101     add : function(panel){
50102         if(arguments.length > 1){
50103             for(var i = 0, len = arguments.length; i < len; i++) {
50104                 this.add(arguments[i]);
50105             }
50106             return null;
50107         }
50108         if(this.hasPanel(panel)){
50109             this.showPanel(panel);
50110             return panel;
50111         }
50112         var el = panel.getEl();
50113         if(el.dom.parentNode != this.mgr.el.dom){
50114             this.mgr.el.dom.appendChild(el.dom);
50115         }
50116         if(panel.setRegion){
50117             panel.setRegion(this);
50118         }
50119         this.panels.add(panel);
50120         el.setStyle("position", "absolute");
50121         if(!panel.background){
50122             this.setActivePanel(panel);
50123             if(this.config.initialSize && this.panels.getCount()==1){
50124                 this.resizeTo(this.config.initialSize);
50125             }
50126         }
50127         this.fireEvent("paneladded", this, panel);
50128         return panel;
50129     },
50130     
50131     /**
50132      * Returns true if the panel is in this region.
50133      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50134      * @return {Boolean}
50135      */
50136     hasPanel : function(panel){
50137         if(typeof panel == "object"){ // must be panel obj
50138             panel = panel.getId();
50139         }
50140         return this.getPanel(panel) ? true : false;
50141     },
50142     
50143     /**
50144      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50145      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50146      * @param {Boolean} preservePanel Overrides the config preservePanel option
50147      * @return {Roo.ContentPanel} The panel that was removed
50148      */
50149     remove : function(panel, preservePanel){
50150         panel = this.getPanel(panel);
50151         if(!panel){
50152             return null;
50153         }
50154         var e = {};
50155         this.fireEvent("beforeremove", this, panel, e);
50156         if(e.cancel === true){
50157             return null;
50158         }
50159         var panelId = panel.getId();
50160         this.panels.removeKey(panelId);
50161         return panel;
50162     },
50163     
50164     /**
50165      * Returns the panel specified or null if it's not in this region.
50166      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50167      * @return {Roo.ContentPanel}
50168      */
50169     getPanel : function(id){
50170         if(typeof id == "object"){ // must be panel obj
50171             return id;
50172         }
50173         return this.panels.get(id);
50174     },
50175     
50176     /**
50177      * Returns this regions position (north/south/east/west/center).
50178      * @return {String} 
50179      */
50180     getPosition: function(){
50181         return this.position;    
50182     }
50183 });/*
50184  * Based on:
50185  * Ext JS Library 1.1.1
50186  * Copyright(c) 2006-2007, Ext JS, LLC.
50187  *
50188  * Originally Released Under LGPL - original licence link has changed is not relivant.
50189  *
50190  * Fork - LGPL
50191  * <script type="text/javascript">
50192  */
50193  
50194 /**
50195  * @class Roo.LayoutRegion
50196  * @extends Roo.BasicLayoutRegion
50197  * This class represents a region in a layout manager.
50198  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
50199  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
50200  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
50201  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
50202  * @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})
50203  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
50204  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
50205  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
50206  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
50207  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
50208  * @cfg {String}    title           The title for the region (overrides panel titles)
50209  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
50210  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
50211  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
50212  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
50213  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
50214  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
50215  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
50216  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
50217  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
50218  * @cfg {Boolean}   showPin         True to show a pin button
50219  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
50220  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
50221  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
50222  * @cfg {Number}    width           For East/West panels
50223  * @cfg {Number}    height          For North/South panels
50224  * @cfg {Boolean}   split           To show the splitter
50225  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
50226  */
50227 Roo.LayoutRegion = function(mgr, config, pos){
50228     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
50229     var dh = Roo.DomHelper;
50230     /** This region's container element 
50231     * @type Roo.Element */
50232     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
50233     /** This region's title element 
50234     * @type Roo.Element */
50235
50236     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
50237         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
50238         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
50239     ]}, true);
50240     this.titleEl.enableDisplayMode();
50241     /** This region's title text element 
50242     * @type HTMLElement */
50243     this.titleTextEl = this.titleEl.dom.firstChild;
50244     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
50245     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
50246     this.closeBtn.enableDisplayMode();
50247     this.closeBtn.on("click", this.closeClicked, this);
50248     this.closeBtn.hide();
50249
50250     this.createBody(config);
50251     this.visible = true;
50252     this.collapsed = false;
50253
50254     if(config.hideWhenEmpty){
50255         this.hide();
50256         this.on("paneladded", this.validateVisibility, this);
50257         this.on("panelremoved", this.validateVisibility, this);
50258     }
50259     this.applyConfig(config);
50260 };
50261
50262 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
50263
50264     createBody : function(){
50265         /** This region's body element 
50266         * @type Roo.Element */
50267         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
50268     },
50269
50270     applyConfig : function(c){
50271         if(c.collapsible && this.position != "center" && !this.collapsedEl){
50272             var dh = Roo.DomHelper;
50273             if(c.titlebar !== false){
50274                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
50275                 this.collapseBtn.on("click", this.collapse, this);
50276                 this.collapseBtn.enableDisplayMode();
50277
50278                 if(c.showPin === true || this.showPin){
50279                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
50280                     this.stickBtn.enableDisplayMode();
50281                     this.stickBtn.on("click", this.expand, this);
50282                     this.stickBtn.hide();
50283                 }
50284             }
50285             /** This region's collapsed element
50286             * @type Roo.Element */
50287             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
50288                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
50289             ]}, true);
50290             if(c.floatable !== false){
50291                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
50292                this.collapsedEl.on("click", this.collapseClick, this);
50293             }
50294
50295             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
50296                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
50297                    id: "message", unselectable: "on", style:{"float":"left"}});
50298                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
50299              }
50300             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
50301             this.expandBtn.on("click", this.expand, this);
50302         }
50303         if(this.collapseBtn){
50304             this.collapseBtn.setVisible(c.collapsible == true);
50305         }
50306         this.cmargins = c.cmargins || this.cmargins ||
50307                          (this.position == "west" || this.position == "east" ?
50308                              {top: 0, left: 2, right:2, bottom: 0} :
50309                              {top: 2, left: 0, right:0, bottom: 2});
50310         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50311         this.bottomTabs = c.tabPosition != "top";
50312         this.autoScroll = c.autoScroll || false;
50313         if(this.autoScroll){
50314             this.bodyEl.setStyle("overflow", "auto");
50315         }else{
50316             this.bodyEl.setStyle("overflow", "hidden");
50317         }
50318         //if(c.titlebar !== false){
50319             if((!c.titlebar && !c.title) || c.titlebar === false){
50320                 this.titleEl.hide();
50321             }else{
50322                 this.titleEl.show();
50323                 if(c.title){
50324                     this.titleTextEl.innerHTML = c.title;
50325                 }
50326             }
50327         //}
50328         this.duration = c.duration || .30;
50329         this.slideDuration = c.slideDuration || .45;
50330         this.config = c;
50331         if(c.collapsed){
50332             this.collapse(true);
50333         }
50334         if(c.hidden){
50335             this.hide();
50336         }
50337     },
50338     /**
50339      * Returns true if this region is currently visible.
50340      * @return {Boolean}
50341      */
50342     isVisible : function(){
50343         return this.visible;
50344     },
50345
50346     /**
50347      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
50348      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
50349      */
50350     setCollapsedTitle : function(title){
50351         title = title || "&#160;";
50352         if(this.collapsedTitleTextEl){
50353             this.collapsedTitleTextEl.innerHTML = title;
50354         }
50355     },
50356
50357     getBox : function(){
50358         var b;
50359         if(!this.collapsed){
50360             b = this.el.getBox(false, true);
50361         }else{
50362             b = this.collapsedEl.getBox(false, true);
50363         }
50364         return b;
50365     },
50366
50367     getMargins : function(){
50368         return this.collapsed ? this.cmargins : this.margins;
50369     },
50370
50371     highlight : function(){
50372         this.el.addClass("x-layout-panel-dragover");
50373     },
50374
50375     unhighlight : function(){
50376         this.el.removeClass("x-layout-panel-dragover");
50377     },
50378
50379     updateBox : function(box){
50380         this.box = box;
50381         if(!this.collapsed){
50382             this.el.dom.style.left = box.x + "px";
50383             this.el.dom.style.top = box.y + "px";
50384             this.updateBody(box.width, box.height);
50385         }else{
50386             this.collapsedEl.dom.style.left = box.x + "px";
50387             this.collapsedEl.dom.style.top = box.y + "px";
50388             this.collapsedEl.setSize(box.width, box.height);
50389         }
50390         if(this.tabs){
50391             this.tabs.autoSizeTabs();
50392         }
50393     },
50394
50395     updateBody : function(w, h){
50396         if(w !== null){
50397             this.el.setWidth(w);
50398             w -= this.el.getBorderWidth("rl");
50399             if(this.config.adjustments){
50400                 w += this.config.adjustments[0];
50401             }
50402         }
50403         if(h !== null){
50404             this.el.setHeight(h);
50405             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
50406             h -= this.el.getBorderWidth("tb");
50407             if(this.config.adjustments){
50408                 h += this.config.adjustments[1];
50409             }
50410             this.bodyEl.setHeight(h);
50411             if(this.tabs){
50412                 h = this.tabs.syncHeight(h);
50413             }
50414         }
50415         if(this.panelSize){
50416             w = w !== null ? w : this.panelSize.width;
50417             h = h !== null ? h : this.panelSize.height;
50418         }
50419         if(this.activePanel){
50420             var el = this.activePanel.getEl();
50421             w = w !== null ? w : el.getWidth();
50422             h = h !== null ? h : el.getHeight();
50423             this.panelSize = {width: w, height: h};
50424             this.activePanel.setSize(w, h);
50425         }
50426         if(Roo.isIE && this.tabs){
50427             this.tabs.el.repaint();
50428         }
50429     },
50430
50431     /**
50432      * Returns the container element for this region.
50433      * @return {Roo.Element}
50434      */
50435     getEl : function(){
50436         return this.el;
50437     },
50438
50439     /**
50440      * Hides this region.
50441      */
50442     hide : function(){
50443         if(!this.collapsed){
50444             this.el.dom.style.left = "-2000px";
50445             this.el.hide();
50446         }else{
50447             this.collapsedEl.dom.style.left = "-2000px";
50448             this.collapsedEl.hide();
50449         }
50450         this.visible = false;
50451         this.fireEvent("visibilitychange", this, false);
50452     },
50453
50454     /**
50455      * Shows this region if it was previously hidden.
50456      */
50457     show : function(){
50458         if(!this.collapsed){
50459             this.el.show();
50460         }else{
50461             this.collapsedEl.show();
50462         }
50463         this.visible = true;
50464         this.fireEvent("visibilitychange", this, true);
50465     },
50466
50467     closeClicked : function(){
50468         if(this.activePanel){
50469             this.remove(this.activePanel);
50470         }
50471     },
50472
50473     collapseClick : function(e){
50474         if(this.isSlid){
50475            e.stopPropagation();
50476            this.slideIn();
50477         }else{
50478            e.stopPropagation();
50479            this.slideOut();
50480         }
50481     },
50482
50483     /**
50484      * Collapses this region.
50485      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
50486      */
50487     collapse : function(skipAnim){
50488         if(this.collapsed) return;
50489         this.collapsed = true;
50490         if(this.split){
50491             this.split.el.hide();
50492         }
50493         if(this.config.animate && skipAnim !== true){
50494             this.fireEvent("invalidated", this);
50495             this.animateCollapse();
50496         }else{
50497             this.el.setLocation(-20000,-20000);
50498             this.el.hide();
50499             this.collapsedEl.show();
50500             this.fireEvent("collapsed", this);
50501             this.fireEvent("invalidated", this);
50502         }
50503     },
50504
50505     animateCollapse : function(){
50506         // overridden
50507     },
50508
50509     /**
50510      * Expands this region if it was previously collapsed.
50511      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
50512      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
50513      */
50514     expand : function(e, skipAnim){
50515         if(e) e.stopPropagation();
50516         if(!this.collapsed || this.el.hasActiveFx()) return;
50517         if(this.isSlid){
50518             this.afterSlideIn();
50519             skipAnim = true;
50520         }
50521         this.collapsed = false;
50522         if(this.config.animate && skipAnim !== true){
50523             this.animateExpand();
50524         }else{
50525             this.el.show();
50526             if(this.split){
50527                 this.split.el.show();
50528             }
50529             this.collapsedEl.setLocation(-2000,-2000);
50530             this.collapsedEl.hide();
50531             this.fireEvent("invalidated", this);
50532             this.fireEvent("expanded", this);
50533         }
50534     },
50535
50536     animateExpand : function(){
50537         // overridden
50538     },
50539
50540     initTabs : function()
50541     {
50542         this.bodyEl.setStyle("overflow", "hidden");
50543         var ts = new Roo.TabPanel(
50544                 this.bodyEl.dom,
50545                 {
50546                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
50547                     disableTooltips: this.config.disableTabTips,
50548                     toolbar : this.config.toolbar
50549                 }
50550         );
50551         if(this.config.hideTabs){
50552             ts.stripWrap.setDisplayed(false);
50553         }
50554         this.tabs = ts;
50555         ts.resizeTabs = this.config.resizeTabs === true;
50556         ts.minTabWidth = this.config.minTabWidth || 40;
50557         ts.maxTabWidth = this.config.maxTabWidth || 250;
50558         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
50559         ts.monitorResize = false;
50560         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50561         ts.bodyEl.addClass('x-layout-tabs-body');
50562         this.panels.each(this.initPanelAsTab, this);
50563     },
50564
50565     initPanelAsTab : function(panel){
50566         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
50567                     this.config.closeOnTab && panel.isClosable());
50568         if(panel.tabTip !== undefined){
50569             ti.setTooltip(panel.tabTip);
50570         }
50571         ti.on("activate", function(){
50572               this.setActivePanel(panel);
50573         }, this);
50574         if(this.config.closeOnTab){
50575             ti.on("beforeclose", function(t, e){
50576                 e.cancel = true;
50577                 this.remove(panel);
50578             }, this);
50579         }
50580         return ti;
50581     },
50582
50583     updatePanelTitle : function(panel, title){
50584         if(this.activePanel == panel){
50585             this.updateTitle(title);
50586         }
50587         if(this.tabs){
50588             var ti = this.tabs.getTab(panel.getEl().id);
50589             ti.setText(title);
50590             if(panel.tabTip !== undefined){
50591                 ti.setTooltip(panel.tabTip);
50592             }
50593         }
50594     },
50595
50596     updateTitle : function(title){
50597         if(this.titleTextEl && !this.config.title){
50598             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
50599         }
50600     },
50601
50602     setActivePanel : function(panel){
50603         panel = this.getPanel(panel);
50604         if(this.activePanel && this.activePanel != panel){
50605             this.activePanel.setActiveState(false);
50606         }
50607         this.activePanel = panel;
50608         panel.setActiveState(true);
50609         if(this.panelSize){
50610             panel.setSize(this.panelSize.width, this.panelSize.height);
50611         }
50612         if(this.closeBtn){
50613             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50614         }
50615         this.updateTitle(panel.getTitle());
50616         if(this.tabs){
50617             this.fireEvent("invalidated", this);
50618         }
50619         this.fireEvent("panelactivated", this, panel);
50620     },
50621
50622     /**
50623      * Shows the specified panel.
50624      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50625      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50626      */
50627     showPanel : function(panel)
50628     {
50629         panel = this.getPanel(panel);
50630         if(panel){
50631             if(this.tabs){
50632                 var tab = this.tabs.getTab(panel.getEl().id);
50633                 if(tab.isHidden()){
50634                     this.tabs.unhideTab(tab.id);
50635                 }
50636                 tab.activate();
50637             }else{
50638                 this.setActivePanel(panel);
50639             }
50640         }
50641         return panel;
50642     },
50643
50644     /**
50645      * Get the active panel for this region.
50646      * @return {Roo.ContentPanel} The active panel or null
50647      */
50648     getActivePanel : function(){
50649         return this.activePanel;
50650     },
50651
50652     validateVisibility : function(){
50653         if(this.panels.getCount() < 1){
50654             this.updateTitle("&#160;");
50655             this.closeBtn.hide();
50656             this.hide();
50657         }else{
50658             if(!this.isVisible()){
50659                 this.show();
50660             }
50661         }
50662     },
50663
50664     /**
50665      * Adds the passed ContentPanel(s) to this region.
50666      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50667      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50668      */
50669     add : function(panel){
50670         if(arguments.length > 1){
50671             for(var i = 0, len = arguments.length; i < len; i++) {
50672                 this.add(arguments[i]);
50673             }
50674             return null;
50675         }
50676         if(this.hasPanel(panel)){
50677             this.showPanel(panel);
50678             return panel;
50679         }
50680         panel.setRegion(this);
50681         this.panels.add(panel);
50682         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50683             this.bodyEl.dom.appendChild(panel.getEl().dom);
50684             if(panel.background !== true){
50685                 this.setActivePanel(panel);
50686             }
50687             this.fireEvent("paneladded", this, panel);
50688             return panel;
50689         }
50690         if(!this.tabs){
50691             this.initTabs();
50692         }else{
50693             this.initPanelAsTab(panel);
50694         }
50695         if(panel.background !== true){
50696             this.tabs.activate(panel.getEl().id);
50697         }
50698         this.fireEvent("paneladded", this, panel);
50699         return panel;
50700     },
50701
50702     /**
50703      * Hides the tab for the specified panel.
50704      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50705      */
50706     hidePanel : function(panel){
50707         if(this.tabs && (panel = this.getPanel(panel))){
50708             this.tabs.hideTab(panel.getEl().id);
50709         }
50710     },
50711
50712     /**
50713      * Unhides the tab for a previously hidden panel.
50714      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50715      */
50716     unhidePanel : function(panel){
50717         if(this.tabs && (panel = this.getPanel(panel))){
50718             this.tabs.unhideTab(panel.getEl().id);
50719         }
50720     },
50721
50722     clearPanels : function(){
50723         while(this.panels.getCount() > 0){
50724              this.remove(this.panels.first());
50725         }
50726     },
50727
50728     /**
50729      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50730      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50731      * @param {Boolean} preservePanel Overrides the config preservePanel option
50732      * @return {Roo.ContentPanel} The panel that was removed
50733      */
50734     remove : function(panel, preservePanel){
50735         panel = this.getPanel(panel);
50736         if(!panel){
50737             return null;
50738         }
50739         var e = {};
50740         this.fireEvent("beforeremove", this, panel, e);
50741         if(e.cancel === true){
50742             return null;
50743         }
50744         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50745         var panelId = panel.getId();
50746         this.panels.removeKey(panelId);
50747         if(preservePanel){
50748             document.body.appendChild(panel.getEl().dom);
50749         }
50750         if(this.tabs){
50751             this.tabs.removeTab(panel.getEl().id);
50752         }else if (!preservePanel){
50753             this.bodyEl.dom.removeChild(panel.getEl().dom);
50754         }
50755         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50756             var p = this.panels.first();
50757             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50758             tempEl.appendChild(p.getEl().dom);
50759             this.bodyEl.update("");
50760             this.bodyEl.dom.appendChild(p.getEl().dom);
50761             tempEl = null;
50762             this.updateTitle(p.getTitle());
50763             this.tabs = null;
50764             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50765             this.setActivePanel(p);
50766         }
50767         panel.setRegion(null);
50768         if(this.activePanel == panel){
50769             this.activePanel = null;
50770         }
50771         if(this.config.autoDestroy !== false && preservePanel !== true){
50772             try{panel.destroy();}catch(e){}
50773         }
50774         this.fireEvent("panelremoved", this, panel);
50775         return panel;
50776     },
50777
50778     /**
50779      * Returns the TabPanel component used by this region
50780      * @return {Roo.TabPanel}
50781      */
50782     getTabs : function(){
50783         return this.tabs;
50784     },
50785
50786     createTool : function(parentEl, className){
50787         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50788             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50789         btn.addClassOnOver("x-layout-tools-button-over");
50790         return btn;
50791     }
50792 });/*
50793  * Based on:
50794  * Ext JS Library 1.1.1
50795  * Copyright(c) 2006-2007, Ext JS, LLC.
50796  *
50797  * Originally Released Under LGPL - original licence link has changed is not relivant.
50798  *
50799  * Fork - LGPL
50800  * <script type="text/javascript">
50801  */
50802  
50803
50804
50805 /**
50806  * @class Roo.SplitLayoutRegion
50807  * @extends Roo.LayoutRegion
50808  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50809  */
50810 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50811     this.cursor = cursor;
50812     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50813 };
50814
50815 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50816     splitTip : "Drag to resize.",
50817     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50818     useSplitTips : false,
50819
50820     applyConfig : function(config){
50821         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50822         if(config.split){
50823             if(!this.split){
50824                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50825                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50826                 /** The SplitBar for this region 
50827                 * @type Roo.SplitBar */
50828                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50829                 this.split.on("moved", this.onSplitMove, this);
50830                 this.split.useShim = config.useShim === true;
50831                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50832                 if(this.useSplitTips){
50833                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50834                 }
50835                 if(config.collapsible){
50836                     this.split.el.on("dblclick", this.collapse,  this);
50837                 }
50838             }
50839             if(typeof config.minSize != "undefined"){
50840                 this.split.minSize = config.minSize;
50841             }
50842             if(typeof config.maxSize != "undefined"){
50843                 this.split.maxSize = config.maxSize;
50844             }
50845             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50846                 this.hideSplitter();
50847             }
50848         }
50849     },
50850
50851     getHMaxSize : function(){
50852          var cmax = this.config.maxSize || 10000;
50853          var center = this.mgr.getRegion("center");
50854          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50855     },
50856
50857     getVMaxSize : function(){
50858          var cmax = this.config.maxSize || 10000;
50859          var center = this.mgr.getRegion("center");
50860          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50861     },
50862
50863     onSplitMove : function(split, newSize){
50864         this.fireEvent("resized", this, newSize);
50865     },
50866     
50867     /** 
50868      * Returns the {@link Roo.SplitBar} for this region.
50869      * @return {Roo.SplitBar}
50870      */
50871     getSplitBar : function(){
50872         return this.split;
50873     },
50874     
50875     hide : function(){
50876         this.hideSplitter();
50877         Roo.SplitLayoutRegion.superclass.hide.call(this);
50878     },
50879
50880     hideSplitter : function(){
50881         if(this.split){
50882             this.split.el.setLocation(-2000,-2000);
50883             this.split.el.hide();
50884         }
50885     },
50886
50887     show : function(){
50888         if(this.split){
50889             this.split.el.show();
50890         }
50891         Roo.SplitLayoutRegion.superclass.show.call(this);
50892     },
50893     
50894     beforeSlide: function(){
50895         if(Roo.isGecko){// firefox overflow auto bug workaround
50896             this.bodyEl.clip();
50897             if(this.tabs) this.tabs.bodyEl.clip();
50898             if(this.activePanel){
50899                 this.activePanel.getEl().clip();
50900                 
50901                 if(this.activePanel.beforeSlide){
50902                     this.activePanel.beforeSlide();
50903                 }
50904             }
50905         }
50906     },
50907     
50908     afterSlide : function(){
50909         if(Roo.isGecko){// firefox overflow auto bug workaround
50910             this.bodyEl.unclip();
50911             if(this.tabs) this.tabs.bodyEl.unclip();
50912             if(this.activePanel){
50913                 this.activePanel.getEl().unclip();
50914                 if(this.activePanel.afterSlide){
50915                     this.activePanel.afterSlide();
50916                 }
50917             }
50918         }
50919     },
50920
50921     initAutoHide : function(){
50922         if(this.autoHide !== false){
50923             if(!this.autoHideHd){
50924                 var st = new Roo.util.DelayedTask(this.slideIn, this);
50925                 this.autoHideHd = {
50926                     "mouseout": function(e){
50927                         if(!e.within(this.el, true)){
50928                             st.delay(500);
50929                         }
50930                     },
50931                     "mouseover" : function(e){
50932                         st.cancel();
50933                     },
50934                     scope : this
50935                 };
50936             }
50937             this.el.on(this.autoHideHd);
50938         }
50939     },
50940
50941     clearAutoHide : function(){
50942         if(this.autoHide !== false){
50943             this.el.un("mouseout", this.autoHideHd.mouseout);
50944             this.el.un("mouseover", this.autoHideHd.mouseover);
50945         }
50946     },
50947
50948     clearMonitor : function(){
50949         Roo.get(document).un("click", this.slideInIf, this);
50950     },
50951
50952     // these names are backwards but not changed for compat
50953     slideOut : function(){
50954         if(this.isSlid || this.el.hasActiveFx()){
50955             return;
50956         }
50957         this.isSlid = true;
50958         if(this.collapseBtn){
50959             this.collapseBtn.hide();
50960         }
50961         this.closeBtnState = this.closeBtn.getStyle('display');
50962         this.closeBtn.hide();
50963         if(this.stickBtn){
50964             this.stickBtn.show();
50965         }
50966         this.el.show();
50967         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50968         this.beforeSlide();
50969         this.el.setStyle("z-index", 10001);
50970         this.el.slideIn(this.getSlideAnchor(), {
50971             callback: function(){
50972                 this.afterSlide();
50973                 this.initAutoHide();
50974                 Roo.get(document).on("click", this.slideInIf, this);
50975                 this.fireEvent("slideshow", this);
50976             },
50977             scope: this,
50978             block: true
50979         });
50980     },
50981
50982     afterSlideIn : function(){
50983         this.clearAutoHide();
50984         this.isSlid = false;
50985         this.clearMonitor();
50986         this.el.setStyle("z-index", "");
50987         if(this.collapseBtn){
50988             this.collapseBtn.show();
50989         }
50990         this.closeBtn.setStyle('display', this.closeBtnState);
50991         if(this.stickBtn){
50992             this.stickBtn.hide();
50993         }
50994         this.fireEvent("slidehide", this);
50995     },
50996
50997     slideIn : function(cb){
50998         if(!this.isSlid || this.el.hasActiveFx()){
50999             Roo.callback(cb);
51000             return;
51001         }
51002         this.isSlid = false;
51003         this.beforeSlide();
51004         this.el.slideOut(this.getSlideAnchor(), {
51005             callback: function(){
51006                 this.el.setLeftTop(-10000, -10000);
51007                 this.afterSlide();
51008                 this.afterSlideIn();
51009                 Roo.callback(cb);
51010             },
51011             scope: this,
51012             block: true
51013         });
51014     },
51015     
51016     slideInIf : function(e){
51017         if(!e.within(this.el)){
51018             this.slideIn();
51019         }
51020     },
51021
51022     animateCollapse : function(){
51023         this.beforeSlide();
51024         this.el.setStyle("z-index", 20000);
51025         var anchor = this.getSlideAnchor();
51026         this.el.slideOut(anchor, {
51027             callback : function(){
51028                 this.el.setStyle("z-index", "");
51029                 this.collapsedEl.slideIn(anchor, {duration:.3});
51030                 this.afterSlide();
51031                 this.el.setLocation(-10000,-10000);
51032                 this.el.hide();
51033                 this.fireEvent("collapsed", this);
51034             },
51035             scope: this,
51036             block: true
51037         });
51038     },
51039
51040     animateExpand : function(){
51041         this.beforeSlide();
51042         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
51043         this.el.setStyle("z-index", 20000);
51044         this.collapsedEl.hide({
51045             duration:.1
51046         });
51047         this.el.slideIn(this.getSlideAnchor(), {
51048             callback : function(){
51049                 this.el.setStyle("z-index", "");
51050                 this.afterSlide();
51051                 if(this.split){
51052                     this.split.el.show();
51053                 }
51054                 this.fireEvent("invalidated", this);
51055                 this.fireEvent("expanded", this);
51056             },
51057             scope: this,
51058             block: true
51059         });
51060     },
51061
51062     anchors : {
51063         "west" : "left",
51064         "east" : "right",
51065         "north" : "top",
51066         "south" : "bottom"
51067     },
51068
51069     sanchors : {
51070         "west" : "l",
51071         "east" : "r",
51072         "north" : "t",
51073         "south" : "b"
51074     },
51075
51076     canchors : {
51077         "west" : "tl-tr",
51078         "east" : "tr-tl",
51079         "north" : "tl-bl",
51080         "south" : "bl-tl"
51081     },
51082
51083     getAnchor : function(){
51084         return this.anchors[this.position];
51085     },
51086
51087     getCollapseAnchor : function(){
51088         return this.canchors[this.position];
51089     },
51090
51091     getSlideAnchor : function(){
51092         return this.sanchors[this.position];
51093     },
51094
51095     getAlignAdj : function(){
51096         var cm = this.cmargins;
51097         switch(this.position){
51098             case "west":
51099                 return [0, 0];
51100             break;
51101             case "east":
51102                 return [0, 0];
51103             break;
51104             case "north":
51105                 return [0, 0];
51106             break;
51107             case "south":
51108                 return [0, 0];
51109             break;
51110         }
51111     },
51112
51113     getExpandAdj : function(){
51114         var c = this.collapsedEl, cm = this.cmargins;
51115         switch(this.position){
51116             case "west":
51117                 return [-(cm.right+c.getWidth()+cm.left), 0];
51118             break;
51119             case "east":
51120                 return [cm.right+c.getWidth()+cm.left, 0];
51121             break;
51122             case "north":
51123                 return [0, -(cm.top+cm.bottom+c.getHeight())];
51124             break;
51125             case "south":
51126                 return [0, cm.top+cm.bottom+c.getHeight()];
51127             break;
51128         }
51129     }
51130 });/*
51131  * Based on:
51132  * Ext JS Library 1.1.1
51133  * Copyright(c) 2006-2007, Ext JS, LLC.
51134  *
51135  * Originally Released Under LGPL - original licence link has changed is not relivant.
51136  *
51137  * Fork - LGPL
51138  * <script type="text/javascript">
51139  */
51140 /*
51141  * These classes are private internal classes
51142  */
51143 Roo.CenterLayoutRegion = function(mgr, config){
51144     Roo.LayoutRegion.call(this, mgr, config, "center");
51145     this.visible = true;
51146     this.minWidth = config.minWidth || 20;
51147     this.minHeight = config.minHeight || 20;
51148 };
51149
51150 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
51151     hide : function(){
51152         // center panel can't be hidden
51153     },
51154     
51155     show : function(){
51156         // center panel can't be hidden
51157     },
51158     
51159     getMinWidth: function(){
51160         return this.minWidth;
51161     },
51162     
51163     getMinHeight: function(){
51164         return this.minHeight;
51165     }
51166 });
51167
51168
51169 Roo.NorthLayoutRegion = function(mgr, config){
51170     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
51171     if(this.split){
51172         this.split.placement = Roo.SplitBar.TOP;
51173         this.split.orientation = Roo.SplitBar.VERTICAL;
51174         this.split.el.addClass("x-layout-split-v");
51175     }
51176     var size = config.initialSize || config.height;
51177     if(typeof size != "undefined"){
51178         this.el.setHeight(size);
51179     }
51180 };
51181 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
51182     orientation: Roo.SplitBar.VERTICAL,
51183     getBox : function(){
51184         if(this.collapsed){
51185             return this.collapsedEl.getBox();
51186         }
51187         var box = this.el.getBox();
51188         if(this.split){
51189             box.height += this.split.el.getHeight();
51190         }
51191         return box;
51192     },
51193     
51194     updateBox : function(box){
51195         if(this.split && !this.collapsed){
51196             box.height -= this.split.el.getHeight();
51197             this.split.el.setLeft(box.x);
51198             this.split.el.setTop(box.y+box.height);
51199             this.split.el.setWidth(box.width);
51200         }
51201         if(this.collapsed){
51202             this.updateBody(box.width, null);
51203         }
51204         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51205     }
51206 });
51207
51208 Roo.SouthLayoutRegion = function(mgr, config){
51209     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
51210     if(this.split){
51211         this.split.placement = Roo.SplitBar.BOTTOM;
51212         this.split.orientation = Roo.SplitBar.VERTICAL;
51213         this.split.el.addClass("x-layout-split-v");
51214     }
51215     var size = config.initialSize || config.height;
51216     if(typeof size != "undefined"){
51217         this.el.setHeight(size);
51218     }
51219 };
51220 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
51221     orientation: Roo.SplitBar.VERTICAL,
51222     getBox : function(){
51223         if(this.collapsed){
51224             return this.collapsedEl.getBox();
51225         }
51226         var box = this.el.getBox();
51227         if(this.split){
51228             var sh = this.split.el.getHeight();
51229             box.height += sh;
51230             box.y -= sh;
51231         }
51232         return box;
51233     },
51234     
51235     updateBox : function(box){
51236         if(this.split && !this.collapsed){
51237             var sh = this.split.el.getHeight();
51238             box.height -= sh;
51239             box.y += sh;
51240             this.split.el.setLeft(box.x);
51241             this.split.el.setTop(box.y-sh);
51242             this.split.el.setWidth(box.width);
51243         }
51244         if(this.collapsed){
51245             this.updateBody(box.width, null);
51246         }
51247         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51248     }
51249 });
51250
51251 Roo.EastLayoutRegion = function(mgr, config){
51252     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
51253     if(this.split){
51254         this.split.placement = Roo.SplitBar.RIGHT;
51255         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51256         this.split.el.addClass("x-layout-split-h");
51257     }
51258     var size = config.initialSize || config.width;
51259     if(typeof size != "undefined"){
51260         this.el.setWidth(size);
51261     }
51262 };
51263 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
51264     orientation: Roo.SplitBar.HORIZONTAL,
51265     getBox : function(){
51266         if(this.collapsed){
51267             return this.collapsedEl.getBox();
51268         }
51269         var box = this.el.getBox();
51270         if(this.split){
51271             var sw = this.split.el.getWidth();
51272             box.width += sw;
51273             box.x -= sw;
51274         }
51275         return box;
51276     },
51277
51278     updateBox : function(box){
51279         if(this.split && !this.collapsed){
51280             var sw = this.split.el.getWidth();
51281             box.width -= sw;
51282             this.split.el.setLeft(box.x);
51283             this.split.el.setTop(box.y);
51284             this.split.el.setHeight(box.height);
51285             box.x += sw;
51286         }
51287         if(this.collapsed){
51288             this.updateBody(null, box.height);
51289         }
51290         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51291     }
51292 });
51293
51294 Roo.WestLayoutRegion = function(mgr, config){
51295     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
51296     if(this.split){
51297         this.split.placement = Roo.SplitBar.LEFT;
51298         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51299         this.split.el.addClass("x-layout-split-h");
51300     }
51301     var size = config.initialSize || config.width;
51302     if(typeof size != "undefined"){
51303         this.el.setWidth(size);
51304     }
51305 };
51306 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
51307     orientation: Roo.SplitBar.HORIZONTAL,
51308     getBox : function(){
51309         if(this.collapsed){
51310             return this.collapsedEl.getBox();
51311         }
51312         var box = this.el.getBox();
51313         if(this.split){
51314             box.width += this.split.el.getWidth();
51315         }
51316         return box;
51317     },
51318     
51319     updateBox : function(box){
51320         if(this.split && !this.collapsed){
51321             var sw = this.split.el.getWidth();
51322             box.width -= sw;
51323             this.split.el.setLeft(box.x+box.width);
51324             this.split.el.setTop(box.y);
51325             this.split.el.setHeight(box.height);
51326         }
51327         if(this.collapsed){
51328             this.updateBody(null, box.height);
51329         }
51330         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51331     }
51332 });
51333 /*
51334  * Based on:
51335  * Ext JS Library 1.1.1
51336  * Copyright(c) 2006-2007, Ext JS, LLC.
51337  *
51338  * Originally Released Under LGPL - original licence link has changed is not relivant.
51339  *
51340  * Fork - LGPL
51341  * <script type="text/javascript">
51342  */
51343  
51344  
51345 /*
51346  * Private internal class for reading and applying state
51347  */
51348 Roo.LayoutStateManager = function(layout){
51349      // default empty state
51350      this.state = {
51351         north: {},
51352         south: {},
51353         east: {},
51354         west: {}       
51355     };
51356 };
51357
51358 Roo.LayoutStateManager.prototype = {
51359     init : function(layout, provider){
51360         this.provider = provider;
51361         var state = provider.get(layout.id+"-layout-state");
51362         if(state){
51363             var wasUpdating = layout.isUpdating();
51364             if(!wasUpdating){
51365                 layout.beginUpdate();
51366             }
51367             for(var key in state){
51368                 if(typeof state[key] != "function"){
51369                     var rstate = state[key];
51370                     var r = layout.getRegion(key);
51371                     if(r && rstate){
51372                         if(rstate.size){
51373                             r.resizeTo(rstate.size);
51374                         }
51375                         if(rstate.collapsed == true){
51376                             r.collapse(true);
51377                         }else{
51378                             r.expand(null, true);
51379                         }
51380                     }
51381                 }
51382             }
51383             if(!wasUpdating){
51384                 layout.endUpdate();
51385             }
51386             this.state = state; 
51387         }
51388         this.layout = layout;
51389         layout.on("regionresized", this.onRegionResized, this);
51390         layout.on("regioncollapsed", this.onRegionCollapsed, this);
51391         layout.on("regionexpanded", this.onRegionExpanded, this);
51392     },
51393     
51394     storeState : function(){
51395         this.provider.set(this.layout.id+"-layout-state", this.state);
51396     },
51397     
51398     onRegionResized : function(region, newSize){
51399         this.state[region.getPosition()].size = newSize;
51400         this.storeState();
51401     },
51402     
51403     onRegionCollapsed : function(region){
51404         this.state[region.getPosition()].collapsed = true;
51405         this.storeState();
51406     },
51407     
51408     onRegionExpanded : function(region){
51409         this.state[region.getPosition()].collapsed = false;
51410         this.storeState();
51411     }
51412 };/*
51413  * Based on:
51414  * Ext JS Library 1.1.1
51415  * Copyright(c) 2006-2007, Ext JS, LLC.
51416  *
51417  * Originally Released Under LGPL - original licence link has changed is not relivant.
51418  *
51419  * Fork - LGPL
51420  * <script type="text/javascript">
51421  */
51422 /**
51423  * @class Roo.ContentPanel
51424  * @extends Roo.util.Observable
51425  * A basic ContentPanel element.
51426  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
51427  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
51428  * @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
51429  * @cfg {Boolean}   closable      True if the panel can be closed/removed
51430  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
51431  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
51432  * @cfg {Toolbar}   toolbar       A toolbar for this panel
51433  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
51434  * @cfg {String} title          The title for this panel
51435  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
51436  * @cfg {String} url            Calls {@link #setUrl} with this value
51437  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
51438  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
51439  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
51440  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
51441
51442  * @constructor
51443  * Create a new ContentPanel.
51444  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
51445  * @param {String/Object} config A string to set only the title or a config object
51446  * @param {String} content (optional) Set the HTML content for this panel
51447  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
51448  */
51449 Roo.ContentPanel = function(el, config, content){
51450     
51451      
51452     /*
51453     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
51454         config = el;
51455         el = Roo.id();
51456     }
51457     if (config && config.parentLayout) { 
51458         el = config.parentLayout.el.createChild(); 
51459     }
51460     */
51461     if(el.autoCreate){ // xtype is available if this is called from factory
51462         config = el;
51463         el = Roo.id();
51464     }
51465     this.el = Roo.get(el);
51466     if(!this.el && config && config.autoCreate){
51467         if(typeof config.autoCreate == "object"){
51468             if(!config.autoCreate.id){
51469                 config.autoCreate.id = config.id||el;
51470             }
51471             this.el = Roo.DomHelper.append(document.body,
51472                         config.autoCreate, true);
51473         }else{
51474             this.el = Roo.DomHelper.append(document.body,
51475                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
51476         }
51477     }
51478     this.closable = false;
51479     this.loaded = false;
51480     this.active = false;
51481     if(typeof config == "string"){
51482         this.title = config;
51483     }else{
51484         Roo.apply(this, config);
51485     }
51486     
51487     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
51488         this.wrapEl = this.el.wrap();
51489         this.toolbar.container = this.el.insertSibling(false, 'before');
51490         this.toolbar = new Roo.Toolbar(this.toolbar);
51491     }
51492     
51493     // xtype created footer. - not sure if will work as we normally have to render first..
51494     if (this.footer && !this.footer.el && this.footer.xtype) {
51495         if (!this.wrapEl) {
51496             this.wrapEl = this.el.wrap();
51497         }
51498     
51499         this.footer.container = this.wrapEl.createChild();
51500          
51501         this.footer = Roo.factory(this.footer, Roo);
51502         
51503     }
51504     
51505     if(this.resizeEl){
51506         this.resizeEl = Roo.get(this.resizeEl, true);
51507     }else{
51508         this.resizeEl = this.el;
51509     }
51510     // handle view.xtype
51511     
51512  
51513     
51514     
51515     this.addEvents({
51516         /**
51517          * @event activate
51518          * Fires when this panel is activated. 
51519          * @param {Roo.ContentPanel} this
51520          */
51521         "activate" : true,
51522         /**
51523          * @event deactivate
51524          * Fires when this panel is activated. 
51525          * @param {Roo.ContentPanel} this
51526          */
51527         "deactivate" : true,
51528
51529         /**
51530          * @event resize
51531          * Fires when this panel is resized if fitToFrame is true.
51532          * @param {Roo.ContentPanel} this
51533          * @param {Number} width The width after any component adjustments
51534          * @param {Number} height The height after any component adjustments
51535          */
51536         "resize" : true,
51537         
51538          /**
51539          * @event render
51540          * Fires when this tab is created
51541          * @param {Roo.ContentPanel} this
51542          */
51543         "render" : true
51544         
51545         
51546         
51547     });
51548     
51549
51550     
51551     
51552     if(this.autoScroll){
51553         this.resizeEl.setStyle("overflow", "auto");
51554     } else {
51555         // fix randome scrolling
51556         this.el.on('scroll', function() {
51557             Roo.log('fix random scolling');
51558             this.scrollTo('top',0); 
51559         });
51560     }
51561     content = content || this.content;
51562     if(content){
51563         this.setContent(content);
51564     }
51565     if(config && config.url){
51566         this.setUrl(this.url, this.params, this.loadOnce);
51567     }
51568     
51569     
51570     
51571     Roo.ContentPanel.superclass.constructor.call(this);
51572     
51573     if (this.view && typeof(this.view.xtype) != 'undefined') {
51574         this.view.el = this.el.appendChild(document.createElement("div"));
51575         this.view = Roo.factory(this.view); 
51576         this.view.render  &&  this.view.render(false, '');  
51577     }
51578     
51579     
51580     this.fireEvent('render', this);
51581 };
51582
51583 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
51584     tabTip:'',
51585     setRegion : function(region){
51586         this.region = region;
51587         if(region){
51588            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
51589         }else{
51590            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
51591         } 
51592     },
51593     
51594     /**
51595      * Returns the toolbar for this Panel if one was configured. 
51596      * @return {Roo.Toolbar} 
51597      */
51598     getToolbar : function(){
51599         return this.toolbar;
51600     },
51601     
51602     setActiveState : function(active){
51603         this.active = active;
51604         if(!active){
51605             this.fireEvent("deactivate", this);
51606         }else{
51607             this.fireEvent("activate", this);
51608         }
51609     },
51610     /**
51611      * Updates this panel's element
51612      * @param {String} content The new content
51613      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51614     */
51615     setContent : function(content, loadScripts){
51616         this.el.update(content, loadScripts);
51617     },
51618
51619     ignoreResize : function(w, h){
51620         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51621             return true;
51622         }else{
51623             this.lastSize = {width: w, height: h};
51624             return false;
51625         }
51626     },
51627     /**
51628      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51629      * @return {Roo.UpdateManager} The UpdateManager
51630      */
51631     getUpdateManager : function(){
51632         return this.el.getUpdateManager();
51633     },
51634      /**
51635      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51636      * @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:
51637 <pre><code>
51638 panel.load({
51639     url: "your-url.php",
51640     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51641     callback: yourFunction,
51642     scope: yourObject, //(optional scope)
51643     discardUrl: false,
51644     nocache: false,
51645     text: "Loading...",
51646     timeout: 30,
51647     scripts: false
51648 });
51649 </code></pre>
51650      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51651      * 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.
51652      * @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}
51653      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51654      * @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.
51655      * @return {Roo.ContentPanel} this
51656      */
51657     load : function(){
51658         var um = this.el.getUpdateManager();
51659         um.update.apply(um, arguments);
51660         return this;
51661     },
51662
51663
51664     /**
51665      * 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.
51666      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51667      * @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)
51668      * @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)
51669      * @return {Roo.UpdateManager} The UpdateManager
51670      */
51671     setUrl : function(url, params, loadOnce){
51672         if(this.refreshDelegate){
51673             this.removeListener("activate", this.refreshDelegate);
51674         }
51675         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51676         this.on("activate", this.refreshDelegate);
51677         return this.el.getUpdateManager();
51678     },
51679     
51680     _handleRefresh : function(url, params, loadOnce){
51681         if(!loadOnce || !this.loaded){
51682             var updater = this.el.getUpdateManager();
51683             updater.update(url, params, this._setLoaded.createDelegate(this));
51684         }
51685     },
51686     
51687     _setLoaded : function(){
51688         this.loaded = true;
51689     }, 
51690     
51691     /**
51692      * Returns this panel's id
51693      * @return {String} 
51694      */
51695     getId : function(){
51696         return this.el.id;
51697     },
51698     
51699     /** 
51700      * Returns this panel's element - used by regiosn to add.
51701      * @return {Roo.Element} 
51702      */
51703     getEl : function(){
51704         return this.wrapEl || this.el;
51705     },
51706     
51707     adjustForComponents : function(width, height)
51708     {
51709         //Roo.log('adjustForComponents ');
51710         if(this.resizeEl != this.el){
51711             width -= this.el.getFrameWidth('lr');
51712             height -= this.el.getFrameWidth('tb');
51713         }
51714         if(this.toolbar){
51715             var te = this.toolbar.getEl();
51716             height -= te.getHeight();
51717             te.setWidth(width);
51718         }
51719         if(this.footer){
51720             var te = this.footer.getEl();
51721             Roo.log("footer:" + te.getHeight());
51722             
51723             height -= te.getHeight();
51724             te.setWidth(width);
51725         }
51726         
51727         
51728         if(this.adjustments){
51729             width += this.adjustments[0];
51730             height += this.adjustments[1];
51731         }
51732         return {"width": width, "height": height};
51733     },
51734     
51735     setSize : function(width, height){
51736         if(this.fitToFrame && !this.ignoreResize(width, height)){
51737             if(this.fitContainer && this.resizeEl != this.el){
51738                 this.el.setSize(width, height);
51739             }
51740             var size = this.adjustForComponents(width, height);
51741             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51742             this.fireEvent('resize', this, size.width, size.height);
51743         }
51744     },
51745     
51746     /**
51747      * Returns this panel's title
51748      * @return {String} 
51749      */
51750     getTitle : function(){
51751         return this.title;
51752     },
51753     
51754     /**
51755      * Set this panel's title
51756      * @param {String} title
51757      */
51758     setTitle : function(title){
51759         this.title = title;
51760         if(this.region){
51761             this.region.updatePanelTitle(this, title);
51762         }
51763     },
51764     
51765     /**
51766      * Returns true is this panel was configured to be closable
51767      * @return {Boolean} 
51768      */
51769     isClosable : function(){
51770         return this.closable;
51771     },
51772     
51773     beforeSlide : function(){
51774         this.el.clip();
51775         this.resizeEl.clip();
51776     },
51777     
51778     afterSlide : function(){
51779         this.el.unclip();
51780         this.resizeEl.unclip();
51781     },
51782     
51783     /**
51784      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51785      *   Will fail silently if the {@link #setUrl} method has not been called.
51786      *   This does not activate the panel, just updates its content.
51787      */
51788     refresh : function(){
51789         if(this.refreshDelegate){
51790            this.loaded = false;
51791            this.refreshDelegate();
51792         }
51793     },
51794     
51795     /**
51796      * Destroys this panel
51797      */
51798     destroy : function(){
51799         this.el.removeAllListeners();
51800         var tempEl = document.createElement("span");
51801         tempEl.appendChild(this.el.dom);
51802         tempEl.innerHTML = "";
51803         this.el.remove();
51804         this.el = null;
51805     },
51806     
51807     /**
51808      * form - if the content panel contains a form - this is a reference to it.
51809      * @type {Roo.form.Form}
51810      */
51811     form : false,
51812     /**
51813      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51814      *    This contains a reference to it.
51815      * @type {Roo.View}
51816      */
51817     view : false,
51818     
51819       /**
51820      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51821      * <pre><code>
51822
51823 layout.addxtype({
51824        xtype : 'Form',
51825        items: [ .... ]
51826    }
51827 );
51828
51829 </code></pre>
51830      * @param {Object} cfg Xtype definition of item to add.
51831      */
51832     
51833     addxtype : function(cfg) {
51834         // add form..
51835         if (cfg.xtype.match(/^Form$/)) {
51836             
51837             var el;
51838             //if (this.footer) {
51839             //    el = this.footer.container.insertSibling(false, 'before');
51840             //} else {
51841                 el = this.el.createChild();
51842             //}
51843
51844             this.form = new  Roo.form.Form(cfg);
51845             
51846             
51847             if ( this.form.allItems.length) this.form.render(el.dom);
51848             return this.form;
51849         }
51850         // should only have one of theses..
51851         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51852             // views.. should not be just added - used named prop 'view''
51853             
51854             cfg.el = this.el.appendChild(document.createElement("div"));
51855             // factory?
51856             
51857             var ret = new Roo.factory(cfg);
51858              
51859              ret.render && ret.render(false, ''); // render blank..
51860             this.view = ret;
51861             return ret;
51862         }
51863         return false;
51864     }
51865 });
51866
51867 /**
51868  * @class Roo.GridPanel
51869  * @extends Roo.ContentPanel
51870  * @constructor
51871  * Create a new GridPanel.
51872  * @param {Roo.grid.Grid} grid The grid for this panel
51873  * @param {String/Object} config A string to set only the panel's title, or a config object
51874  */
51875 Roo.GridPanel = function(grid, config){
51876     
51877   
51878     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51879         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51880         
51881     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51882     
51883     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51884     
51885     if(this.toolbar){
51886         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51887     }
51888     // xtype created footer. - not sure if will work as we normally have to render first..
51889     if (this.footer && !this.footer.el && this.footer.xtype) {
51890         
51891         this.footer.container = this.grid.getView().getFooterPanel(true);
51892         this.footer.dataSource = this.grid.dataSource;
51893         this.footer = Roo.factory(this.footer, Roo);
51894         
51895     }
51896     
51897     grid.monitorWindowResize = false; // turn off autosizing
51898     grid.autoHeight = false;
51899     grid.autoWidth = false;
51900     this.grid = grid;
51901     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
51902 };
51903
51904 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
51905     getId : function(){
51906         return this.grid.id;
51907     },
51908     
51909     /**
51910      * Returns the grid for this panel
51911      * @return {Roo.grid.Grid} 
51912      */
51913     getGrid : function(){
51914         return this.grid;    
51915     },
51916     
51917     setSize : function(width, height){
51918         if(!this.ignoreResize(width, height)){
51919             var grid = this.grid;
51920             var size = this.adjustForComponents(width, height);
51921             grid.getGridEl().setSize(size.width, size.height);
51922             grid.autoSize();
51923         }
51924     },
51925     
51926     beforeSlide : function(){
51927         this.grid.getView().scroller.clip();
51928     },
51929     
51930     afterSlide : function(){
51931         this.grid.getView().scroller.unclip();
51932     },
51933     
51934     destroy : function(){
51935         this.grid.destroy();
51936         delete this.grid;
51937         Roo.GridPanel.superclass.destroy.call(this); 
51938     }
51939 });
51940
51941
51942 /**
51943  * @class Roo.NestedLayoutPanel
51944  * @extends Roo.ContentPanel
51945  * @constructor
51946  * Create a new NestedLayoutPanel.
51947  * 
51948  * 
51949  * @param {Roo.BorderLayout} layout The layout for this panel
51950  * @param {String/Object} config A string to set only the title or a config object
51951  */
51952 Roo.NestedLayoutPanel = function(layout, config)
51953 {
51954     // construct with only one argument..
51955     /* FIXME - implement nicer consturctors
51956     if (layout.layout) {
51957         config = layout;
51958         layout = config.layout;
51959         delete config.layout;
51960     }
51961     if (layout.xtype && !layout.getEl) {
51962         // then layout needs constructing..
51963         layout = Roo.factory(layout, Roo);
51964     }
51965     */
51966     
51967     
51968     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51969     
51970     layout.monitorWindowResize = false; // turn off autosizing
51971     this.layout = layout;
51972     this.layout.getEl().addClass("x-layout-nested-layout");
51973     
51974     
51975     
51976     
51977 };
51978
51979 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
51980
51981     setSize : function(width, height){
51982         if(!this.ignoreResize(width, height)){
51983             var size = this.adjustForComponents(width, height);
51984             var el = this.layout.getEl();
51985             el.setSize(size.width, size.height);
51986             var touch = el.dom.offsetWidth;
51987             this.layout.layout();
51988             // ie requires a double layout on the first pass
51989             if(Roo.isIE && !this.initialized){
51990                 this.initialized = true;
51991                 this.layout.layout();
51992             }
51993         }
51994     },
51995     
51996     // activate all subpanels if not currently active..
51997     
51998     setActiveState : function(active){
51999         this.active = active;
52000         if(!active){
52001             this.fireEvent("deactivate", this);
52002             return;
52003         }
52004         
52005         this.fireEvent("activate", this);
52006         // not sure if this should happen before or after..
52007         if (!this.layout) {
52008             return; // should not happen..
52009         }
52010         var reg = false;
52011         for (var r in this.layout.regions) {
52012             reg = this.layout.getRegion(r);
52013             if (reg.getActivePanel()) {
52014                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
52015                 reg.setActivePanel(reg.getActivePanel());
52016                 continue;
52017             }
52018             if (!reg.panels.length) {
52019                 continue;
52020             }
52021             reg.showPanel(reg.getPanel(0));
52022         }
52023         
52024         
52025         
52026         
52027     },
52028     
52029     /**
52030      * Returns the nested BorderLayout for this panel
52031      * @return {Roo.BorderLayout} 
52032      */
52033     getLayout : function(){
52034         return this.layout;
52035     },
52036     
52037      /**
52038      * Adds a xtype elements to the layout of the nested panel
52039      * <pre><code>
52040
52041 panel.addxtype({
52042        xtype : 'ContentPanel',
52043        region: 'west',
52044        items: [ .... ]
52045    }
52046 );
52047
52048 panel.addxtype({
52049         xtype : 'NestedLayoutPanel',
52050         region: 'west',
52051         layout: {
52052            center: { },
52053            west: { }   
52054         },
52055         items : [ ... list of content panels or nested layout panels.. ]
52056    }
52057 );
52058 </code></pre>
52059      * @param {Object} cfg Xtype definition of item to add.
52060      */
52061     addxtype : function(cfg) {
52062         return this.layout.addxtype(cfg);
52063     
52064     }
52065 });
52066
52067 Roo.ScrollPanel = function(el, config, content){
52068     config = config || {};
52069     config.fitToFrame = true;
52070     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
52071     
52072     this.el.dom.style.overflow = "hidden";
52073     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
52074     this.el.removeClass("x-layout-inactive-content");
52075     this.el.on("mousewheel", this.onWheel, this);
52076
52077     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
52078     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
52079     up.unselectable(); down.unselectable();
52080     up.on("click", this.scrollUp, this);
52081     down.on("click", this.scrollDown, this);
52082     up.addClassOnOver("x-scroller-btn-over");
52083     down.addClassOnOver("x-scroller-btn-over");
52084     up.addClassOnClick("x-scroller-btn-click");
52085     down.addClassOnClick("x-scroller-btn-click");
52086     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
52087
52088     this.resizeEl = this.el;
52089     this.el = wrap; this.up = up; this.down = down;
52090 };
52091
52092 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
52093     increment : 100,
52094     wheelIncrement : 5,
52095     scrollUp : function(){
52096         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
52097     },
52098
52099     scrollDown : function(){
52100         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
52101     },
52102
52103     afterScroll : function(){
52104         var el = this.resizeEl;
52105         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
52106         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52107         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52108     },
52109
52110     setSize : function(){
52111         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
52112         this.afterScroll();
52113     },
52114
52115     onWheel : function(e){
52116         var d = e.getWheelDelta();
52117         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
52118         this.afterScroll();
52119         e.stopEvent();
52120     },
52121
52122     setContent : function(content, loadScripts){
52123         this.resizeEl.update(content, loadScripts);
52124     }
52125
52126 });
52127
52128
52129
52130
52131
52132
52133
52134
52135
52136 /**
52137  * @class Roo.TreePanel
52138  * @extends Roo.ContentPanel
52139  * @constructor
52140  * Create a new TreePanel. - defaults to fit/scoll contents.
52141  * @param {String/Object} config A string to set only the panel's title, or a config object
52142  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
52143  */
52144 Roo.TreePanel = function(config){
52145     var el = config.el;
52146     var tree = config.tree;
52147     delete config.tree; 
52148     delete config.el; // hopefull!
52149     
52150     // wrapper for IE7 strict & safari scroll issue
52151     
52152     var treeEl = el.createChild();
52153     config.resizeEl = treeEl;
52154     
52155     
52156     
52157     Roo.TreePanel.superclass.constructor.call(this, el, config);
52158  
52159  
52160     this.tree = new Roo.tree.TreePanel(treeEl , tree);
52161     //console.log(tree);
52162     this.on('activate', function()
52163     {
52164         if (this.tree.rendered) {
52165             return;
52166         }
52167         //console.log('render tree');
52168         this.tree.render();
52169     });
52170     // this should not be needed.. - it's actually the 'el' that resizes?
52171     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
52172     
52173     //this.on('resize',  function (cp, w, h) {
52174     //        this.tree.innerCt.setWidth(w);
52175     //        this.tree.innerCt.setHeight(h);
52176     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
52177     //});
52178
52179         
52180     
52181 };
52182
52183 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
52184     fitToFrame : true,
52185     autoScroll : true
52186 });
52187
52188
52189
52190
52191
52192
52193
52194
52195
52196
52197
52198 /*
52199  * Based on:
52200  * Ext JS Library 1.1.1
52201  * Copyright(c) 2006-2007, Ext JS, LLC.
52202  *
52203  * Originally Released Under LGPL - original licence link has changed is not relivant.
52204  *
52205  * Fork - LGPL
52206  * <script type="text/javascript">
52207  */
52208  
52209
52210 /**
52211  * @class Roo.ReaderLayout
52212  * @extends Roo.BorderLayout
52213  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
52214  * center region containing two nested regions (a top one for a list view and one for item preview below),
52215  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
52216  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
52217  * expedites the setup of the overall layout and regions for this common application style.
52218  * Example:
52219  <pre><code>
52220 var reader = new Roo.ReaderLayout();
52221 var CP = Roo.ContentPanel;  // shortcut for adding
52222
52223 reader.beginUpdate();
52224 reader.add("north", new CP("north", "North"));
52225 reader.add("west", new CP("west", {title: "West"}));
52226 reader.add("east", new CP("east", {title: "East"}));
52227
52228 reader.regions.listView.add(new CP("listView", "List"));
52229 reader.regions.preview.add(new CP("preview", "Preview"));
52230 reader.endUpdate();
52231 </code></pre>
52232 * @constructor
52233 * Create a new ReaderLayout
52234 * @param {Object} config Configuration options
52235 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
52236 * document.body if omitted)
52237 */
52238 Roo.ReaderLayout = function(config, renderTo){
52239     var c = config || {size:{}};
52240     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
52241         north: c.north !== false ? Roo.apply({
52242             split:false,
52243             initialSize: 32,
52244             titlebar: false
52245         }, c.north) : false,
52246         west: c.west !== false ? Roo.apply({
52247             split:true,
52248             initialSize: 200,
52249             minSize: 175,
52250             maxSize: 400,
52251             titlebar: true,
52252             collapsible: true,
52253             animate: true,
52254             margins:{left:5,right:0,bottom:5,top:5},
52255             cmargins:{left:5,right:5,bottom:5,top:5}
52256         }, c.west) : false,
52257         east: c.east !== false ? Roo.apply({
52258             split:true,
52259             initialSize: 200,
52260             minSize: 175,
52261             maxSize: 400,
52262             titlebar: true,
52263             collapsible: true,
52264             animate: true,
52265             margins:{left:0,right:5,bottom:5,top:5},
52266             cmargins:{left:5,right:5,bottom:5,top:5}
52267         }, c.east) : false,
52268         center: Roo.apply({
52269             tabPosition: 'top',
52270             autoScroll:false,
52271             closeOnTab: true,
52272             titlebar:false,
52273             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
52274         }, c.center)
52275     });
52276
52277     this.el.addClass('x-reader');
52278
52279     this.beginUpdate();
52280
52281     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
52282         south: c.preview !== false ? Roo.apply({
52283             split:true,
52284             initialSize: 200,
52285             minSize: 100,
52286             autoScroll:true,
52287             collapsible:true,
52288             titlebar: true,
52289             cmargins:{top:5,left:0, right:0, bottom:0}
52290         }, c.preview) : false,
52291         center: Roo.apply({
52292             autoScroll:false,
52293             titlebar:false,
52294             minHeight:200
52295         }, c.listView)
52296     });
52297     this.add('center', new Roo.NestedLayoutPanel(inner,
52298             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
52299
52300     this.endUpdate();
52301
52302     this.regions.preview = inner.getRegion('south');
52303     this.regions.listView = inner.getRegion('center');
52304 };
52305
52306 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
52307  * Based on:
52308  * Ext JS Library 1.1.1
52309  * Copyright(c) 2006-2007, Ext JS, LLC.
52310  *
52311  * Originally Released Under LGPL - original licence link has changed is not relivant.
52312  *
52313  * Fork - LGPL
52314  * <script type="text/javascript">
52315  */
52316  
52317 /**
52318  * @class Roo.grid.Grid
52319  * @extends Roo.util.Observable
52320  * This class represents the primary interface of a component based grid control.
52321  * <br><br>Usage:<pre><code>
52322  var grid = new Roo.grid.Grid("my-container-id", {
52323      ds: myDataStore,
52324      cm: myColModel,
52325      selModel: mySelectionModel,
52326      autoSizeColumns: true,
52327      monitorWindowResize: false,
52328      trackMouseOver: true
52329  });
52330  // set any options
52331  grid.render();
52332  * </code></pre>
52333  * <b>Common Problems:</b><br/>
52334  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
52335  * element will correct this<br/>
52336  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
52337  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
52338  * are unpredictable.<br/>
52339  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
52340  * grid to calculate dimensions/offsets.<br/>
52341   * @constructor
52342  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
52343  * The container MUST have some type of size defined for the grid to fill. The container will be
52344  * automatically set to position relative if it isn't already.
52345  * @param {Object} config A config object that sets properties on this grid.
52346  */
52347 Roo.grid.Grid = function(container, config){
52348         // initialize the container
52349         this.container = Roo.get(container);
52350         this.container.update("");
52351         this.container.setStyle("overflow", "hidden");
52352     this.container.addClass('x-grid-container');
52353
52354     this.id = this.container.id;
52355
52356     Roo.apply(this, config);
52357     // check and correct shorthanded configs
52358     if(this.ds){
52359         this.dataSource = this.ds;
52360         delete this.ds;
52361     }
52362     if(this.cm){
52363         this.colModel = this.cm;
52364         delete this.cm;
52365     }
52366     if(this.sm){
52367         this.selModel = this.sm;
52368         delete this.sm;
52369     }
52370
52371     if (this.selModel) {
52372         this.selModel = Roo.factory(this.selModel, Roo.grid);
52373         this.sm = this.selModel;
52374         this.sm.xmodule = this.xmodule || false;
52375     }
52376     if (typeof(this.colModel.config) == 'undefined') {
52377         this.colModel = new Roo.grid.ColumnModel(this.colModel);
52378         this.cm = this.colModel;
52379         this.cm.xmodule = this.xmodule || false;
52380     }
52381     if (this.dataSource) {
52382         this.dataSource= Roo.factory(this.dataSource, Roo.data);
52383         this.ds = this.dataSource;
52384         this.ds.xmodule = this.xmodule || false;
52385          
52386     }
52387     
52388     
52389     
52390     if(this.width){
52391         this.container.setWidth(this.width);
52392     }
52393
52394     if(this.height){
52395         this.container.setHeight(this.height);
52396     }
52397     /** @private */
52398         this.addEvents({
52399         // raw events
52400         /**
52401          * @event click
52402          * The raw click event for the entire grid.
52403          * @param {Roo.EventObject} e
52404          */
52405         "click" : true,
52406         /**
52407          * @event dblclick
52408          * The raw dblclick event for the entire grid.
52409          * @param {Roo.EventObject} e
52410          */
52411         "dblclick" : true,
52412         /**
52413          * @event contextmenu
52414          * The raw contextmenu event for the entire grid.
52415          * @param {Roo.EventObject} e
52416          */
52417         "contextmenu" : true,
52418         /**
52419          * @event mousedown
52420          * The raw mousedown event for the entire grid.
52421          * @param {Roo.EventObject} e
52422          */
52423         "mousedown" : true,
52424         /**
52425          * @event mouseup
52426          * The raw mouseup event for the entire grid.
52427          * @param {Roo.EventObject} e
52428          */
52429         "mouseup" : true,
52430         /**
52431          * @event mouseover
52432          * The raw mouseover event for the entire grid.
52433          * @param {Roo.EventObject} e
52434          */
52435         "mouseover" : true,
52436         /**
52437          * @event mouseout
52438          * The raw mouseout event for the entire grid.
52439          * @param {Roo.EventObject} e
52440          */
52441         "mouseout" : true,
52442         /**
52443          * @event keypress
52444          * The raw keypress event for the entire grid.
52445          * @param {Roo.EventObject} e
52446          */
52447         "keypress" : true,
52448         /**
52449          * @event keydown
52450          * The raw keydown event for the entire grid.
52451          * @param {Roo.EventObject} e
52452          */
52453         "keydown" : true,
52454
52455         // custom events
52456
52457         /**
52458          * @event cellclick
52459          * Fires when a cell is clicked
52460          * @param {Grid} this
52461          * @param {Number} rowIndex
52462          * @param {Number} columnIndex
52463          * @param {Roo.EventObject} e
52464          */
52465         "cellclick" : true,
52466         /**
52467          * @event celldblclick
52468          * Fires when a cell is double clicked
52469          * @param {Grid} this
52470          * @param {Number} rowIndex
52471          * @param {Number} columnIndex
52472          * @param {Roo.EventObject} e
52473          */
52474         "celldblclick" : true,
52475         /**
52476          * @event rowclick
52477          * Fires when a row is clicked
52478          * @param {Grid} this
52479          * @param {Number} rowIndex
52480          * @param {Roo.EventObject} e
52481          */
52482         "rowclick" : true,
52483         /**
52484          * @event rowdblclick
52485          * Fires when a row is double clicked
52486          * @param {Grid} this
52487          * @param {Number} rowIndex
52488          * @param {Roo.EventObject} e
52489          */
52490         "rowdblclick" : true,
52491         /**
52492          * @event headerclick
52493          * Fires when a header is clicked
52494          * @param {Grid} this
52495          * @param {Number} columnIndex
52496          * @param {Roo.EventObject} e
52497          */
52498         "headerclick" : true,
52499         /**
52500          * @event headerdblclick
52501          * Fires when a header cell is double clicked
52502          * @param {Grid} this
52503          * @param {Number} columnIndex
52504          * @param {Roo.EventObject} e
52505          */
52506         "headerdblclick" : true,
52507         /**
52508          * @event rowcontextmenu
52509          * Fires when a row is right clicked
52510          * @param {Grid} this
52511          * @param {Number} rowIndex
52512          * @param {Roo.EventObject} e
52513          */
52514         "rowcontextmenu" : true,
52515         /**
52516          * @event cellcontextmenu
52517          * Fires when a cell is right clicked
52518          * @param {Grid} this
52519          * @param {Number} rowIndex
52520          * @param {Number} cellIndex
52521          * @param {Roo.EventObject} e
52522          */
52523          "cellcontextmenu" : true,
52524         /**
52525          * @event headercontextmenu
52526          * Fires when a header is right clicked
52527          * @param {Grid} this
52528          * @param {Number} columnIndex
52529          * @param {Roo.EventObject} e
52530          */
52531         "headercontextmenu" : true,
52532         /**
52533          * @event bodyscroll
52534          * Fires when the body element is scrolled
52535          * @param {Number} scrollLeft
52536          * @param {Number} scrollTop
52537          */
52538         "bodyscroll" : true,
52539         /**
52540          * @event columnresize
52541          * Fires when the user resizes a column
52542          * @param {Number} columnIndex
52543          * @param {Number} newSize
52544          */
52545         "columnresize" : true,
52546         /**
52547          * @event columnmove
52548          * Fires when the user moves a column
52549          * @param {Number} oldIndex
52550          * @param {Number} newIndex
52551          */
52552         "columnmove" : true,
52553         /**
52554          * @event startdrag
52555          * Fires when row(s) start being dragged
52556          * @param {Grid} this
52557          * @param {Roo.GridDD} dd The drag drop object
52558          * @param {event} e The raw browser event
52559          */
52560         "startdrag" : true,
52561         /**
52562          * @event enddrag
52563          * Fires when a drag operation is complete
52564          * @param {Grid} this
52565          * @param {Roo.GridDD} dd The drag drop object
52566          * @param {event} e The raw browser event
52567          */
52568         "enddrag" : true,
52569         /**
52570          * @event dragdrop
52571          * Fires when dragged row(s) are dropped on a valid DD target
52572          * @param {Grid} this
52573          * @param {Roo.GridDD} dd The drag drop object
52574          * @param {String} targetId The target drag drop object
52575          * @param {event} e The raw browser event
52576          */
52577         "dragdrop" : true,
52578         /**
52579          * @event dragover
52580          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
52581          * @param {Grid} this
52582          * @param {Roo.GridDD} dd The drag drop object
52583          * @param {String} targetId The target drag drop object
52584          * @param {event} e The raw browser event
52585          */
52586         "dragover" : true,
52587         /**
52588          * @event dragenter
52589          *  Fires when the dragged row(s) first cross another DD target while being dragged
52590          * @param {Grid} this
52591          * @param {Roo.GridDD} dd The drag drop object
52592          * @param {String} targetId The target drag drop object
52593          * @param {event} e The raw browser event
52594          */
52595         "dragenter" : true,
52596         /**
52597          * @event dragout
52598          * Fires when the dragged row(s) leave another DD target while being dragged
52599          * @param {Grid} this
52600          * @param {Roo.GridDD} dd The drag drop object
52601          * @param {String} targetId The target drag drop object
52602          * @param {event} e The raw browser event
52603          */
52604         "dragout" : true,
52605         /**
52606          * @event rowclass
52607          * Fires when a row is rendered, so you can change add a style to it.
52608          * @param {GridView} gridview   The grid view
52609          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52610          */
52611         'rowclass' : true,
52612
52613         /**
52614          * @event render
52615          * Fires when the grid is rendered
52616          * @param {Grid} grid
52617          */
52618         'render' : true
52619     });
52620
52621     Roo.grid.Grid.superclass.constructor.call(this);
52622 };
52623 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52624     
52625     /**
52626      * @cfg {String} ddGroup - drag drop group.
52627      */
52628
52629     /**
52630      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52631      */
52632     minColumnWidth : 25,
52633
52634     /**
52635      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52636      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52637      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52638      */
52639     autoSizeColumns : false,
52640
52641     /**
52642      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52643      */
52644     autoSizeHeaders : true,
52645
52646     /**
52647      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52648      */
52649     monitorWindowResize : true,
52650
52651     /**
52652      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52653      * rows measured to get a columns size. Default is 0 (all rows).
52654      */
52655     maxRowsToMeasure : 0,
52656
52657     /**
52658      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52659      */
52660     trackMouseOver : true,
52661
52662     /**
52663     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52664     */
52665     
52666     /**
52667     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52668     */
52669     enableDragDrop : false,
52670     
52671     /**
52672     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52673     */
52674     enableColumnMove : true,
52675     
52676     /**
52677     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52678     */
52679     enableColumnHide : true,
52680     
52681     /**
52682     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52683     */
52684     enableRowHeightSync : false,
52685     
52686     /**
52687     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52688     */
52689     stripeRows : true,
52690     
52691     /**
52692     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52693     */
52694     autoHeight : false,
52695
52696     /**
52697      * @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.
52698      */
52699     autoExpandColumn : false,
52700
52701     /**
52702     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52703     * Default is 50.
52704     */
52705     autoExpandMin : 50,
52706
52707     /**
52708     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52709     */
52710     autoExpandMax : 1000,
52711
52712     /**
52713     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52714     */
52715     view : null,
52716
52717     /**
52718     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52719     */
52720     loadMask : false,
52721     /**
52722     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52723     */
52724     dropTarget: false,
52725     
52726    
52727     
52728     // private
52729     rendered : false,
52730
52731     /**
52732     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52733     * of a fixed width. Default is false.
52734     */
52735     /**
52736     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52737     */
52738     /**
52739      * Called once after all setup has been completed and the grid is ready to be rendered.
52740      * @return {Roo.grid.Grid} this
52741      */
52742     render : function()
52743     {
52744         var c = this.container;
52745         // try to detect autoHeight/width mode
52746         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52747             this.autoHeight = true;
52748         }
52749         var view = this.getView();
52750         view.init(this);
52751
52752         c.on("click", this.onClick, this);
52753         c.on("dblclick", this.onDblClick, this);
52754         c.on("contextmenu", this.onContextMenu, this);
52755         c.on("keydown", this.onKeyDown, this);
52756         if (Roo.isTouch) {
52757             c.on("touchstart", this.onTouchStart, this);
52758         }
52759
52760         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52761
52762         this.getSelectionModel().init(this);
52763
52764         view.render();
52765
52766         if(this.loadMask){
52767             this.loadMask = new Roo.LoadMask(this.container,
52768                     Roo.apply({store:this.dataSource}, this.loadMask));
52769         }
52770         
52771         
52772         if (this.toolbar && this.toolbar.xtype) {
52773             this.toolbar.container = this.getView().getHeaderPanel(true);
52774             this.toolbar = new Roo.Toolbar(this.toolbar);
52775         }
52776         if (this.footer && this.footer.xtype) {
52777             this.footer.dataSource = this.getDataSource();
52778             this.footer.container = this.getView().getFooterPanel(true);
52779             this.footer = Roo.factory(this.footer, Roo);
52780         }
52781         if (this.dropTarget && this.dropTarget.xtype) {
52782             delete this.dropTarget.xtype;
52783             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52784         }
52785         
52786         
52787         this.rendered = true;
52788         this.fireEvent('render', this);
52789         return this;
52790     },
52791
52792         /**
52793          * Reconfigures the grid to use a different Store and Column Model.
52794          * The View will be bound to the new objects and refreshed.
52795          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52796          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52797          */
52798     reconfigure : function(dataSource, colModel){
52799         if(this.loadMask){
52800             this.loadMask.destroy();
52801             this.loadMask = new Roo.LoadMask(this.container,
52802                     Roo.apply({store:dataSource}, this.loadMask));
52803         }
52804         this.view.bind(dataSource, colModel);
52805         this.dataSource = dataSource;
52806         this.colModel = colModel;
52807         this.view.refresh(true);
52808     },
52809
52810     // private
52811     onKeyDown : function(e){
52812         this.fireEvent("keydown", e);
52813     },
52814
52815     /**
52816      * Destroy this grid.
52817      * @param {Boolean} removeEl True to remove the element
52818      */
52819     destroy : function(removeEl, keepListeners){
52820         if(this.loadMask){
52821             this.loadMask.destroy();
52822         }
52823         var c = this.container;
52824         c.removeAllListeners();
52825         this.view.destroy();
52826         this.colModel.purgeListeners();
52827         if(!keepListeners){
52828             this.purgeListeners();
52829         }
52830         c.update("");
52831         if(removeEl === true){
52832             c.remove();
52833         }
52834     },
52835
52836     // private
52837     processEvent : function(name, e){
52838         // does this fire select???
52839         //Roo.log('grid:processEvent '  + name);
52840         
52841         if (name != 'touchstart' ) {
52842             this.fireEvent(name, e);    
52843         }
52844         
52845         var t = e.getTarget();
52846         var v = this.view;
52847         var header = v.findHeaderIndex(t);
52848         if(header !== false){
52849             var ename = name == 'touchstart' ? 'click' : name;
52850              
52851             this.fireEvent("header" + ename, this, header, e);
52852         }else{
52853             var row = v.findRowIndex(t);
52854             var cell = v.findCellIndex(t);
52855             if (name == 'touchstart') {
52856                 // first touch is always a click.
52857                 // hopefull this happens after selection is updated.?
52858                 name = false;
52859                 
52860                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52861                     var cs = this.selModel.getSelectedCell();
52862                     if (row == cs[0] && cell == cs[1]){
52863                         name = 'dblclick';
52864                     }
52865                 }
52866                 if (typeof(this.selModel.getSelections) != 'undefined') {
52867                     var cs = this.selModel.getSelections();
52868                     var ds = this.dataSource;
52869                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52870                         name = 'dblclick';
52871                     }
52872                 }
52873                 if (!name) {
52874                     return;
52875                 }
52876             }
52877             
52878             
52879             if(row !== false){
52880                 this.fireEvent("row" + name, this, row, e);
52881                 if(cell !== false){
52882                     this.fireEvent("cell" + name, this, row, cell, e);
52883                 }
52884             }
52885         }
52886     },
52887
52888     // private
52889     onClick : function(e){
52890         this.processEvent("click", e);
52891     },
52892    // private
52893     onTouchStart : function(e){
52894         this.processEvent("touchstart", e);
52895     },
52896
52897     // private
52898     onContextMenu : function(e, t){
52899         this.processEvent("contextmenu", e);
52900     },
52901
52902     // private
52903     onDblClick : function(e){
52904         this.processEvent("dblclick", e);
52905     },
52906
52907     // private
52908     walkCells : function(row, col, step, fn, scope){
52909         var cm = this.colModel, clen = cm.getColumnCount();
52910         var ds = this.dataSource, rlen = ds.getCount(), first = true;
52911         if(step < 0){
52912             if(col < 0){
52913                 row--;
52914                 first = false;
52915             }
52916             while(row >= 0){
52917                 if(!first){
52918                     col = clen-1;
52919                 }
52920                 first = false;
52921                 while(col >= 0){
52922                     if(fn.call(scope || this, row, col, cm) === true){
52923                         return [row, col];
52924                     }
52925                     col--;
52926                 }
52927                 row--;
52928             }
52929         } else {
52930             if(col >= clen){
52931                 row++;
52932                 first = false;
52933             }
52934             while(row < rlen){
52935                 if(!first){
52936                     col = 0;
52937                 }
52938                 first = false;
52939                 while(col < clen){
52940                     if(fn.call(scope || this, row, col, cm) === true){
52941                         return [row, col];
52942                     }
52943                     col++;
52944                 }
52945                 row++;
52946             }
52947         }
52948         return null;
52949     },
52950
52951     // private
52952     getSelections : function(){
52953         return this.selModel.getSelections();
52954     },
52955
52956     /**
52957      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52958      * but if manual update is required this method will initiate it.
52959      */
52960     autoSize : function(){
52961         if(this.rendered){
52962             this.view.layout();
52963             if(this.view.adjustForScroll){
52964                 this.view.adjustForScroll();
52965             }
52966         }
52967     },
52968
52969     /**
52970      * Returns the grid's underlying element.
52971      * @return {Element} The element
52972      */
52973     getGridEl : function(){
52974         return this.container;
52975     },
52976
52977     // private for compatibility, overridden by editor grid
52978     stopEditing : function(){},
52979
52980     /**
52981      * Returns the grid's SelectionModel.
52982      * @return {SelectionModel}
52983      */
52984     getSelectionModel : function(){
52985         if(!this.selModel){
52986             this.selModel = new Roo.grid.RowSelectionModel();
52987         }
52988         return this.selModel;
52989     },
52990
52991     /**
52992      * Returns the grid's DataSource.
52993      * @return {DataSource}
52994      */
52995     getDataSource : function(){
52996         return this.dataSource;
52997     },
52998
52999     /**
53000      * Returns the grid's ColumnModel.
53001      * @return {ColumnModel}
53002      */
53003     getColumnModel : function(){
53004         return this.colModel;
53005     },
53006
53007     /**
53008      * Returns the grid's GridView object.
53009      * @return {GridView}
53010      */
53011     getView : function(){
53012         if(!this.view){
53013             this.view = new Roo.grid.GridView(this.viewConfig);
53014         }
53015         return this.view;
53016     },
53017     /**
53018      * Called to get grid's drag proxy text, by default returns this.ddText.
53019      * @return {String}
53020      */
53021     getDragDropText : function(){
53022         var count = this.selModel.getCount();
53023         return String.format(this.ddText, count, count == 1 ? '' : 's');
53024     }
53025 });
53026 /**
53027  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
53028  * %0 is replaced with the number of selected rows.
53029  * @type String
53030  */
53031 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
53032  * Based on:
53033  * Ext JS Library 1.1.1
53034  * Copyright(c) 2006-2007, Ext JS, LLC.
53035  *
53036  * Originally Released Under LGPL - original licence link has changed is not relivant.
53037  *
53038  * Fork - LGPL
53039  * <script type="text/javascript">
53040  */
53041  
53042 Roo.grid.AbstractGridView = function(){
53043         this.grid = null;
53044         
53045         this.events = {
53046             "beforerowremoved" : true,
53047             "beforerowsinserted" : true,
53048             "beforerefresh" : true,
53049             "rowremoved" : true,
53050             "rowsinserted" : true,
53051             "rowupdated" : true,
53052             "refresh" : true
53053         };
53054     Roo.grid.AbstractGridView.superclass.constructor.call(this);
53055 };
53056
53057 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
53058     rowClass : "x-grid-row",
53059     cellClass : "x-grid-cell",
53060     tdClass : "x-grid-td",
53061     hdClass : "x-grid-hd",
53062     splitClass : "x-grid-hd-split",
53063     
53064     init: function(grid){
53065         this.grid = grid;
53066                 var cid = this.grid.getGridEl().id;
53067         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
53068         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
53069         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
53070         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
53071         },
53072         
53073     getColumnRenderers : function(){
53074         var renderers = [];
53075         var cm = this.grid.colModel;
53076         var colCount = cm.getColumnCount();
53077         for(var i = 0; i < colCount; i++){
53078             renderers[i] = cm.getRenderer(i);
53079         }
53080         return renderers;
53081     },
53082     
53083     getColumnIds : function(){
53084         var ids = [];
53085         var cm = this.grid.colModel;
53086         var colCount = cm.getColumnCount();
53087         for(var i = 0; i < colCount; i++){
53088             ids[i] = cm.getColumnId(i);
53089         }
53090         return ids;
53091     },
53092     
53093     getDataIndexes : function(){
53094         if(!this.indexMap){
53095             this.indexMap = this.buildIndexMap();
53096         }
53097         return this.indexMap.colToData;
53098     },
53099     
53100     getColumnIndexByDataIndex : function(dataIndex){
53101         if(!this.indexMap){
53102             this.indexMap = this.buildIndexMap();
53103         }
53104         return this.indexMap.dataToCol[dataIndex];
53105     },
53106     
53107     /**
53108      * Set a css style for a column dynamically. 
53109      * @param {Number} colIndex The index of the column
53110      * @param {String} name The css property name
53111      * @param {String} value The css value
53112      */
53113     setCSSStyle : function(colIndex, name, value){
53114         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
53115         Roo.util.CSS.updateRule(selector, name, value);
53116     },
53117     
53118     generateRules : function(cm){
53119         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
53120         Roo.util.CSS.removeStyleSheet(rulesId);
53121         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53122             var cid = cm.getColumnId(i);
53123             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
53124                          this.tdSelector, cid, " {\n}\n",
53125                          this.hdSelector, cid, " {\n}\n",
53126                          this.splitSelector, cid, " {\n}\n");
53127         }
53128         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53129     }
53130 });/*
53131  * Based on:
53132  * Ext JS Library 1.1.1
53133  * Copyright(c) 2006-2007, Ext JS, LLC.
53134  *
53135  * Originally Released Under LGPL - original licence link has changed is not relivant.
53136  *
53137  * Fork - LGPL
53138  * <script type="text/javascript">
53139  */
53140
53141 // private
53142 // This is a support class used internally by the Grid components
53143 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
53144     this.grid = grid;
53145     this.view = grid.getView();
53146     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53147     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
53148     if(hd2){
53149         this.setHandleElId(Roo.id(hd));
53150         this.setOuterHandleElId(Roo.id(hd2));
53151     }
53152     this.scroll = false;
53153 };
53154 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
53155     maxDragWidth: 120,
53156     getDragData : function(e){
53157         var t = Roo.lib.Event.getTarget(e);
53158         var h = this.view.findHeaderCell(t);
53159         if(h){
53160             return {ddel: h.firstChild, header:h};
53161         }
53162         return false;
53163     },
53164
53165     onInitDrag : function(e){
53166         this.view.headersDisabled = true;
53167         var clone = this.dragData.ddel.cloneNode(true);
53168         clone.id = Roo.id();
53169         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
53170         this.proxy.update(clone);
53171         return true;
53172     },
53173
53174     afterValidDrop : function(){
53175         var v = this.view;
53176         setTimeout(function(){
53177             v.headersDisabled = false;
53178         }, 50);
53179     },
53180
53181     afterInvalidDrop : function(){
53182         var v = this.view;
53183         setTimeout(function(){
53184             v.headersDisabled = false;
53185         }, 50);
53186     }
53187 });
53188 /*
53189  * Based on:
53190  * Ext JS Library 1.1.1
53191  * Copyright(c) 2006-2007, Ext JS, LLC.
53192  *
53193  * Originally Released Under LGPL - original licence link has changed is not relivant.
53194  *
53195  * Fork - LGPL
53196  * <script type="text/javascript">
53197  */
53198 // private
53199 // This is a support class used internally by the Grid components
53200 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
53201     this.grid = grid;
53202     this.view = grid.getView();
53203     // split the proxies so they don't interfere with mouse events
53204     this.proxyTop = Roo.DomHelper.append(document.body, {
53205         cls:"col-move-top", html:"&#160;"
53206     }, true);
53207     this.proxyBottom = Roo.DomHelper.append(document.body, {
53208         cls:"col-move-bottom", html:"&#160;"
53209     }, true);
53210     this.proxyTop.hide = this.proxyBottom.hide = function(){
53211         this.setLeftTop(-100,-100);
53212         this.setStyle("visibility", "hidden");
53213     };
53214     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53215     // temporarily disabled
53216     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
53217     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
53218 };
53219 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
53220     proxyOffsets : [-4, -9],
53221     fly: Roo.Element.fly,
53222
53223     getTargetFromEvent : function(e){
53224         var t = Roo.lib.Event.getTarget(e);
53225         var cindex = this.view.findCellIndex(t);
53226         if(cindex !== false){
53227             return this.view.getHeaderCell(cindex);
53228         }
53229         return null;
53230     },
53231
53232     nextVisible : function(h){
53233         var v = this.view, cm = this.grid.colModel;
53234         h = h.nextSibling;
53235         while(h){
53236             if(!cm.isHidden(v.getCellIndex(h))){
53237                 return h;
53238             }
53239             h = h.nextSibling;
53240         }
53241         return null;
53242     },
53243
53244     prevVisible : function(h){
53245         var v = this.view, cm = this.grid.colModel;
53246         h = h.prevSibling;
53247         while(h){
53248             if(!cm.isHidden(v.getCellIndex(h))){
53249                 return h;
53250             }
53251             h = h.prevSibling;
53252         }
53253         return null;
53254     },
53255
53256     positionIndicator : function(h, n, e){
53257         var x = Roo.lib.Event.getPageX(e);
53258         var r = Roo.lib.Dom.getRegion(n.firstChild);
53259         var px, pt, py = r.top + this.proxyOffsets[1];
53260         if((r.right - x) <= (r.right-r.left)/2){
53261             px = r.right+this.view.borderWidth;
53262             pt = "after";
53263         }else{
53264             px = r.left;
53265             pt = "before";
53266         }
53267         var oldIndex = this.view.getCellIndex(h);
53268         var newIndex = this.view.getCellIndex(n);
53269
53270         if(this.grid.colModel.isFixed(newIndex)){
53271             return false;
53272         }
53273
53274         var locked = this.grid.colModel.isLocked(newIndex);
53275
53276         if(pt == "after"){
53277             newIndex++;
53278         }
53279         if(oldIndex < newIndex){
53280             newIndex--;
53281         }
53282         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
53283             return false;
53284         }
53285         px +=  this.proxyOffsets[0];
53286         this.proxyTop.setLeftTop(px, py);
53287         this.proxyTop.show();
53288         if(!this.bottomOffset){
53289             this.bottomOffset = this.view.mainHd.getHeight();
53290         }
53291         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
53292         this.proxyBottom.show();
53293         return pt;
53294     },
53295
53296     onNodeEnter : function(n, dd, e, data){
53297         if(data.header != n){
53298             this.positionIndicator(data.header, n, e);
53299         }
53300     },
53301
53302     onNodeOver : function(n, dd, e, data){
53303         var result = false;
53304         if(data.header != n){
53305             result = this.positionIndicator(data.header, n, e);
53306         }
53307         if(!result){
53308             this.proxyTop.hide();
53309             this.proxyBottom.hide();
53310         }
53311         return result ? this.dropAllowed : this.dropNotAllowed;
53312     },
53313
53314     onNodeOut : function(n, dd, e, data){
53315         this.proxyTop.hide();
53316         this.proxyBottom.hide();
53317     },
53318
53319     onNodeDrop : function(n, dd, e, data){
53320         var h = data.header;
53321         if(h != n){
53322             var cm = this.grid.colModel;
53323             var x = Roo.lib.Event.getPageX(e);
53324             var r = Roo.lib.Dom.getRegion(n.firstChild);
53325             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
53326             var oldIndex = this.view.getCellIndex(h);
53327             var newIndex = this.view.getCellIndex(n);
53328             var locked = cm.isLocked(newIndex);
53329             if(pt == "after"){
53330                 newIndex++;
53331             }
53332             if(oldIndex < newIndex){
53333                 newIndex--;
53334             }
53335             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
53336                 return false;
53337             }
53338             cm.setLocked(oldIndex, locked, true);
53339             cm.moveColumn(oldIndex, newIndex);
53340             this.grid.fireEvent("columnmove", oldIndex, newIndex);
53341             return true;
53342         }
53343         return false;
53344     }
53345 });
53346 /*
53347  * Based on:
53348  * Ext JS Library 1.1.1
53349  * Copyright(c) 2006-2007, Ext JS, LLC.
53350  *
53351  * Originally Released Under LGPL - original licence link has changed is not relivant.
53352  *
53353  * Fork - LGPL
53354  * <script type="text/javascript">
53355  */
53356   
53357 /**
53358  * @class Roo.grid.GridView
53359  * @extends Roo.util.Observable
53360  *
53361  * @constructor
53362  * @param {Object} config
53363  */
53364 Roo.grid.GridView = function(config){
53365     Roo.grid.GridView.superclass.constructor.call(this);
53366     this.el = null;
53367
53368     Roo.apply(this, config);
53369 };
53370
53371 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
53372
53373     unselectable :  'unselectable="on"',
53374     unselectableCls :  'x-unselectable',
53375     
53376     
53377     rowClass : "x-grid-row",
53378
53379     cellClass : "x-grid-col",
53380
53381     tdClass : "x-grid-td",
53382
53383     hdClass : "x-grid-hd",
53384
53385     splitClass : "x-grid-split",
53386
53387     sortClasses : ["sort-asc", "sort-desc"],
53388
53389     enableMoveAnim : false,
53390
53391     hlColor: "C3DAF9",
53392
53393     dh : Roo.DomHelper,
53394
53395     fly : Roo.Element.fly,
53396
53397     css : Roo.util.CSS,
53398
53399     borderWidth: 1,
53400
53401     splitOffset: 3,
53402
53403     scrollIncrement : 22,
53404
53405     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
53406
53407     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
53408
53409     bind : function(ds, cm){
53410         if(this.ds){
53411             this.ds.un("load", this.onLoad, this);
53412             this.ds.un("datachanged", this.onDataChange, this);
53413             this.ds.un("add", this.onAdd, this);
53414             this.ds.un("remove", this.onRemove, this);
53415             this.ds.un("update", this.onUpdate, this);
53416             this.ds.un("clear", this.onClear, this);
53417         }
53418         if(ds){
53419             ds.on("load", this.onLoad, this);
53420             ds.on("datachanged", this.onDataChange, this);
53421             ds.on("add", this.onAdd, this);
53422             ds.on("remove", this.onRemove, this);
53423             ds.on("update", this.onUpdate, this);
53424             ds.on("clear", this.onClear, this);
53425         }
53426         this.ds = ds;
53427
53428         if(this.cm){
53429             this.cm.un("widthchange", this.onColWidthChange, this);
53430             this.cm.un("headerchange", this.onHeaderChange, this);
53431             this.cm.un("hiddenchange", this.onHiddenChange, this);
53432             this.cm.un("columnmoved", this.onColumnMove, this);
53433             this.cm.un("columnlockchange", this.onColumnLock, this);
53434         }
53435         if(cm){
53436             this.generateRules(cm);
53437             cm.on("widthchange", this.onColWidthChange, this);
53438             cm.on("headerchange", this.onHeaderChange, this);
53439             cm.on("hiddenchange", this.onHiddenChange, this);
53440             cm.on("columnmoved", this.onColumnMove, this);
53441             cm.on("columnlockchange", this.onColumnLock, this);
53442         }
53443         this.cm = cm;
53444     },
53445
53446     init: function(grid){
53447         Roo.grid.GridView.superclass.init.call(this, grid);
53448
53449         this.bind(grid.dataSource, grid.colModel);
53450
53451         grid.on("headerclick", this.handleHeaderClick, this);
53452
53453         if(grid.trackMouseOver){
53454             grid.on("mouseover", this.onRowOver, this);
53455             grid.on("mouseout", this.onRowOut, this);
53456         }
53457         grid.cancelTextSelection = function(){};
53458         this.gridId = grid.id;
53459
53460         var tpls = this.templates || {};
53461
53462         if(!tpls.master){
53463             tpls.master = new Roo.Template(
53464                '<div class="x-grid" hidefocus="true">',
53465                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
53466                   '<div class="x-grid-topbar"></div>',
53467                   '<div class="x-grid-scroller"><div></div></div>',
53468                   '<div class="x-grid-locked">',
53469                       '<div class="x-grid-header">{lockedHeader}</div>',
53470                       '<div class="x-grid-body">{lockedBody}</div>',
53471                   "</div>",
53472                   '<div class="x-grid-viewport">',
53473                       '<div class="x-grid-header">{header}</div>',
53474                       '<div class="x-grid-body">{body}</div>',
53475                   "</div>",
53476                   '<div class="x-grid-bottombar"></div>',
53477                  
53478                   '<div class="x-grid-resize-proxy">&#160;</div>',
53479                "</div>"
53480             );
53481             tpls.master.disableformats = true;
53482         }
53483
53484         if(!tpls.header){
53485             tpls.header = new Roo.Template(
53486                '<table border="0" cellspacing="0" cellpadding="0">',
53487                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
53488                "</table>{splits}"
53489             );
53490             tpls.header.disableformats = true;
53491         }
53492         tpls.header.compile();
53493
53494         if(!tpls.hcell){
53495             tpls.hcell = new Roo.Template(
53496                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
53497                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
53498                 "</div></td>"
53499              );
53500              tpls.hcell.disableFormats = true;
53501         }
53502         tpls.hcell.compile();
53503
53504         if(!tpls.hsplit){
53505             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
53506                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
53507             tpls.hsplit.disableFormats = true;
53508         }
53509         tpls.hsplit.compile();
53510
53511         if(!tpls.body){
53512             tpls.body = new Roo.Template(
53513                '<table border="0" cellspacing="0" cellpadding="0">',
53514                "<tbody>{rows}</tbody>",
53515                "</table>"
53516             );
53517             tpls.body.disableFormats = true;
53518         }
53519         tpls.body.compile();
53520
53521         if(!tpls.row){
53522             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
53523             tpls.row.disableFormats = true;
53524         }
53525         tpls.row.compile();
53526
53527         if(!tpls.cell){
53528             tpls.cell = new Roo.Template(
53529                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
53530                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
53531                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
53532                 "</td>"
53533             );
53534             tpls.cell.disableFormats = true;
53535         }
53536         tpls.cell.compile();
53537
53538         this.templates = tpls;
53539     },
53540
53541     // remap these for backwards compat
53542     onColWidthChange : function(){
53543         this.updateColumns.apply(this, arguments);
53544     },
53545     onHeaderChange : function(){
53546         this.updateHeaders.apply(this, arguments);
53547     }, 
53548     onHiddenChange : function(){
53549         this.handleHiddenChange.apply(this, arguments);
53550     },
53551     onColumnMove : function(){
53552         this.handleColumnMove.apply(this, arguments);
53553     },
53554     onColumnLock : function(){
53555         this.handleLockChange.apply(this, arguments);
53556     },
53557
53558     onDataChange : function(){
53559         this.refresh();
53560         this.updateHeaderSortState();
53561     },
53562
53563     onClear : function(){
53564         this.refresh();
53565     },
53566
53567     onUpdate : function(ds, record){
53568         this.refreshRow(record);
53569     },
53570
53571     refreshRow : function(record){
53572         var ds = this.ds, index;
53573         if(typeof record == 'number'){
53574             index = record;
53575             record = ds.getAt(index);
53576         }else{
53577             index = ds.indexOf(record);
53578         }
53579         this.insertRows(ds, index, index, true);
53580         this.onRemove(ds, record, index+1, true);
53581         this.syncRowHeights(index, index);
53582         this.layout();
53583         this.fireEvent("rowupdated", this, index, record);
53584     },
53585
53586     onAdd : function(ds, records, index){
53587         this.insertRows(ds, index, index + (records.length-1));
53588     },
53589
53590     onRemove : function(ds, record, index, isUpdate){
53591         if(isUpdate !== true){
53592             this.fireEvent("beforerowremoved", this, index, record);
53593         }
53594         var bt = this.getBodyTable(), lt = this.getLockedTable();
53595         if(bt.rows[index]){
53596             bt.firstChild.removeChild(bt.rows[index]);
53597         }
53598         if(lt.rows[index]){
53599             lt.firstChild.removeChild(lt.rows[index]);
53600         }
53601         if(isUpdate !== true){
53602             this.stripeRows(index);
53603             this.syncRowHeights(index, index);
53604             this.layout();
53605             this.fireEvent("rowremoved", this, index, record);
53606         }
53607     },
53608
53609     onLoad : function(){
53610         this.scrollToTop();
53611     },
53612
53613     /**
53614      * Scrolls the grid to the top
53615      */
53616     scrollToTop : function(){
53617         if(this.scroller){
53618             this.scroller.dom.scrollTop = 0;
53619             this.syncScroll();
53620         }
53621     },
53622
53623     /**
53624      * Gets a panel in the header of the grid that can be used for toolbars etc.
53625      * After modifying the contents of this panel a call to grid.autoSize() may be
53626      * required to register any changes in size.
53627      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53628      * @return Roo.Element
53629      */
53630     getHeaderPanel : function(doShow){
53631         if(doShow){
53632             this.headerPanel.show();
53633         }
53634         return this.headerPanel;
53635     },
53636
53637     /**
53638      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53639      * After modifying the contents of this panel a call to grid.autoSize() may be
53640      * required to register any changes in size.
53641      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53642      * @return Roo.Element
53643      */
53644     getFooterPanel : function(doShow){
53645         if(doShow){
53646             this.footerPanel.show();
53647         }
53648         return this.footerPanel;
53649     },
53650
53651     initElements : function(){
53652         var E = Roo.Element;
53653         var el = this.grid.getGridEl().dom.firstChild;
53654         var cs = el.childNodes;
53655
53656         this.el = new E(el);
53657         
53658          this.focusEl = new E(el.firstChild);
53659         this.focusEl.swallowEvent("click", true);
53660         
53661         this.headerPanel = new E(cs[1]);
53662         this.headerPanel.enableDisplayMode("block");
53663
53664         this.scroller = new E(cs[2]);
53665         this.scrollSizer = new E(this.scroller.dom.firstChild);
53666
53667         this.lockedWrap = new E(cs[3]);
53668         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53669         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53670
53671         this.mainWrap = new E(cs[4]);
53672         this.mainHd = new E(this.mainWrap.dom.firstChild);
53673         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53674
53675         this.footerPanel = new E(cs[5]);
53676         this.footerPanel.enableDisplayMode("block");
53677
53678         this.resizeProxy = new E(cs[6]);
53679
53680         this.headerSelector = String.format(
53681            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53682            this.lockedHd.id, this.mainHd.id
53683         );
53684
53685         this.splitterSelector = String.format(
53686            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53687            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53688         );
53689     },
53690     idToCssName : function(s)
53691     {
53692         return s.replace(/[^a-z0-9]+/ig, '-');
53693     },
53694
53695     getHeaderCell : function(index){
53696         return Roo.DomQuery.select(this.headerSelector)[index];
53697     },
53698
53699     getHeaderCellMeasure : function(index){
53700         return this.getHeaderCell(index).firstChild;
53701     },
53702
53703     getHeaderCellText : function(index){
53704         return this.getHeaderCell(index).firstChild.firstChild;
53705     },
53706
53707     getLockedTable : function(){
53708         return this.lockedBody.dom.firstChild;
53709     },
53710
53711     getBodyTable : function(){
53712         return this.mainBody.dom.firstChild;
53713     },
53714
53715     getLockedRow : function(index){
53716         return this.getLockedTable().rows[index];
53717     },
53718
53719     getRow : function(index){
53720         return this.getBodyTable().rows[index];
53721     },
53722
53723     getRowComposite : function(index){
53724         if(!this.rowEl){
53725             this.rowEl = new Roo.CompositeElementLite();
53726         }
53727         var els = [], lrow, mrow;
53728         if(lrow = this.getLockedRow(index)){
53729             els.push(lrow);
53730         }
53731         if(mrow = this.getRow(index)){
53732             els.push(mrow);
53733         }
53734         this.rowEl.elements = els;
53735         return this.rowEl;
53736     },
53737     /**
53738      * Gets the 'td' of the cell
53739      * 
53740      * @param {Integer} rowIndex row to select
53741      * @param {Integer} colIndex column to select
53742      * 
53743      * @return {Object} 
53744      */
53745     getCell : function(rowIndex, colIndex){
53746         var locked = this.cm.getLockedCount();
53747         var source;
53748         if(colIndex < locked){
53749             source = this.lockedBody.dom.firstChild;
53750         }else{
53751             source = this.mainBody.dom.firstChild;
53752             colIndex -= locked;
53753         }
53754         return source.rows[rowIndex].childNodes[colIndex];
53755     },
53756
53757     getCellText : function(rowIndex, colIndex){
53758         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53759     },
53760
53761     getCellBox : function(cell){
53762         var b = this.fly(cell).getBox();
53763         if(Roo.isOpera){ // opera fails to report the Y
53764             b.y = cell.offsetTop + this.mainBody.getY();
53765         }
53766         return b;
53767     },
53768
53769     getCellIndex : function(cell){
53770         var id = String(cell.className).match(this.cellRE);
53771         if(id){
53772             return parseInt(id[1], 10);
53773         }
53774         return 0;
53775     },
53776
53777     findHeaderIndex : function(n){
53778         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53779         return r ? this.getCellIndex(r) : false;
53780     },
53781
53782     findHeaderCell : function(n){
53783         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53784         return r ? r : false;
53785     },
53786
53787     findRowIndex : function(n){
53788         if(!n){
53789             return false;
53790         }
53791         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53792         return r ? r.rowIndex : false;
53793     },
53794
53795     findCellIndex : function(node){
53796         var stop = this.el.dom;
53797         while(node && node != stop){
53798             if(this.findRE.test(node.className)){
53799                 return this.getCellIndex(node);
53800             }
53801             node = node.parentNode;
53802         }
53803         return false;
53804     },
53805
53806     getColumnId : function(index){
53807         return this.cm.getColumnId(index);
53808     },
53809
53810     getSplitters : function()
53811     {
53812         if(this.splitterSelector){
53813            return Roo.DomQuery.select(this.splitterSelector);
53814         }else{
53815             return null;
53816       }
53817     },
53818
53819     getSplitter : function(index){
53820         return this.getSplitters()[index];
53821     },
53822
53823     onRowOver : function(e, t){
53824         var row;
53825         if((row = this.findRowIndex(t)) !== false){
53826             this.getRowComposite(row).addClass("x-grid-row-over");
53827         }
53828     },
53829
53830     onRowOut : function(e, t){
53831         var row;
53832         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53833             this.getRowComposite(row).removeClass("x-grid-row-over");
53834         }
53835     },
53836
53837     renderHeaders : function(){
53838         var cm = this.cm;
53839         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53840         var cb = [], lb = [], sb = [], lsb = [], p = {};
53841         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53842             p.cellId = "x-grid-hd-0-" + i;
53843             p.splitId = "x-grid-csplit-0-" + i;
53844             p.id = cm.getColumnId(i);
53845             p.title = cm.getColumnTooltip(i) || "";
53846             p.value = cm.getColumnHeader(i) || "";
53847             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53848             if(!cm.isLocked(i)){
53849                 cb[cb.length] = ct.apply(p);
53850                 sb[sb.length] = st.apply(p);
53851             }else{
53852                 lb[lb.length] = ct.apply(p);
53853                 lsb[lsb.length] = st.apply(p);
53854             }
53855         }
53856         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53857                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53858     },
53859
53860     updateHeaders : function(){
53861         var html = this.renderHeaders();
53862         this.lockedHd.update(html[0]);
53863         this.mainHd.update(html[1]);
53864     },
53865
53866     /**
53867      * Focuses the specified row.
53868      * @param {Number} row The row index
53869      */
53870     focusRow : function(row)
53871     {
53872         //Roo.log('GridView.focusRow');
53873         var x = this.scroller.dom.scrollLeft;
53874         this.focusCell(row, 0, false);
53875         this.scroller.dom.scrollLeft = x;
53876     },
53877
53878     /**
53879      * Focuses the specified cell.
53880      * @param {Number} row The row index
53881      * @param {Number} col The column index
53882      * @param {Boolean} hscroll false to disable horizontal scrolling
53883      */
53884     focusCell : function(row, col, hscroll)
53885     {
53886         //Roo.log('GridView.focusCell');
53887         var el = this.ensureVisible(row, col, hscroll);
53888         this.focusEl.alignTo(el, "tl-tl");
53889         if(Roo.isGecko){
53890             this.focusEl.focus();
53891         }else{
53892             this.focusEl.focus.defer(1, this.focusEl);
53893         }
53894     },
53895
53896     /**
53897      * Scrolls the specified cell into view
53898      * @param {Number} row The row index
53899      * @param {Number} col The column index
53900      * @param {Boolean} hscroll false to disable horizontal scrolling
53901      */
53902     ensureVisible : function(row, col, hscroll)
53903     {
53904         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
53905         //return null; //disable for testing.
53906         if(typeof row != "number"){
53907             row = row.rowIndex;
53908         }
53909         if(row < 0 && row >= this.ds.getCount()){
53910             return  null;
53911         }
53912         col = (col !== undefined ? col : 0);
53913         var cm = this.grid.colModel;
53914         while(cm.isHidden(col)){
53915             col++;
53916         }
53917
53918         var el = this.getCell(row, col);
53919         if(!el){
53920             return null;
53921         }
53922         var c = this.scroller.dom;
53923
53924         var ctop = parseInt(el.offsetTop, 10);
53925         var cleft = parseInt(el.offsetLeft, 10);
53926         var cbot = ctop + el.offsetHeight;
53927         var cright = cleft + el.offsetWidth;
53928         
53929         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
53930         var stop = parseInt(c.scrollTop, 10);
53931         var sleft = parseInt(c.scrollLeft, 10);
53932         var sbot = stop + ch;
53933         var sright = sleft + c.clientWidth;
53934         /*
53935         Roo.log('GridView.ensureVisible:' +
53936                 ' ctop:' + ctop +
53937                 ' c.clientHeight:' + c.clientHeight +
53938                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
53939                 ' stop:' + stop +
53940                 ' cbot:' + cbot +
53941                 ' sbot:' + sbot +
53942                 ' ch:' + ch  
53943                 );
53944         */
53945         if(ctop < stop){
53946              c.scrollTop = ctop;
53947             //Roo.log("set scrolltop to ctop DISABLE?");
53948         }else if(cbot > sbot){
53949             //Roo.log("set scrolltop to cbot-ch");
53950             c.scrollTop = cbot-ch;
53951         }
53952         
53953         if(hscroll !== false){
53954             if(cleft < sleft){
53955                 c.scrollLeft = cleft;
53956             }else if(cright > sright){
53957                 c.scrollLeft = cright-c.clientWidth;
53958             }
53959         }
53960          
53961         return el;
53962     },
53963
53964     updateColumns : function(){
53965         this.grid.stopEditing();
53966         var cm = this.grid.colModel, colIds = this.getColumnIds();
53967         //var totalWidth = cm.getTotalWidth();
53968         var pos = 0;
53969         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53970             //if(cm.isHidden(i)) continue;
53971             var w = cm.getColumnWidth(i);
53972             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53973             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53974         }
53975         this.updateSplitters();
53976     },
53977
53978     generateRules : function(cm){
53979         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
53980         Roo.util.CSS.removeStyleSheet(rulesId);
53981         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53982             var cid = cm.getColumnId(i);
53983             var align = '';
53984             if(cm.config[i].align){
53985                 align = 'text-align:'+cm.config[i].align+';';
53986             }
53987             var hidden = '';
53988             if(cm.isHidden(i)){
53989                 hidden = 'display:none;';
53990             }
53991             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
53992             ruleBuf.push(
53993                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
53994                     this.hdSelector, cid, " {\n", align, width, "}\n",
53995                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
53996                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
53997         }
53998         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53999     },
54000
54001     updateSplitters : function(){
54002         var cm = this.cm, s = this.getSplitters();
54003         if(s){ // splitters not created yet
54004             var pos = 0, locked = true;
54005             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54006                 if(cm.isHidden(i)) continue;
54007                 var w = cm.getColumnWidth(i); // make sure it's a number
54008                 if(!cm.isLocked(i) && locked){
54009                     pos = 0;
54010                     locked = false;
54011                 }
54012                 pos += w;
54013                 s[i].style.left = (pos-this.splitOffset) + "px";
54014             }
54015         }
54016     },
54017
54018     handleHiddenChange : function(colModel, colIndex, hidden){
54019         if(hidden){
54020             this.hideColumn(colIndex);
54021         }else{
54022             this.unhideColumn(colIndex);
54023         }
54024     },
54025
54026     hideColumn : function(colIndex){
54027         var cid = this.getColumnId(colIndex);
54028         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
54029         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
54030         if(Roo.isSafari){
54031             this.updateHeaders();
54032         }
54033         this.updateSplitters();
54034         this.layout();
54035     },
54036
54037     unhideColumn : function(colIndex){
54038         var cid = this.getColumnId(colIndex);
54039         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
54040         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
54041
54042         if(Roo.isSafari){
54043             this.updateHeaders();
54044         }
54045         this.updateSplitters();
54046         this.layout();
54047     },
54048
54049     insertRows : function(dm, firstRow, lastRow, isUpdate){
54050         if(firstRow == 0 && lastRow == dm.getCount()-1){
54051             this.refresh();
54052         }else{
54053             if(!isUpdate){
54054                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
54055             }
54056             var s = this.getScrollState();
54057             var markup = this.renderRows(firstRow, lastRow);
54058             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
54059             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
54060             this.restoreScroll(s);
54061             if(!isUpdate){
54062                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
54063                 this.syncRowHeights(firstRow, lastRow);
54064                 this.stripeRows(firstRow);
54065                 this.layout();
54066             }
54067         }
54068     },
54069
54070     bufferRows : function(markup, target, index){
54071         var before = null, trows = target.rows, tbody = target.tBodies[0];
54072         if(index < trows.length){
54073             before = trows[index];
54074         }
54075         var b = document.createElement("div");
54076         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
54077         var rows = b.firstChild.rows;
54078         for(var i = 0, len = rows.length; i < len; i++){
54079             if(before){
54080                 tbody.insertBefore(rows[0], before);
54081             }else{
54082                 tbody.appendChild(rows[0]);
54083             }
54084         }
54085         b.innerHTML = "";
54086         b = null;
54087     },
54088
54089     deleteRows : function(dm, firstRow, lastRow){
54090         if(dm.getRowCount()<1){
54091             this.fireEvent("beforerefresh", this);
54092             this.mainBody.update("");
54093             this.lockedBody.update("");
54094             this.fireEvent("refresh", this);
54095         }else{
54096             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
54097             var bt = this.getBodyTable();
54098             var tbody = bt.firstChild;
54099             var rows = bt.rows;
54100             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
54101                 tbody.removeChild(rows[firstRow]);
54102             }
54103             this.stripeRows(firstRow);
54104             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
54105         }
54106     },
54107
54108     updateRows : function(dataSource, firstRow, lastRow){
54109         var s = this.getScrollState();
54110         this.refresh();
54111         this.restoreScroll(s);
54112     },
54113
54114     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
54115         if(!noRefresh){
54116            this.refresh();
54117         }
54118         this.updateHeaderSortState();
54119     },
54120
54121     getScrollState : function(){
54122         
54123         var sb = this.scroller.dom;
54124         return {left: sb.scrollLeft, top: sb.scrollTop};
54125     },
54126
54127     stripeRows : function(startRow){
54128         if(!this.grid.stripeRows || this.ds.getCount() < 1){
54129             return;
54130         }
54131         startRow = startRow || 0;
54132         var rows = this.getBodyTable().rows;
54133         var lrows = this.getLockedTable().rows;
54134         var cls = ' x-grid-row-alt ';
54135         for(var i = startRow, len = rows.length; i < len; i++){
54136             var row = rows[i], lrow = lrows[i];
54137             var isAlt = ((i+1) % 2 == 0);
54138             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
54139             if(isAlt == hasAlt){
54140                 continue;
54141             }
54142             if(isAlt){
54143                 row.className += " x-grid-row-alt";
54144             }else{
54145                 row.className = row.className.replace("x-grid-row-alt", "");
54146             }
54147             if(lrow){
54148                 lrow.className = row.className;
54149             }
54150         }
54151     },
54152
54153     restoreScroll : function(state){
54154         //Roo.log('GridView.restoreScroll');
54155         var sb = this.scroller.dom;
54156         sb.scrollLeft = state.left;
54157         sb.scrollTop = state.top;
54158         this.syncScroll();
54159     },
54160
54161     syncScroll : function(){
54162         //Roo.log('GridView.syncScroll');
54163         var sb = this.scroller.dom;
54164         var sh = this.mainHd.dom;
54165         var bs = this.mainBody.dom;
54166         var lv = this.lockedBody.dom;
54167         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
54168         lv.scrollTop = bs.scrollTop = sb.scrollTop;
54169     },
54170
54171     handleScroll : function(e){
54172         this.syncScroll();
54173         var sb = this.scroller.dom;
54174         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
54175         e.stopEvent();
54176     },
54177
54178     handleWheel : function(e){
54179         var d = e.getWheelDelta();
54180         this.scroller.dom.scrollTop -= d*22;
54181         // set this here to prevent jumpy scrolling on large tables
54182         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
54183         e.stopEvent();
54184     },
54185
54186     renderRows : function(startRow, endRow){
54187         // pull in all the crap needed to render rows
54188         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
54189         var colCount = cm.getColumnCount();
54190
54191         if(ds.getCount() < 1){
54192             return ["", ""];
54193         }
54194
54195         // build a map for all the columns
54196         var cs = [];
54197         for(var i = 0; i < colCount; i++){
54198             var name = cm.getDataIndex(i);
54199             cs[i] = {
54200                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
54201                 renderer : cm.getRenderer(i),
54202                 id : cm.getColumnId(i),
54203                 locked : cm.isLocked(i)
54204             };
54205         }
54206
54207         startRow = startRow || 0;
54208         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
54209
54210         // records to render
54211         var rs = ds.getRange(startRow, endRow);
54212
54213         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
54214     },
54215
54216     // As much as I hate to duplicate code, this was branched because FireFox really hates
54217     // [].join("") on strings. The performance difference was substantial enough to
54218     // branch this function
54219     doRender : Roo.isGecko ?
54220             function(cs, rs, ds, startRow, colCount, stripe){
54221                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54222                 // buffers
54223                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54224                 
54225                 var hasListener = this.grid.hasListener('rowclass');
54226                 var rowcfg = {};
54227                 for(var j = 0, len = rs.length; j < len; j++){
54228                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
54229                     for(var i = 0; i < colCount; i++){
54230                         c = cs[i];
54231                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54232                         p.id = c.id;
54233                         p.css = p.attr = "";
54234                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54235                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54236                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54237                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54238                         }
54239                         var markup = ct.apply(p);
54240                         if(!c.locked){
54241                             cb+= markup;
54242                         }else{
54243                             lcb+= markup;
54244                         }
54245                     }
54246                     var alt = [];
54247                     if(stripe && ((rowIndex+1) % 2 == 0)){
54248                         alt.push("x-grid-row-alt")
54249                     }
54250                     if(r.dirty){
54251                         alt.push(  " x-grid-dirty-row");
54252                     }
54253                     rp.cells = lcb;
54254                     if(this.getRowClass){
54255                         alt.push(this.getRowClass(r, rowIndex));
54256                     }
54257                     if (hasListener) {
54258                         rowcfg = {
54259                              
54260                             record: r,
54261                             rowIndex : rowIndex,
54262                             rowClass : ''
54263                         }
54264                         this.grid.fireEvent('rowclass', this, rowcfg);
54265                         alt.push(rowcfg.rowClass);
54266                     }
54267                     rp.alt = alt.join(" ");
54268                     lbuf+= rt.apply(rp);
54269                     rp.cells = cb;
54270                     buf+=  rt.apply(rp);
54271                 }
54272                 return [lbuf, buf];
54273             } :
54274             function(cs, rs, ds, startRow, colCount, stripe){
54275                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54276                 // buffers
54277                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54278                 var hasListener = this.grid.hasListener('rowclass');
54279  
54280                 var rowcfg = {};
54281                 for(var j = 0, len = rs.length; j < len; j++){
54282                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
54283                     for(var i = 0; i < colCount; i++){
54284                         c = cs[i];
54285                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54286                         p.id = c.id;
54287                         p.css = p.attr = "";
54288                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54289                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54290                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54291                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54292                         }
54293                         
54294                         var markup = ct.apply(p);
54295                         if(!c.locked){
54296                             cb[cb.length] = markup;
54297                         }else{
54298                             lcb[lcb.length] = markup;
54299                         }
54300                     }
54301                     var alt = [];
54302                     if(stripe && ((rowIndex+1) % 2 == 0)){
54303                         alt.push( "x-grid-row-alt");
54304                     }
54305                     if(r.dirty){
54306                         alt.push(" x-grid-dirty-row");
54307                     }
54308                     rp.cells = lcb;
54309                     if(this.getRowClass){
54310                         alt.push( this.getRowClass(r, rowIndex));
54311                     }
54312                     if (hasListener) {
54313                         rowcfg = {
54314                              
54315                             record: r,
54316                             rowIndex : rowIndex,
54317                             rowClass : ''
54318                         }
54319                         this.grid.fireEvent('rowclass', this, rowcfg);
54320                         alt.push(rowcfg.rowClass);
54321                     }
54322                     rp.alt = alt.join(" ");
54323                     rp.cells = lcb.join("");
54324                     lbuf[lbuf.length] = rt.apply(rp);
54325                     rp.cells = cb.join("");
54326                     buf[buf.length] =  rt.apply(rp);
54327                 }
54328                 return [lbuf.join(""), buf.join("")];
54329             },
54330
54331     renderBody : function(){
54332         var markup = this.renderRows();
54333         var bt = this.templates.body;
54334         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
54335     },
54336
54337     /**
54338      * Refreshes the grid
54339      * @param {Boolean} headersToo
54340      */
54341     refresh : function(headersToo){
54342         this.fireEvent("beforerefresh", this);
54343         this.grid.stopEditing();
54344         var result = this.renderBody();
54345         this.lockedBody.update(result[0]);
54346         this.mainBody.update(result[1]);
54347         if(headersToo === true){
54348             this.updateHeaders();
54349             this.updateColumns();
54350             this.updateSplitters();
54351             this.updateHeaderSortState();
54352         }
54353         this.syncRowHeights();
54354         this.layout();
54355         this.fireEvent("refresh", this);
54356     },
54357
54358     handleColumnMove : function(cm, oldIndex, newIndex){
54359         this.indexMap = null;
54360         var s = this.getScrollState();
54361         this.refresh(true);
54362         this.restoreScroll(s);
54363         this.afterMove(newIndex);
54364     },
54365
54366     afterMove : function(colIndex){
54367         if(this.enableMoveAnim && Roo.enableFx){
54368             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
54369         }
54370         // if multisort - fix sortOrder, and reload..
54371         if (this.grid.dataSource.multiSort) {
54372             // the we can call sort again..
54373             var dm = this.grid.dataSource;
54374             var cm = this.grid.colModel;
54375             var so = [];
54376             for(var i = 0; i < cm.config.length; i++ ) {
54377                 
54378                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
54379                     continue; // dont' bother, it's not in sort list or being set.
54380                 }
54381                 
54382                 so.push(cm.config[i].dataIndex);
54383             };
54384             dm.sortOrder = so;
54385             dm.load(dm.lastOptions);
54386             
54387             
54388         }
54389         
54390     },
54391
54392     updateCell : function(dm, rowIndex, dataIndex){
54393         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
54394         if(typeof colIndex == "undefined"){ // not present in grid
54395             return;
54396         }
54397         var cm = this.grid.colModel;
54398         var cell = this.getCell(rowIndex, colIndex);
54399         var cellText = this.getCellText(rowIndex, colIndex);
54400
54401         var p = {
54402             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
54403             id : cm.getColumnId(colIndex),
54404             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
54405         };
54406         var renderer = cm.getRenderer(colIndex);
54407         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
54408         if(typeof val == "undefined" || val === "") val = "&#160;";
54409         cellText.innerHTML = val;
54410         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
54411         this.syncRowHeights(rowIndex, rowIndex);
54412     },
54413
54414     calcColumnWidth : function(colIndex, maxRowsToMeasure){
54415         var maxWidth = 0;
54416         if(this.grid.autoSizeHeaders){
54417             var h = this.getHeaderCellMeasure(colIndex);
54418             maxWidth = Math.max(maxWidth, h.scrollWidth);
54419         }
54420         var tb, index;
54421         if(this.cm.isLocked(colIndex)){
54422             tb = this.getLockedTable();
54423             index = colIndex;
54424         }else{
54425             tb = this.getBodyTable();
54426             index = colIndex - this.cm.getLockedCount();
54427         }
54428         if(tb && tb.rows){
54429             var rows = tb.rows;
54430             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
54431             for(var i = 0; i < stopIndex; i++){
54432                 var cell = rows[i].childNodes[index].firstChild;
54433                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
54434             }
54435         }
54436         return maxWidth + /*margin for error in IE*/ 5;
54437     },
54438     /**
54439      * Autofit a column to its content.
54440      * @param {Number} colIndex
54441      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
54442      */
54443      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
54444          if(this.cm.isHidden(colIndex)){
54445              return; // can't calc a hidden column
54446          }
54447         if(forceMinSize){
54448             var cid = this.cm.getColumnId(colIndex);
54449             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
54450            if(this.grid.autoSizeHeaders){
54451                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
54452            }
54453         }
54454         var newWidth = this.calcColumnWidth(colIndex);
54455         this.cm.setColumnWidth(colIndex,
54456             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
54457         if(!suppressEvent){
54458             this.grid.fireEvent("columnresize", colIndex, newWidth);
54459         }
54460     },
54461
54462     /**
54463      * Autofits all columns to their content and then expands to fit any extra space in the grid
54464      */
54465      autoSizeColumns : function(){
54466         var cm = this.grid.colModel;
54467         var colCount = cm.getColumnCount();
54468         for(var i = 0; i < colCount; i++){
54469             this.autoSizeColumn(i, true, true);
54470         }
54471         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
54472             this.fitColumns();
54473         }else{
54474             this.updateColumns();
54475             this.layout();
54476         }
54477     },
54478
54479     /**
54480      * Autofits all columns to the grid's width proportionate with their current size
54481      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
54482      */
54483     fitColumns : function(reserveScrollSpace){
54484         var cm = this.grid.colModel;
54485         var colCount = cm.getColumnCount();
54486         var cols = [];
54487         var width = 0;
54488         var i, w;
54489         for (i = 0; i < colCount; i++){
54490             if(!cm.isHidden(i) && !cm.isFixed(i)){
54491                 w = cm.getColumnWidth(i);
54492                 cols.push(i);
54493                 cols.push(w);
54494                 width += w;
54495             }
54496         }
54497         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
54498         if(reserveScrollSpace){
54499             avail -= 17;
54500         }
54501         var frac = (avail - cm.getTotalWidth())/width;
54502         while (cols.length){
54503             w = cols.pop();
54504             i = cols.pop();
54505             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
54506         }
54507         this.updateColumns();
54508         this.layout();
54509     },
54510
54511     onRowSelect : function(rowIndex){
54512         var row = this.getRowComposite(rowIndex);
54513         row.addClass("x-grid-row-selected");
54514     },
54515
54516     onRowDeselect : function(rowIndex){
54517         var row = this.getRowComposite(rowIndex);
54518         row.removeClass("x-grid-row-selected");
54519     },
54520
54521     onCellSelect : function(row, col){
54522         var cell = this.getCell(row, col);
54523         if(cell){
54524             Roo.fly(cell).addClass("x-grid-cell-selected");
54525         }
54526     },
54527
54528     onCellDeselect : function(row, col){
54529         var cell = this.getCell(row, col);
54530         if(cell){
54531             Roo.fly(cell).removeClass("x-grid-cell-selected");
54532         }
54533     },
54534
54535     updateHeaderSortState : function(){
54536         
54537         // sort state can be single { field: xxx, direction : yyy}
54538         // or   { xxx=>ASC , yyy : DESC ..... }
54539         
54540         var mstate = {};
54541         if (!this.ds.multiSort) { 
54542             var state = this.ds.getSortState();
54543             if(!state){
54544                 return;
54545             }
54546             mstate[state.field] = state.direction;
54547             // FIXME... - this is not used here.. but might be elsewhere..
54548             this.sortState = state;
54549             
54550         } else {
54551             mstate = this.ds.sortToggle;
54552         }
54553         //remove existing sort classes..
54554         
54555         var sc = this.sortClasses;
54556         var hds = this.el.select(this.headerSelector).removeClass(sc);
54557         
54558         for(var f in mstate) {
54559         
54560             var sortColumn = this.cm.findColumnIndex(f);
54561             
54562             if(sortColumn != -1){
54563                 var sortDir = mstate[f];        
54564                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
54565             }
54566         }
54567         
54568          
54569         
54570     },
54571
54572
54573     handleHeaderClick : function(g, index,e){
54574         
54575         Roo.log("header click");
54576         
54577         if (Roo.isTouch) {
54578             // touch events on header are handled by context
54579             this.handleHdCtx(g,index,e);
54580             return;
54581         }
54582         
54583         
54584         if(this.headersDisabled){
54585             return;
54586         }
54587         var dm = g.dataSource, cm = g.colModel;
54588         if(!cm.isSortable(index)){
54589             return;
54590         }
54591         g.stopEditing();
54592         
54593         if (dm.multiSort) {
54594             // update the sortOrder
54595             var so = [];
54596             for(var i = 0; i < cm.config.length; i++ ) {
54597                 
54598                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
54599                     continue; // dont' bother, it's not in sort list or being set.
54600                 }
54601                 
54602                 so.push(cm.config[i].dataIndex);
54603             };
54604             dm.sortOrder = so;
54605         }
54606         
54607         
54608         dm.sort(cm.getDataIndex(index));
54609     },
54610
54611
54612     destroy : function(){
54613         if(this.colMenu){
54614             this.colMenu.removeAll();
54615             Roo.menu.MenuMgr.unregister(this.colMenu);
54616             this.colMenu.getEl().remove();
54617             delete this.colMenu;
54618         }
54619         if(this.hmenu){
54620             this.hmenu.removeAll();
54621             Roo.menu.MenuMgr.unregister(this.hmenu);
54622             this.hmenu.getEl().remove();
54623             delete this.hmenu;
54624         }
54625         if(this.grid.enableColumnMove){
54626             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54627             if(dds){
54628                 for(var dd in dds){
54629                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54630                         var elid = dds[dd].dragElId;
54631                         dds[dd].unreg();
54632                         Roo.get(elid).remove();
54633                     } else if(dds[dd].config.isTarget){
54634                         dds[dd].proxyTop.remove();
54635                         dds[dd].proxyBottom.remove();
54636                         dds[dd].unreg();
54637                     }
54638                     if(Roo.dd.DDM.locationCache[dd]){
54639                         delete Roo.dd.DDM.locationCache[dd];
54640                     }
54641                 }
54642                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54643             }
54644         }
54645         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54646         this.bind(null, null);
54647         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54648     },
54649
54650     handleLockChange : function(){
54651         this.refresh(true);
54652     },
54653
54654     onDenyColumnLock : function(){
54655
54656     },
54657
54658     onDenyColumnHide : function(){
54659
54660     },
54661
54662     handleHdMenuClick : function(item){
54663         var index = this.hdCtxIndex;
54664         var cm = this.cm, ds = this.ds;
54665         switch(item.id){
54666             case "asc":
54667                 ds.sort(cm.getDataIndex(index), "ASC");
54668                 break;
54669             case "desc":
54670                 ds.sort(cm.getDataIndex(index), "DESC");
54671                 break;
54672             case "lock":
54673                 var lc = cm.getLockedCount();
54674                 if(cm.getColumnCount(true) <= lc+1){
54675                     this.onDenyColumnLock();
54676                     return;
54677                 }
54678                 if(lc != index){
54679                     cm.setLocked(index, true, true);
54680                     cm.moveColumn(index, lc);
54681                     this.grid.fireEvent("columnmove", index, lc);
54682                 }else{
54683                     cm.setLocked(index, true);
54684                 }
54685             break;
54686             case "unlock":
54687                 var lc = cm.getLockedCount();
54688                 if((lc-1) != index){
54689                     cm.setLocked(index, false, true);
54690                     cm.moveColumn(index, lc-1);
54691                     this.grid.fireEvent("columnmove", index, lc-1);
54692                 }else{
54693                     cm.setLocked(index, false);
54694                 }
54695             break;
54696             case 'wider': // used to expand cols on touch..
54697             case 'narrow':
54698                 var cw = cm.getColumnWidth(index);
54699                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54700                 cw = Math.max(0, cw);
54701                 cw = Math.min(cw,4000);
54702                 cm.setColumnWidth(index, cw);
54703                 break;
54704                 
54705             default:
54706                 index = cm.getIndexById(item.id.substr(4));
54707                 if(index != -1){
54708                     if(item.checked && cm.getColumnCount(true) <= 1){
54709                         this.onDenyColumnHide();
54710                         return false;
54711                     }
54712                     cm.setHidden(index, item.checked);
54713                 }
54714         }
54715         return true;
54716     },
54717
54718     beforeColMenuShow : function(){
54719         var cm = this.cm,  colCount = cm.getColumnCount();
54720         this.colMenu.removeAll();
54721         for(var i = 0; i < colCount; i++){
54722             this.colMenu.add(new Roo.menu.CheckItem({
54723                 id: "col-"+cm.getColumnId(i),
54724                 text: cm.getColumnHeader(i),
54725                 checked: !cm.isHidden(i),
54726                 hideOnClick:false
54727             }));
54728         }
54729     },
54730
54731     handleHdCtx : function(g, index, e){
54732         e.stopEvent();
54733         var hd = this.getHeaderCell(index);
54734         this.hdCtxIndex = index;
54735         var ms = this.hmenu.items, cm = this.cm;
54736         ms.get("asc").setDisabled(!cm.isSortable(index));
54737         ms.get("desc").setDisabled(!cm.isSortable(index));
54738         if(this.grid.enableColLock !== false){
54739             ms.get("lock").setDisabled(cm.isLocked(index));
54740             ms.get("unlock").setDisabled(!cm.isLocked(index));
54741         }
54742         this.hmenu.show(hd, "tl-bl");
54743     },
54744
54745     handleHdOver : function(e){
54746         var hd = this.findHeaderCell(e.getTarget());
54747         if(hd && !this.headersDisabled){
54748             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54749                this.fly(hd).addClass("x-grid-hd-over");
54750             }
54751         }
54752     },
54753
54754     handleHdOut : function(e){
54755         var hd = this.findHeaderCell(e.getTarget());
54756         if(hd){
54757             this.fly(hd).removeClass("x-grid-hd-over");
54758         }
54759     },
54760
54761     handleSplitDblClick : function(e, t){
54762         var i = this.getCellIndex(t);
54763         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54764             this.autoSizeColumn(i, true);
54765             this.layout();
54766         }
54767     },
54768
54769     render : function(){
54770
54771         var cm = this.cm;
54772         var colCount = cm.getColumnCount();
54773
54774         if(this.grid.monitorWindowResize === true){
54775             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54776         }
54777         var header = this.renderHeaders();
54778         var body = this.templates.body.apply({rows:""});
54779         var html = this.templates.master.apply({
54780             lockedBody: body,
54781             body: body,
54782             lockedHeader: header[0],
54783             header: header[1]
54784         });
54785
54786         //this.updateColumns();
54787
54788         this.grid.getGridEl().dom.innerHTML = html;
54789
54790         this.initElements();
54791         
54792         // a kludge to fix the random scolling effect in webkit
54793         this.el.on("scroll", function() {
54794             this.el.dom.scrollTop=0; // hopefully not recursive..
54795         },this);
54796
54797         this.scroller.on("scroll", this.handleScroll, this);
54798         this.lockedBody.on("mousewheel", this.handleWheel, this);
54799         this.mainBody.on("mousewheel", this.handleWheel, this);
54800
54801         this.mainHd.on("mouseover", this.handleHdOver, this);
54802         this.mainHd.on("mouseout", this.handleHdOut, this);
54803         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54804                 {delegate: "."+this.splitClass});
54805
54806         this.lockedHd.on("mouseover", this.handleHdOver, this);
54807         this.lockedHd.on("mouseout", this.handleHdOut, this);
54808         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54809                 {delegate: "."+this.splitClass});
54810
54811         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54812             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54813         }
54814
54815         this.updateSplitters();
54816
54817         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54818             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54819             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54820         }
54821
54822         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54823             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54824             this.hmenu.add(
54825                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54826                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54827             );
54828             if(this.grid.enableColLock !== false){
54829                 this.hmenu.add('-',
54830                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54831                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54832                 );
54833             }
54834             if (Roo.isTouch) {
54835                  this.hmenu.add('-',
54836                     {id:"wider", text: this.columnsWiderText},
54837                     {id:"narrow", text: this.columnsNarrowText }
54838                 );
54839                 
54840                  
54841             }
54842             
54843             if(this.grid.enableColumnHide !== false){
54844
54845                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54846                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54847                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54848
54849                 this.hmenu.add('-',
54850                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54851                 );
54852             }
54853             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54854
54855             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54856         }
54857
54858         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54859             this.dd = new Roo.grid.GridDragZone(this.grid, {
54860                 ddGroup : this.grid.ddGroup || 'GridDD'
54861             });
54862             
54863         }
54864
54865         /*
54866         for(var i = 0; i < colCount; i++){
54867             if(cm.isHidden(i)){
54868                 this.hideColumn(i);
54869             }
54870             if(cm.config[i].align){
54871                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54872                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54873             }
54874         }*/
54875         
54876         this.updateHeaderSortState();
54877
54878         this.beforeInitialResize();
54879         this.layout(true);
54880
54881         // two part rendering gives faster view to the user
54882         this.renderPhase2.defer(1, this);
54883     },
54884
54885     renderPhase2 : function(){
54886         // render the rows now
54887         this.refresh();
54888         if(this.grid.autoSizeColumns){
54889             this.autoSizeColumns();
54890         }
54891     },
54892
54893     beforeInitialResize : function(){
54894
54895     },
54896
54897     onColumnSplitterMoved : function(i, w){
54898         this.userResized = true;
54899         var cm = this.grid.colModel;
54900         cm.setColumnWidth(i, w, true);
54901         var cid = cm.getColumnId(i);
54902         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54903         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54904         this.updateSplitters();
54905         this.layout();
54906         this.grid.fireEvent("columnresize", i, w);
54907     },
54908
54909     syncRowHeights : function(startIndex, endIndex){
54910         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
54911             startIndex = startIndex || 0;
54912             var mrows = this.getBodyTable().rows;
54913             var lrows = this.getLockedTable().rows;
54914             var len = mrows.length-1;
54915             endIndex = Math.min(endIndex || len, len);
54916             for(var i = startIndex; i <= endIndex; i++){
54917                 var m = mrows[i], l = lrows[i];
54918                 var h = Math.max(m.offsetHeight, l.offsetHeight);
54919                 m.style.height = l.style.height = h + "px";
54920             }
54921         }
54922     },
54923
54924     layout : function(initialRender, is2ndPass){
54925         var g = this.grid;
54926         var auto = g.autoHeight;
54927         var scrollOffset = 16;
54928         var c = g.getGridEl(), cm = this.cm,
54929                 expandCol = g.autoExpandColumn,
54930                 gv = this;
54931         //c.beginMeasure();
54932
54933         if(!c.dom.offsetWidth){ // display:none?
54934             if(initialRender){
54935                 this.lockedWrap.show();
54936                 this.mainWrap.show();
54937             }
54938             return;
54939         }
54940
54941         var hasLock = this.cm.isLocked(0);
54942
54943         var tbh = this.headerPanel.getHeight();
54944         var bbh = this.footerPanel.getHeight();
54945
54946         if(auto){
54947             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
54948             var newHeight = ch + c.getBorderWidth("tb");
54949             if(g.maxHeight){
54950                 newHeight = Math.min(g.maxHeight, newHeight);
54951             }
54952             c.setHeight(newHeight);
54953         }
54954
54955         if(g.autoWidth){
54956             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54957         }
54958
54959         var s = this.scroller;
54960
54961         var csize = c.getSize(true);
54962
54963         this.el.setSize(csize.width, csize.height);
54964
54965         this.headerPanel.setWidth(csize.width);
54966         this.footerPanel.setWidth(csize.width);
54967
54968         var hdHeight = this.mainHd.getHeight();
54969         var vw = csize.width;
54970         var vh = csize.height - (tbh + bbh);
54971
54972         s.setSize(vw, vh);
54973
54974         var bt = this.getBodyTable();
54975         var ltWidth = hasLock ?
54976                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54977
54978         var scrollHeight = bt.offsetHeight;
54979         var scrollWidth = ltWidth + bt.offsetWidth;
54980         var vscroll = false, hscroll = false;
54981
54982         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
54983
54984         var lw = this.lockedWrap, mw = this.mainWrap;
54985         var lb = this.lockedBody, mb = this.mainBody;
54986
54987         setTimeout(function(){
54988             var t = s.dom.offsetTop;
54989             var w = s.dom.clientWidth,
54990                 h = s.dom.clientHeight;
54991
54992             lw.setTop(t);
54993             lw.setSize(ltWidth, h);
54994
54995             mw.setLeftTop(ltWidth, t);
54996             mw.setSize(w-ltWidth, h);
54997
54998             lb.setHeight(h-hdHeight);
54999             mb.setHeight(h-hdHeight);
55000
55001             if(is2ndPass !== true && !gv.userResized && expandCol){
55002                 // high speed resize without full column calculation
55003                 
55004                 var ci = cm.getIndexById(expandCol);
55005                 if (ci < 0) {
55006                     ci = cm.findColumnIndex(expandCol);
55007                 }
55008                 ci = Math.max(0, ci); // make sure it's got at least the first col.
55009                 var expandId = cm.getColumnId(ci);
55010                 var  tw = cm.getTotalWidth(false);
55011                 var currentWidth = cm.getColumnWidth(ci);
55012                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
55013                 if(currentWidth != cw){
55014                     cm.setColumnWidth(ci, cw, true);
55015                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
55016                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
55017                     gv.updateSplitters();
55018                     gv.layout(false, true);
55019                 }
55020             }
55021
55022             if(initialRender){
55023                 lw.show();
55024                 mw.show();
55025             }
55026             //c.endMeasure();
55027         }, 10);
55028     },
55029
55030     onWindowResize : function(){
55031         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
55032             return;
55033         }
55034         this.layout();
55035     },
55036
55037     appendFooter : function(parentEl){
55038         return null;
55039     },
55040
55041     sortAscText : "Sort Ascending",
55042     sortDescText : "Sort Descending",
55043     lockText : "Lock Column",
55044     unlockText : "Unlock Column",
55045     columnsText : "Columns",
55046  
55047     columnsWiderText : "Wider",
55048     columnsNarrowText : "Thinner"
55049 });
55050
55051
55052 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
55053     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
55054     this.proxy.el.addClass('x-grid3-col-dd');
55055 };
55056
55057 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
55058     handleMouseDown : function(e){
55059
55060     },
55061
55062     callHandleMouseDown : function(e){
55063         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
55064     }
55065 });
55066 /*
55067  * Based on:
55068  * Ext JS Library 1.1.1
55069  * Copyright(c) 2006-2007, Ext JS, LLC.
55070  *
55071  * Originally Released Under LGPL - original licence link has changed is not relivant.
55072  *
55073  * Fork - LGPL
55074  * <script type="text/javascript">
55075  */
55076  
55077 // private
55078 // This is a support class used internally by the Grid components
55079 Roo.grid.SplitDragZone = function(grid, hd, hd2){
55080     this.grid = grid;
55081     this.view = grid.getView();
55082     this.proxy = this.view.resizeProxy;
55083     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
55084         "gridSplitters" + this.grid.getGridEl().id, {
55085         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
55086     });
55087     this.setHandleElId(Roo.id(hd));
55088     this.setOuterHandleElId(Roo.id(hd2));
55089     this.scroll = false;
55090 };
55091 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
55092     fly: Roo.Element.fly,
55093
55094     b4StartDrag : function(x, y){
55095         this.view.headersDisabled = true;
55096         this.proxy.setHeight(this.view.mainWrap.getHeight());
55097         var w = this.cm.getColumnWidth(this.cellIndex);
55098         var minw = Math.max(w-this.grid.minColumnWidth, 0);
55099         this.resetConstraints();
55100         this.setXConstraint(minw, 1000);
55101         this.setYConstraint(0, 0);
55102         this.minX = x - minw;
55103         this.maxX = x + 1000;
55104         this.startPos = x;
55105         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
55106     },
55107
55108
55109     handleMouseDown : function(e){
55110         ev = Roo.EventObject.setEvent(e);
55111         var t = this.fly(ev.getTarget());
55112         if(t.hasClass("x-grid-split")){
55113             this.cellIndex = this.view.getCellIndex(t.dom);
55114             this.split = t.dom;
55115             this.cm = this.grid.colModel;
55116             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
55117                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
55118             }
55119         }
55120     },
55121
55122     endDrag : function(e){
55123         this.view.headersDisabled = false;
55124         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
55125         var diff = endX - this.startPos;
55126         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
55127     },
55128
55129     autoOffset : function(){
55130         this.setDelta(0,0);
55131     }
55132 });/*
55133  * Based on:
55134  * Ext JS Library 1.1.1
55135  * Copyright(c) 2006-2007, Ext JS, LLC.
55136  *
55137  * Originally Released Under LGPL - original licence link has changed is not relivant.
55138  *
55139  * Fork - LGPL
55140  * <script type="text/javascript">
55141  */
55142  
55143 // private
55144 // This is a support class used internally by the Grid components
55145 Roo.grid.GridDragZone = function(grid, config){
55146     this.view = grid.getView();
55147     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
55148     if(this.view.lockedBody){
55149         this.setHandleElId(Roo.id(this.view.mainBody.dom));
55150         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
55151     }
55152     this.scroll = false;
55153     this.grid = grid;
55154     this.ddel = document.createElement('div');
55155     this.ddel.className = 'x-grid-dd-wrap';
55156 };
55157
55158 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
55159     ddGroup : "GridDD",
55160
55161     getDragData : function(e){
55162         var t = Roo.lib.Event.getTarget(e);
55163         var rowIndex = this.view.findRowIndex(t);
55164         var sm = this.grid.selModel;
55165             
55166         //Roo.log(rowIndex);
55167         
55168         if (sm.getSelectedCell) {
55169             // cell selection..
55170             if (!sm.getSelectedCell()) {
55171                 return false;
55172             }
55173             if (rowIndex != sm.getSelectedCell()[0]) {
55174                 return false;
55175             }
55176         
55177         }
55178         
55179         if(rowIndex !== false){
55180             
55181             // if editorgrid.. 
55182             
55183             
55184             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
55185                
55186             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
55187               //  
55188             //}
55189             if (e.hasModifier()){
55190                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
55191             }
55192             
55193             Roo.log("getDragData");
55194             
55195             return {
55196                 grid: this.grid,
55197                 ddel: this.ddel,
55198                 rowIndex: rowIndex,
55199                 selections:sm.getSelections ? sm.getSelections() : (
55200                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
55201                 )
55202             };
55203         }
55204         return false;
55205     },
55206
55207     onInitDrag : function(e){
55208         var data = this.dragData;
55209         this.ddel.innerHTML = this.grid.getDragDropText();
55210         this.proxy.update(this.ddel);
55211         // fire start drag?
55212     },
55213
55214     afterRepair : function(){
55215         this.dragging = false;
55216     },
55217
55218     getRepairXY : function(e, data){
55219         return false;
55220     },
55221
55222     onEndDrag : function(data, e){
55223         // fire end drag?
55224     },
55225
55226     onValidDrop : function(dd, e, id){
55227         // fire drag drop?
55228         this.hideProxy();
55229     },
55230
55231     beforeInvalidDrop : function(e, id){
55232
55233     }
55234 });/*
55235  * Based on:
55236  * Ext JS Library 1.1.1
55237  * Copyright(c) 2006-2007, Ext JS, LLC.
55238  *
55239  * Originally Released Under LGPL - original licence link has changed is not relivant.
55240  *
55241  * Fork - LGPL
55242  * <script type="text/javascript">
55243  */
55244  
55245
55246 /**
55247  * @class Roo.grid.ColumnModel
55248  * @extends Roo.util.Observable
55249  * This is the default implementation of a ColumnModel used by the Grid. It defines
55250  * the columns in the grid.
55251  * <br>Usage:<br>
55252  <pre><code>
55253  var colModel = new Roo.grid.ColumnModel([
55254         {header: "Ticker", width: 60, sortable: true, locked: true},
55255         {header: "Company Name", width: 150, sortable: true},
55256         {header: "Market Cap.", width: 100, sortable: true},
55257         {header: "$ Sales", width: 100, sortable: true, renderer: money},
55258         {header: "Employees", width: 100, sortable: true, resizable: false}
55259  ]);
55260  </code></pre>
55261  * <p>
55262  
55263  * The config options listed for this class are options which may appear in each
55264  * individual column definition.
55265  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
55266  * @constructor
55267  * @param {Object} config An Array of column config objects. See this class's
55268  * config objects for details.
55269 */
55270 Roo.grid.ColumnModel = function(config){
55271         /**
55272      * The config passed into the constructor
55273      */
55274     this.config = config;
55275     this.lookup = {};
55276
55277     // if no id, create one
55278     // if the column does not have a dataIndex mapping,
55279     // map it to the order it is in the config
55280     for(var i = 0, len = config.length; i < len; i++){
55281         var c = config[i];
55282         if(typeof c.dataIndex == "undefined"){
55283             c.dataIndex = i;
55284         }
55285         if(typeof c.renderer == "string"){
55286             c.renderer = Roo.util.Format[c.renderer];
55287         }
55288         if(typeof c.id == "undefined"){
55289             c.id = Roo.id();
55290         }
55291         if(c.editor && c.editor.xtype){
55292             c.editor  = Roo.factory(c.editor, Roo.grid);
55293         }
55294         if(c.editor && c.editor.isFormField){
55295             c.editor = new Roo.grid.GridEditor(c.editor);
55296         }
55297         this.lookup[c.id] = c;
55298     }
55299
55300     /**
55301      * The width of columns which have no width specified (defaults to 100)
55302      * @type Number
55303      */
55304     this.defaultWidth = 100;
55305
55306     /**
55307      * Default sortable of columns which have no sortable specified (defaults to false)
55308      * @type Boolean
55309      */
55310     this.defaultSortable = false;
55311
55312     this.addEvents({
55313         /**
55314              * @event widthchange
55315              * Fires when the width of a column changes.
55316              * @param {ColumnModel} this
55317              * @param {Number} columnIndex The column index
55318              * @param {Number} newWidth The new width
55319              */
55320             "widthchange": true,
55321         /**
55322              * @event headerchange
55323              * Fires when the text of a header changes.
55324              * @param {ColumnModel} this
55325              * @param {Number} columnIndex The column index
55326              * @param {Number} newText The new header text
55327              */
55328             "headerchange": true,
55329         /**
55330              * @event hiddenchange
55331              * Fires when a column is hidden or "unhidden".
55332              * @param {ColumnModel} this
55333              * @param {Number} columnIndex The column index
55334              * @param {Boolean} hidden true if hidden, false otherwise
55335              */
55336             "hiddenchange": true,
55337             /**
55338          * @event columnmoved
55339          * Fires when a column is moved.
55340          * @param {ColumnModel} this
55341          * @param {Number} oldIndex
55342          * @param {Number} newIndex
55343          */
55344         "columnmoved" : true,
55345         /**
55346          * @event columlockchange
55347          * Fires when a column's locked state is changed
55348          * @param {ColumnModel} this
55349          * @param {Number} colIndex
55350          * @param {Boolean} locked true if locked
55351          */
55352         "columnlockchange" : true
55353     });
55354     Roo.grid.ColumnModel.superclass.constructor.call(this);
55355 };
55356 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
55357     /**
55358      * @cfg {String} header The header text to display in the Grid view.
55359      */
55360     /**
55361      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
55362      * {@link Roo.data.Record} definition from which to draw the column's value. If not
55363      * specified, the column's index is used as an index into the Record's data Array.
55364      */
55365     /**
55366      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
55367      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
55368      */
55369     /**
55370      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
55371      * Defaults to the value of the {@link #defaultSortable} property.
55372      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
55373      */
55374     /**
55375      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
55376      */
55377     /**
55378      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
55379      */
55380     /**
55381      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
55382      */
55383     /**
55384      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
55385      */
55386     /**
55387      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
55388      * given the cell's data value. See {@link #setRenderer}. If not specified, the
55389      * default renderer uses the raw data value. If an object is returned (bootstrap only)
55390      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
55391      */
55392        /**
55393      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
55394      */
55395     /**
55396      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
55397      */
55398     /**
55399      * @cfg {String} cursor (Optional)
55400      */
55401     /**
55402      * @cfg {String} tooltip (Optional)
55403      */
55404     /**
55405      * Returns the id of the column at the specified index.
55406      * @param {Number} index The column index
55407      * @return {String} the id
55408      */
55409     getColumnId : function(index){
55410         return this.config[index].id;
55411     },
55412
55413     /**
55414      * Returns the column for a specified id.
55415      * @param {String} id The column id
55416      * @return {Object} the column
55417      */
55418     getColumnById : function(id){
55419         return this.lookup[id];
55420     },
55421
55422     
55423     /**
55424      * Returns the column for a specified dataIndex.
55425      * @param {String} dataIndex The column dataIndex
55426      * @return {Object|Boolean} the column or false if not found
55427      */
55428     getColumnByDataIndex: function(dataIndex){
55429         var index = this.findColumnIndex(dataIndex);
55430         return index > -1 ? this.config[index] : false;
55431     },
55432     
55433     /**
55434      * Returns the index for a specified column id.
55435      * @param {String} id The column id
55436      * @return {Number} the index, or -1 if not found
55437      */
55438     getIndexById : function(id){
55439         for(var i = 0, len = this.config.length; i < len; i++){
55440             if(this.config[i].id == id){
55441                 return i;
55442             }
55443         }
55444         return -1;
55445     },
55446     
55447     /**
55448      * Returns the index for a specified column dataIndex.
55449      * @param {String} dataIndex The column dataIndex
55450      * @return {Number} the index, or -1 if not found
55451      */
55452     
55453     findColumnIndex : function(dataIndex){
55454         for(var i = 0, len = this.config.length; i < len; i++){
55455             if(this.config[i].dataIndex == dataIndex){
55456                 return i;
55457             }
55458         }
55459         return -1;
55460     },
55461     
55462     
55463     moveColumn : function(oldIndex, newIndex){
55464         var c = this.config[oldIndex];
55465         this.config.splice(oldIndex, 1);
55466         this.config.splice(newIndex, 0, c);
55467         this.dataMap = null;
55468         this.fireEvent("columnmoved", this, oldIndex, newIndex);
55469     },
55470
55471     isLocked : function(colIndex){
55472         return this.config[colIndex].locked === true;
55473     },
55474
55475     setLocked : function(colIndex, value, suppressEvent){
55476         if(this.isLocked(colIndex) == value){
55477             return;
55478         }
55479         this.config[colIndex].locked = value;
55480         if(!suppressEvent){
55481             this.fireEvent("columnlockchange", this, colIndex, value);
55482         }
55483     },
55484
55485     getTotalLockedWidth : function(){
55486         var totalWidth = 0;
55487         for(var i = 0; i < this.config.length; i++){
55488             if(this.isLocked(i) && !this.isHidden(i)){
55489                 this.totalWidth += this.getColumnWidth(i);
55490             }
55491         }
55492         return totalWidth;
55493     },
55494
55495     getLockedCount : function(){
55496         for(var i = 0, len = this.config.length; i < len; i++){
55497             if(!this.isLocked(i)){
55498                 return i;
55499             }
55500         }
55501     },
55502
55503     /**
55504      * Returns the number of columns.
55505      * @return {Number}
55506      */
55507     getColumnCount : function(visibleOnly){
55508         if(visibleOnly === true){
55509             var c = 0;
55510             for(var i = 0, len = this.config.length; i < len; i++){
55511                 if(!this.isHidden(i)){
55512                     c++;
55513                 }
55514             }
55515             return c;
55516         }
55517         return this.config.length;
55518     },
55519
55520     /**
55521      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
55522      * @param {Function} fn
55523      * @param {Object} scope (optional)
55524      * @return {Array} result
55525      */
55526     getColumnsBy : function(fn, scope){
55527         var r = [];
55528         for(var i = 0, len = this.config.length; i < len; i++){
55529             var c = this.config[i];
55530             if(fn.call(scope||this, c, i) === true){
55531                 r[r.length] = c;
55532             }
55533         }
55534         return r;
55535     },
55536
55537     /**
55538      * Returns true if the specified column is sortable.
55539      * @param {Number} col The column index
55540      * @return {Boolean}
55541      */
55542     isSortable : function(col){
55543         if(typeof this.config[col].sortable == "undefined"){
55544             return this.defaultSortable;
55545         }
55546         return this.config[col].sortable;
55547     },
55548
55549     /**
55550      * Returns the rendering (formatting) function defined for the column.
55551      * @param {Number} col The column index.
55552      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
55553      */
55554     getRenderer : function(col){
55555         if(!this.config[col].renderer){
55556             return Roo.grid.ColumnModel.defaultRenderer;
55557         }
55558         return this.config[col].renderer;
55559     },
55560
55561     /**
55562      * Sets the rendering (formatting) function for a column.
55563      * @param {Number} col The column index
55564      * @param {Function} fn The function to use to process the cell's raw data
55565      * to return HTML markup for the grid view. The render function is called with
55566      * the following parameters:<ul>
55567      * <li>Data value.</li>
55568      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
55569      * <li>css A CSS style string to apply to the table cell.</li>
55570      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
55571      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
55572      * <li>Row index</li>
55573      * <li>Column index</li>
55574      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
55575      */
55576     setRenderer : function(col, fn){
55577         this.config[col].renderer = fn;
55578     },
55579
55580     /**
55581      * Returns the width for the specified column.
55582      * @param {Number} col The column index
55583      * @return {Number}
55584      */
55585     getColumnWidth : function(col){
55586         return this.config[col].width * 1 || this.defaultWidth;
55587     },
55588
55589     /**
55590      * Sets the width for a column.
55591      * @param {Number} col The column index
55592      * @param {Number} width The new width
55593      */
55594     setColumnWidth : function(col, width, suppressEvent){
55595         this.config[col].width = width;
55596         this.totalWidth = null;
55597         if(!suppressEvent){
55598              this.fireEvent("widthchange", this, col, width);
55599         }
55600     },
55601
55602     /**
55603      * Returns the total width of all columns.
55604      * @param {Boolean} includeHidden True to include hidden column widths
55605      * @return {Number}
55606      */
55607     getTotalWidth : function(includeHidden){
55608         if(!this.totalWidth){
55609             this.totalWidth = 0;
55610             for(var i = 0, len = this.config.length; i < len; i++){
55611                 if(includeHidden || !this.isHidden(i)){
55612                     this.totalWidth += this.getColumnWidth(i);
55613                 }
55614             }
55615         }
55616         return this.totalWidth;
55617     },
55618
55619     /**
55620      * Returns the header for the specified column.
55621      * @param {Number} col The column index
55622      * @return {String}
55623      */
55624     getColumnHeader : function(col){
55625         return this.config[col].header;
55626     },
55627
55628     /**
55629      * Sets the header for a column.
55630      * @param {Number} col The column index
55631      * @param {String} header The new header
55632      */
55633     setColumnHeader : function(col, header){
55634         this.config[col].header = header;
55635         this.fireEvent("headerchange", this, col, header);
55636     },
55637
55638     /**
55639      * Returns the tooltip for the specified column.
55640      * @param {Number} col The column index
55641      * @return {String}
55642      */
55643     getColumnTooltip : function(col){
55644             return this.config[col].tooltip;
55645     },
55646     /**
55647      * Sets the tooltip for a column.
55648      * @param {Number} col The column index
55649      * @param {String} tooltip The new tooltip
55650      */
55651     setColumnTooltip : function(col, tooltip){
55652             this.config[col].tooltip = tooltip;
55653     },
55654
55655     /**
55656      * Returns the dataIndex for the specified column.
55657      * @param {Number} col The column index
55658      * @return {Number}
55659      */
55660     getDataIndex : function(col){
55661         return this.config[col].dataIndex;
55662     },
55663
55664     /**
55665      * Sets the dataIndex for a column.
55666      * @param {Number} col The column index
55667      * @param {Number} dataIndex The new dataIndex
55668      */
55669     setDataIndex : function(col, dataIndex){
55670         this.config[col].dataIndex = dataIndex;
55671     },
55672
55673     
55674     
55675     /**
55676      * Returns true if the cell is editable.
55677      * @param {Number} colIndex The column index
55678      * @param {Number} rowIndex The row index
55679      * @return {Boolean}
55680      */
55681     isCellEditable : function(colIndex, rowIndex){
55682         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55683     },
55684
55685     /**
55686      * Returns the editor defined for the cell/column.
55687      * return false or null to disable editing.
55688      * @param {Number} colIndex The column index
55689      * @param {Number} rowIndex The row index
55690      * @return {Object}
55691      */
55692     getCellEditor : function(colIndex, rowIndex){
55693         return this.config[colIndex].editor;
55694     },
55695
55696     /**
55697      * Sets if a column is editable.
55698      * @param {Number} col The column index
55699      * @param {Boolean} editable True if the column is editable
55700      */
55701     setEditable : function(col, editable){
55702         this.config[col].editable = editable;
55703     },
55704
55705
55706     /**
55707      * Returns true if the column is hidden.
55708      * @param {Number} colIndex The column index
55709      * @return {Boolean}
55710      */
55711     isHidden : function(colIndex){
55712         return this.config[colIndex].hidden;
55713     },
55714
55715
55716     /**
55717      * Returns true if the column width cannot be changed
55718      */
55719     isFixed : function(colIndex){
55720         return this.config[colIndex].fixed;
55721     },
55722
55723     /**
55724      * Returns true if the column can be resized
55725      * @return {Boolean}
55726      */
55727     isResizable : function(colIndex){
55728         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55729     },
55730     /**
55731      * Sets if a column is hidden.
55732      * @param {Number} colIndex The column index
55733      * @param {Boolean} hidden True if the column is hidden
55734      */
55735     setHidden : function(colIndex, hidden){
55736         this.config[colIndex].hidden = hidden;
55737         this.totalWidth = null;
55738         this.fireEvent("hiddenchange", this, colIndex, hidden);
55739     },
55740
55741     /**
55742      * Sets the editor for a column.
55743      * @param {Number} col The column index
55744      * @param {Object} editor The editor object
55745      */
55746     setEditor : function(col, editor){
55747         this.config[col].editor = editor;
55748     }
55749 });
55750
55751 Roo.grid.ColumnModel.defaultRenderer = function(value){
55752         if(typeof value == "string" && value.length < 1){
55753             return "&#160;";
55754         }
55755         return value;
55756 };
55757
55758 // Alias for backwards compatibility
55759 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55760 /*
55761  * Based on:
55762  * Ext JS Library 1.1.1
55763  * Copyright(c) 2006-2007, Ext JS, LLC.
55764  *
55765  * Originally Released Under LGPL - original licence link has changed is not relivant.
55766  *
55767  * Fork - LGPL
55768  * <script type="text/javascript">
55769  */
55770
55771 /**
55772  * @class Roo.grid.AbstractSelectionModel
55773  * @extends Roo.util.Observable
55774  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55775  * implemented by descendant classes.  This class should not be directly instantiated.
55776  * @constructor
55777  */
55778 Roo.grid.AbstractSelectionModel = function(){
55779     this.locked = false;
55780     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55781 };
55782
55783 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55784     /** @ignore Called by the grid automatically. Do not call directly. */
55785     init : function(grid){
55786         this.grid = grid;
55787         this.initEvents();
55788     },
55789
55790     /**
55791      * Locks the selections.
55792      */
55793     lock : function(){
55794         this.locked = true;
55795     },
55796
55797     /**
55798      * Unlocks the selections.
55799      */
55800     unlock : function(){
55801         this.locked = false;
55802     },
55803
55804     /**
55805      * Returns true if the selections are locked.
55806      * @return {Boolean}
55807      */
55808     isLocked : function(){
55809         return this.locked;
55810     }
55811 });/*
55812  * Based on:
55813  * Ext JS Library 1.1.1
55814  * Copyright(c) 2006-2007, Ext JS, LLC.
55815  *
55816  * Originally Released Under LGPL - original licence link has changed is not relivant.
55817  *
55818  * Fork - LGPL
55819  * <script type="text/javascript">
55820  */
55821 /**
55822  * @extends Roo.grid.AbstractSelectionModel
55823  * @class Roo.grid.RowSelectionModel
55824  * The default SelectionModel used by {@link Roo.grid.Grid}.
55825  * It supports multiple selections and keyboard selection/navigation. 
55826  * @constructor
55827  * @param {Object} config
55828  */
55829 Roo.grid.RowSelectionModel = function(config){
55830     Roo.apply(this, config);
55831     this.selections = new Roo.util.MixedCollection(false, function(o){
55832         return o.id;
55833     });
55834
55835     this.last = false;
55836     this.lastActive = false;
55837
55838     this.addEvents({
55839         /**
55840              * @event selectionchange
55841              * Fires when the selection changes
55842              * @param {SelectionModel} this
55843              */
55844             "selectionchange" : true,
55845         /**
55846              * @event afterselectionchange
55847              * Fires after the selection changes (eg. by key press or clicking)
55848              * @param {SelectionModel} this
55849              */
55850             "afterselectionchange" : true,
55851         /**
55852              * @event beforerowselect
55853              * Fires when a row is selected being selected, return false to cancel.
55854              * @param {SelectionModel} this
55855              * @param {Number} rowIndex The selected index
55856              * @param {Boolean} keepExisting False if other selections will be cleared
55857              */
55858             "beforerowselect" : true,
55859         /**
55860              * @event rowselect
55861              * Fires when a row is selected.
55862              * @param {SelectionModel} this
55863              * @param {Number} rowIndex The selected index
55864              * @param {Roo.data.Record} r The record
55865              */
55866             "rowselect" : true,
55867         /**
55868              * @event rowdeselect
55869              * Fires when a row is deselected.
55870              * @param {SelectionModel} this
55871              * @param {Number} rowIndex The selected index
55872              */
55873         "rowdeselect" : true
55874     });
55875     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55876     this.locked = false;
55877 };
55878
55879 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
55880     /**
55881      * @cfg {Boolean} singleSelect
55882      * True to allow selection of only one row at a time (defaults to false)
55883      */
55884     singleSelect : false,
55885
55886     // private
55887     initEvents : function(){
55888
55889         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
55890             this.grid.on("mousedown", this.handleMouseDown, this);
55891         }else{ // allow click to work like normal
55892             this.grid.on("rowclick", this.handleDragableRowClick, this);
55893         }
55894
55895         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
55896             "up" : function(e){
55897                 if(!e.shiftKey){
55898                     this.selectPrevious(e.shiftKey);
55899                 }else if(this.last !== false && this.lastActive !== false){
55900                     var last = this.last;
55901                     this.selectRange(this.last,  this.lastActive-1);
55902                     this.grid.getView().focusRow(this.lastActive);
55903                     if(last !== false){
55904                         this.last = last;
55905                     }
55906                 }else{
55907                     this.selectFirstRow();
55908                 }
55909                 this.fireEvent("afterselectionchange", this);
55910             },
55911             "down" : function(e){
55912                 if(!e.shiftKey){
55913                     this.selectNext(e.shiftKey);
55914                 }else if(this.last !== false && this.lastActive !== false){
55915                     var last = this.last;
55916                     this.selectRange(this.last,  this.lastActive+1);
55917                     this.grid.getView().focusRow(this.lastActive);
55918                     if(last !== false){
55919                         this.last = last;
55920                     }
55921                 }else{
55922                     this.selectFirstRow();
55923                 }
55924                 this.fireEvent("afterselectionchange", this);
55925             },
55926             scope: this
55927         });
55928
55929         var view = this.grid.view;
55930         view.on("refresh", this.onRefresh, this);
55931         view.on("rowupdated", this.onRowUpdated, this);
55932         view.on("rowremoved", this.onRemove, this);
55933     },
55934
55935     // private
55936     onRefresh : function(){
55937         var ds = this.grid.dataSource, i, v = this.grid.view;
55938         var s = this.selections;
55939         s.each(function(r){
55940             if((i = ds.indexOfId(r.id)) != -1){
55941                 v.onRowSelect(i);
55942                 s.add(ds.getAt(i)); // updating the selection relate data
55943             }else{
55944                 s.remove(r);
55945             }
55946         });
55947     },
55948
55949     // private
55950     onRemove : function(v, index, r){
55951         this.selections.remove(r);
55952     },
55953
55954     // private
55955     onRowUpdated : function(v, index, r){
55956         if(this.isSelected(r)){
55957             v.onRowSelect(index);
55958         }
55959     },
55960
55961     /**
55962      * Select records.
55963      * @param {Array} records The records to select
55964      * @param {Boolean} keepExisting (optional) True to keep existing selections
55965      */
55966     selectRecords : function(records, keepExisting){
55967         if(!keepExisting){
55968             this.clearSelections();
55969         }
55970         var ds = this.grid.dataSource;
55971         for(var i = 0, len = records.length; i < len; i++){
55972             this.selectRow(ds.indexOf(records[i]), true);
55973         }
55974     },
55975
55976     /**
55977      * Gets the number of selected rows.
55978      * @return {Number}
55979      */
55980     getCount : function(){
55981         return this.selections.length;
55982     },
55983
55984     /**
55985      * Selects the first row in the grid.
55986      */
55987     selectFirstRow : function(){
55988         this.selectRow(0);
55989     },
55990
55991     /**
55992      * Select the last row.
55993      * @param {Boolean} keepExisting (optional) True to keep existing selections
55994      */
55995     selectLastRow : function(keepExisting){
55996         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
55997     },
55998
55999     /**
56000      * Selects the row immediately following the last selected row.
56001      * @param {Boolean} keepExisting (optional) True to keep existing selections
56002      */
56003     selectNext : function(keepExisting){
56004         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
56005             this.selectRow(this.last+1, keepExisting);
56006             this.grid.getView().focusRow(this.last);
56007         }
56008     },
56009
56010     /**
56011      * Selects the row that precedes the last selected row.
56012      * @param {Boolean} keepExisting (optional) True to keep existing selections
56013      */
56014     selectPrevious : function(keepExisting){
56015         if(this.last){
56016             this.selectRow(this.last-1, keepExisting);
56017             this.grid.getView().focusRow(this.last);
56018         }
56019     },
56020
56021     /**
56022      * Returns the selected records
56023      * @return {Array} Array of selected records
56024      */
56025     getSelections : function(){
56026         return [].concat(this.selections.items);
56027     },
56028
56029     /**
56030      * Returns the first selected record.
56031      * @return {Record}
56032      */
56033     getSelected : function(){
56034         return this.selections.itemAt(0);
56035     },
56036
56037
56038     /**
56039      * Clears all selections.
56040      */
56041     clearSelections : function(fast){
56042         if(this.locked) return;
56043         if(fast !== true){
56044             var ds = this.grid.dataSource;
56045             var s = this.selections;
56046             s.each(function(r){
56047                 this.deselectRow(ds.indexOfId(r.id));
56048             }, this);
56049             s.clear();
56050         }else{
56051             this.selections.clear();
56052         }
56053         this.last = false;
56054     },
56055
56056
56057     /**
56058      * Selects all rows.
56059      */
56060     selectAll : function(){
56061         if(this.locked) return;
56062         this.selections.clear();
56063         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
56064             this.selectRow(i, true);
56065         }
56066     },
56067
56068     /**
56069      * Returns True if there is a selection.
56070      * @return {Boolean}
56071      */
56072     hasSelection : function(){
56073         return this.selections.length > 0;
56074     },
56075
56076     /**
56077      * Returns True if the specified row is selected.
56078      * @param {Number/Record} record The record or index of the record to check
56079      * @return {Boolean}
56080      */
56081     isSelected : function(index){
56082         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
56083         return (r && this.selections.key(r.id) ? true : false);
56084     },
56085
56086     /**
56087      * Returns True if the specified record id is selected.
56088      * @param {String} id The id of record to check
56089      * @return {Boolean}
56090      */
56091     isIdSelected : function(id){
56092         return (this.selections.key(id) ? true : false);
56093     },
56094
56095     // private
56096     handleMouseDown : function(e, t){
56097         var view = this.grid.getView(), rowIndex;
56098         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
56099             return;
56100         };
56101         if(e.shiftKey && this.last !== false){
56102             var last = this.last;
56103             this.selectRange(last, rowIndex, e.ctrlKey);
56104             this.last = last; // reset the last
56105             view.focusRow(rowIndex);
56106         }else{
56107             var isSelected = this.isSelected(rowIndex);
56108             if(e.button !== 0 && isSelected){
56109                 view.focusRow(rowIndex);
56110             }else if(e.ctrlKey && isSelected){
56111                 this.deselectRow(rowIndex);
56112             }else if(!isSelected){
56113                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
56114                 view.focusRow(rowIndex);
56115             }
56116         }
56117         this.fireEvent("afterselectionchange", this);
56118     },
56119     // private
56120     handleDragableRowClick :  function(grid, rowIndex, e) 
56121     {
56122         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
56123             this.selectRow(rowIndex, false);
56124             grid.view.focusRow(rowIndex);
56125              this.fireEvent("afterselectionchange", this);
56126         }
56127     },
56128     
56129     /**
56130      * Selects multiple rows.
56131      * @param {Array} rows Array of the indexes of the row to select
56132      * @param {Boolean} keepExisting (optional) True to keep existing selections
56133      */
56134     selectRows : function(rows, keepExisting){
56135         if(!keepExisting){
56136             this.clearSelections();
56137         }
56138         for(var i = 0, len = rows.length; i < len; i++){
56139             this.selectRow(rows[i], true);
56140         }
56141     },
56142
56143     /**
56144      * Selects a range of rows. All rows in between startRow and endRow are also selected.
56145      * @param {Number} startRow The index of the first row in the range
56146      * @param {Number} endRow The index of the last row in the range
56147      * @param {Boolean} keepExisting (optional) True to retain existing selections
56148      */
56149     selectRange : function(startRow, endRow, keepExisting){
56150         if(this.locked) return;
56151         if(!keepExisting){
56152             this.clearSelections();
56153         }
56154         if(startRow <= endRow){
56155             for(var i = startRow; i <= endRow; i++){
56156                 this.selectRow(i, true);
56157             }
56158         }else{
56159             for(var i = startRow; i >= endRow; i--){
56160                 this.selectRow(i, true);
56161             }
56162         }
56163     },
56164
56165     /**
56166      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
56167      * @param {Number} startRow The index of the first row in the range
56168      * @param {Number} endRow The index of the last row in the range
56169      */
56170     deselectRange : function(startRow, endRow, preventViewNotify){
56171         if(this.locked) return;
56172         for(var i = startRow; i <= endRow; i++){
56173             this.deselectRow(i, preventViewNotify);
56174         }
56175     },
56176
56177     /**
56178      * Selects a row.
56179      * @param {Number} row The index of the row to select
56180      * @param {Boolean} keepExisting (optional) True to keep existing selections
56181      */
56182     selectRow : function(index, keepExisting, preventViewNotify){
56183         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
56184         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
56185             if(!keepExisting || this.singleSelect){
56186                 this.clearSelections();
56187             }
56188             var r = this.grid.dataSource.getAt(index);
56189             this.selections.add(r);
56190             this.last = this.lastActive = index;
56191             if(!preventViewNotify){
56192                 this.grid.getView().onRowSelect(index);
56193             }
56194             this.fireEvent("rowselect", this, index, r);
56195             this.fireEvent("selectionchange", this);
56196         }
56197     },
56198
56199     /**
56200      * Deselects a row.
56201      * @param {Number} row The index of the row to deselect
56202      */
56203     deselectRow : function(index, preventViewNotify){
56204         if(this.locked) return;
56205         if(this.last == index){
56206             this.last = false;
56207         }
56208         if(this.lastActive == index){
56209             this.lastActive = false;
56210         }
56211         var r = this.grid.dataSource.getAt(index);
56212         this.selections.remove(r);
56213         if(!preventViewNotify){
56214             this.grid.getView().onRowDeselect(index);
56215         }
56216         this.fireEvent("rowdeselect", this, index);
56217         this.fireEvent("selectionchange", this);
56218     },
56219
56220     // private
56221     restoreLast : function(){
56222         if(this._last){
56223             this.last = this._last;
56224         }
56225     },
56226
56227     // private
56228     acceptsNav : function(row, col, cm){
56229         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56230     },
56231
56232     // private
56233     onEditorKey : function(field, e){
56234         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
56235         if(k == e.TAB){
56236             e.stopEvent();
56237             ed.completeEdit();
56238             if(e.shiftKey){
56239                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56240             }else{
56241                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56242             }
56243         }else if(k == e.ENTER && !e.ctrlKey){
56244             e.stopEvent();
56245             ed.completeEdit();
56246             if(e.shiftKey){
56247                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
56248             }else{
56249                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
56250             }
56251         }else if(k == e.ESC){
56252             ed.cancelEdit();
56253         }
56254         if(newCell){
56255             g.startEditing(newCell[0], newCell[1]);
56256         }
56257     }
56258 });/*
56259  * Based on:
56260  * Ext JS Library 1.1.1
56261  * Copyright(c) 2006-2007, Ext JS, LLC.
56262  *
56263  * Originally Released Under LGPL - original licence link has changed is not relivant.
56264  *
56265  * Fork - LGPL
56266  * <script type="text/javascript">
56267  */
56268 /**
56269  * @class Roo.grid.CellSelectionModel
56270  * @extends Roo.grid.AbstractSelectionModel
56271  * This class provides the basic implementation for cell selection in a grid.
56272  * @constructor
56273  * @param {Object} config The object containing the configuration of this model.
56274  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
56275  */
56276 Roo.grid.CellSelectionModel = function(config){
56277     Roo.apply(this, config);
56278
56279     this.selection = null;
56280
56281     this.addEvents({
56282         /**
56283              * @event beforerowselect
56284              * Fires before a cell is selected.
56285              * @param {SelectionModel} this
56286              * @param {Number} rowIndex The selected row index
56287              * @param {Number} colIndex The selected cell index
56288              */
56289             "beforecellselect" : true,
56290         /**
56291              * @event cellselect
56292              * Fires when a cell is selected.
56293              * @param {SelectionModel} this
56294              * @param {Number} rowIndex The selected row index
56295              * @param {Number} colIndex The selected cell index
56296              */
56297             "cellselect" : true,
56298         /**
56299              * @event selectionchange
56300              * Fires when the active selection changes.
56301              * @param {SelectionModel} this
56302              * @param {Object} selection null for no selection or an object (o) with two properties
56303                 <ul>
56304                 <li>o.record: the record object for the row the selection is in</li>
56305                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
56306                 </ul>
56307              */
56308             "selectionchange" : true,
56309         /**
56310              * @event tabend
56311              * Fires when the tab (or enter) was pressed on the last editable cell
56312              * You can use this to trigger add new row.
56313              * @param {SelectionModel} this
56314              */
56315             "tabend" : true,
56316          /**
56317              * @event beforeeditnext
56318              * Fires before the next editable sell is made active
56319              * You can use this to skip to another cell or fire the tabend
56320              *    if you set cell to false
56321              * @param {Object} eventdata object : { cell : [ row, col ] } 
56322              */
56323             "beforeeditnext" : true
56324     });
56325     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
56326 };
56327
56328 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
56329     
56330     enter_is_tab: false,
56331
56332     /** @ignore */
56333     initEvents : function(){
56334         this.grid.on("mousedown", this.handleMouseDown, this);
56335         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
56336         var view = this.grid.view;
56337         view.on("refresh", this.onViewChange, this);
56338         view.on("rowupdated", this.onRowUpdated, this);
56339         view.on("beforerowremoved", this.clearSelections, this);
56340         view.on("beforerowsinserted", this.clearSelections, this);
56341         if(this.grid.isEditor){
56342             this.grid.on("beforeedit", this.beforeEdit,  this);
56343         }
56344     },
56345
56346         //private
56347     beforeEdit : function(e){
56348         this.select(e.row, e.column, false, true, e.record);
56349     },
56350
56351         //private
56352     onRowUpdated : function(v, index, r){
56353         if(this.selection && this.selection.record == r){
56354             v.onCellSelect(index, this.selection.cell[1]);
56355         }
56356     },
56357
56358         //private
56359     onViewChange : function(){
56360         this.clearSelections(true);
56361     },
56362
56363         /**
56364          * Returns the currently selected cell,.
56365          * @return {Array} The selected cell (row, column) or null if none selected.
56366          */
56367     getSelectedCell : function(){
56368         return this.selection ? this.selection.cell : null;
56369     },
56370
56371     /**
56372      * Clears all selections.
56373      * @param {Boolean} true to prevent the gridview from being notified about the change.
56374      */
56375     clearSelections : function(preventNotify){
56376         var s = this.selection;
56377         if(s){
56378             if(preventNotify !== true){
56379                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
56380             }
56381             this.selection = null;
56382             this.fireEvent("selectionchange", this, null);
56383         }
56384     },
56385
56386     /**
56387      * Returns true if there is a selection.
56388      * @return {Boolean}
56389      */
56390     hasSelection : function(){
56391         return this.selection ? true : false;
56392     },
56393
56394     /** @ignore */
56395     handleMouseDown : function(e, t){
56396         var v = this.grid.getView();
56397         if(this.isLocked()){
56398             return;
56399         };
56400         var row = v.findRowIndex(t);
56401         var cell = v.findCellIndex(t);
56402         if(row !== false && cell !== false){
56403             this.select(row, cell);
56404         }
56405     },
56406
56407     /**
56408      * Selects a cell.
56409      * @param {Number} rowIndex
56410      * @param {Number} collIndex
56411      */
56412     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
56413         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
56414             this.clearSelections();
56415             r = r || this.grid.dataSource.getAt(rowIndex);
56416             this.selection = {
56417                 record : r,
56418                 cell : [rowIndex, colIndex]
56419             };
56420             if(!preventViewNotify){
56421                 var v = this.grid.getView();
56422                 v.onCellSelect(rowIndex, colIndex);
56423                 if(preventFocus !== true){
56424                     v.focusCell(rowIndex, colIndex);
56425                 }
56426             }
56427             this.fireEvent("cellselect", this, rowIndex, colIndex);
56428             this.fireEvent("selectionchange", this, this.selection);
56429         }
56430     },
56431
56432         //private
56433     isSelectable : function(rowIndex, colIndex, cm){
56434         return !cm.isHidden(colIndex);
56435     },
56436
56437     /** @ignore */
56438     handleKeyDown : function(e){
56439         //Roo.log('Cell Sel Model handleKeyDown');
56440         if(!e.isNavKeyPress()){
56441             return;
56442         }
56443         var g = this.grid, s = this.selection;
56444         if(!s){
56445             e.stopEvent();
56446             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
56447             if(cell){
56448                 this.select(cell[0], cell[1]);
56449             }
56450             return;
56451         }
56452         var sm = this;
56453         var walk = function(row, col, step){
56454             return g.walkCells(row, col, step, sm.isSelectable,  sm);
56455         };
56456         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
56457         var newCell;
56458
56459       
56460
56461         switch(k){
56462             case e.TAB:
56463                 // handled by onEditorKey
56464                 if (g.isEditor && g.editing) {
56465                     return;
56466                 }
56467                 if(e.shiftKey) {
56468                     newCell = walk(r, c-1, -1);
56469                 } else {
56470                     newCell = walk(r, c+1, 1);
56471                 }
56472                 break;
56473             
56474             case e.DOWN:
56475                newCell = walk(r+1, c, 1);
56476                 break;
56477             
56478             case e.UP:
56479                 newCell = walk(r-1, c, -1);
56480                 break;
56481             
56482             case e.RIGHT:
56483                 newCell = walk(r, c+1, 1);
56484                 break;
56485             
56486             case e.LEFT:
56487                 newCell = walk(r, c-1, -1);
56488                 break;
56489             
56490             case e.ENTER:
56491                 
56492                 if(g.isEditor && !g.editing){
56493                    g.startEditing(r, c);
56494                    e.stopEvent();
56495                    return;
56496                 }
56497                 
56498                 
56499              break;
56500         };
56501         if(newCell){
56502             this.select(newCell[0], newCell[1]);
56503             e.stopEvent();
56504             
56505         }
56506     },
56507
56508     acceptsNav : function(row, col, cm){
56509         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56510     },
56511     /**
56512      * Selects a cell.
56513      * @param {Number} field (not used) - as it's normally used as a listener
56514      * @param {Number} e - event - fake it by using
56515      *
56516      * var e = Roo.EventObjectImpl.prototype;
56517      * e.keyCode = e.TAB
56518      *
56519      * 
56520      */
56521     onEditorKey : function(field, e){
56522         
56523         var k = e.getKey(),
56524             newCell,
56525             g = this.grid,
56526             ed = g.activeEditor,
56527             forward = false;
56528         ///Roo.log('onEditorKey' + k);
56529         
56530         
56531         if (this.enter_is_tab && k == e.ENTER) {
56532             k = e.TAB;
56533         }
56534         
56535         if(k == e.TAB){
56536             if(e.shiftKey){
56537                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56538             }else{
56539                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56540                 forward = true;
56541             }
56542             
56543             e.stopEvent();
56544             
56545         } else if(k == e.ENTER &&  !e.ctrlKey){
56546             ed.completeEdit();
56547             e.stopEvent();
56548             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56549         
56550                 } else if(k == e.ESC){
56551             ed.cancelEdit();
56552         }
56553                 
56554         if (newCell) {
56555             var ecall = { cell : newCell, forward : forward };
56556             this.fireEvent('beforeeditnext', ecall );
56557             newCell = ecall.cell;
56558                         forward = ecall.forward;
56559         }
56560                 
56561         if(newCell){
56562             //Roo.log('next cell after edit');
56563             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
56564         } else if (forward) {
56565             // tabbed past last
56566             this.fireEvent.defer(100, this, ['tabend',this]);
56567         }
56568     }
56569 });/*
56570  * Based on:
56571  * Ext JS Library 1.1.1
56572  * Copyright(c) 2006-2007, Ext JS, LLC.
56573  *
56574  * Originally Released Under LGPL - original licence link has changed is not relivant.
56575  *
56576  * Fork - LGPL
56577  * <script type="text/javascript">
56578  */
56579  
56580 /**
56581  * @class Roo.grid.EditorGrid
56582  * @extends Roo.grid.Grid
56583  * Class for creating and editable grid.
56584  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
56585  * The container MUST have some type of size defined for the grid to fill. The container will be 
56586  * automatically set to position relative if it isn't already.
56587  * @param {Object} dataSource The data model to bind to
56588  * @param {Object} colModel The column model with info about this grid's columns
56589  */
56590 Roo.grid.EditorGrid = function(container, config){
56591     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
56592     this.getGridEl().addClass("xedit-grid");
56593
56594     if(!this.selModel){
56595         this.selModel = new Roo.grid.CellSelectionModel();
56596     }
56597
56598     this.activeEditor = null;
56599
56600         this.addEvents({
56601             /**
56602              * @event beforeedit
56603              * Fires before cell editing is triggered. The edit event object has the following properties <br />
56604              * <ul style="padding:5px;padding-left:16px;">
56605              * <li>grid - This grid</li>
56606              * <li>record - The record being edited</li>
56607              * <li>field - The field name being edited</li>
56608              * <li>value - The value for the field being edited.</li>
56609              * <li>row - The grid row index</li>
56610              * <li>column - The grid column index</li>
56611              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56612              * </ul>
56613              * @param {Object} e An edit event (see above for description)
56614              */
56615             "beforeedit" : true,
56616             /**
56617              * @event afteredit
56618              * Fires after a cell is edited. <br />
56619              * <ul style="padding:5px;padding-left:16px;">
56620              * <li>grid - This grid</li>
56621              * <li>record - The record being edited</li>
56622              * <li>field - The field name being edited</li>
56623              * <li>value - The value being set</li>
56624              * <li>originalValue - The original value for the field, before the edit.</li>
56625              * <li>row - The grid row index</li>
56626              * <li>column - The grid column index</li>
56627              * </ul>
56628              * @param {Object} e An edit event (see above for description)
56629              */
56630             "afteredit" : true,
56631             /**
56632              * @event validateedit
56633              * Fires after a cell is edited, but before the value is set in the record. 
56634          * You can use this to modify the value being set in the field, Return false
56635              * to cancel the change. The edit event object has the following properties <br />
56636              * <ul style="padding:5px;padding-left:16px;">
56637          * <li>editor - This editor</li>
56638              * <li>grid - This grid</li>
56639              * <li>record - The record being edited</li>
56640              * <li>field - The field name being edited</li>
56641              * <li>value - The value being set</li>
56642              * <li>originalValue - The original value for the field, before the edit.</li>
56643              * <li>row - The grid row index</li>
56644              * <li>column - The grid column index</li>
56645              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56646              * </ul>
56647              * @param {Object} e An edit event (see above for description)
56648              */
56649             "validateedit" : true
56650         });
56651     this.on("bodyscroll", this.stopEditing,  this);
56652     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56653 };
56654
56655 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56656     /**
56657      * @cfg {Number} clicksToEdit
56658      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56659      */
56660     clicksToEdit: 2,
56661
56662     // private
56663     isEditor : true,
56664     // private
56665     trackMouseOver: false, // causes very odd FF errors
56666
56667     onCellDblClick : function(g, row, col){
56668         this.startEditing(row, col);
56669     },
56670
56671     onEditComplete : function(ed, value, startValue){
56672         this.editing = false;
56673         this.activeEditor = null;
56674         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56675         var r = ed.record;
56676         var field = this.colModel.getDataIndex(ed.col);
56677         var e = {
56678             grid: this,
56679             record: r,
56680             field: field,
56681             originalValue: startValue,
56682             value: value,
56683             row: ed.row,
56684             column: ed.col,
56685             cancel:false,
56686             editor: ed
56687         };
56688         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
56689         cell.show();
56690           
56691         if(String(value) !== String(startValue)){
56692             
56693             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56694                 r.set(field, e.value);
56695                 // if we are dealing with a combo box..
56696                 // then we also set the 'name' colum to be the displayField
56697                 if (ed.field.displayField && ed.field.name) {
56698                     r.set(ed.field.name, ed.field.el.dom.value);
56699                 }
56700                 
56701                 delete e.cancel; //?? why!!!
56702                 this.fireEvent("afteredit", e);
56703             }
56704         } else {
56705             this.fireEvent("afteredit", e); // always fire it!
56706         }
56707         this.view.focusCell(ed.row, ed.col);
56708     },
56709
56710     /**
56711      * Starts editing the specified for the specified row/column
56712      * @param {Number} rowIndex
56713      * @param {Number} colIndex
56714      */
56715     startEditing : function(row, col){
56716         this.stopEditing();
56717         if(this.colModel.isCellEditable(col, row)){
56718             this.view.ensureVisible(row, col, true);
56719           
56720             var r = this.dataSource.getAt(row);
56721             var field = this.colModel.getDataIndex(col);
56722             var cell = Roo.get(this.view.getCell(row,col));
56723             var e = {
56724                 grid: this,
56725                 record: r,
56726                 field: field,
56727                 value: r.data[field],
56728                 row: row,
56729                 column: col,
56730                 cancel:false 
56731             };
56732             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56733                 this.editing = true;
56734                 var ed = this.colModel.getCellEditor(col, row);
56735                 
56736                 if (!ed) {
56737                     return;
56738                 }
56739                 if(!ed.rendered){
56740                     ed.render(ed.parentEl || document.body);
56741                 }
56742                 ed.field.reset();
56743                
56744                 cell.hide();
56745                 
56746                 (function(){ // complex but required for focus issues in safari, ie and opera
56747                     ed.row = row;
56748                     ed.col = col;
56749                     ed.record = r;
56750                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56751                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56752                     this.activeEditor = ed;
56753                     var v = r.data[field];
56754                     ed.startEdit(this.view.getCell(row, col), v);
56755                     // combo's with 'displayField and name set
56756                     if (ed.field.displayField && ed.field.name) {
56757                         ed.field.el.dom.value = r.data[ed.field.name];
56758                     }
56759                     
56760                     
56761                 }).defer(50, this);
56762             }
56763         }
56764     },
56765         
56766     /**
56767      * Stops any active editing
56768      */
56769     stopEditing : function(){
56770         if(this.activeEditor){
56771             this.activeEditor.completeEdit();
56772         }
56773         this.activeEditor = null;
56774     },
56775         
56776          /**
56777      * Called to get grid's drag proxy text, by default returns this.ddText.
56778      * @return {String}
56779      */
56780     getDragDropText : function(){
56781         var count = this.selModel.getSelectedCell() ? 1 : 0;
56782         return String.format(this.ddText, count, count == 1 ? '' : 's');
56783     }
56784         
56785 });/*
56786  * Based on:
56787  * Ext JS Library 1.1.1
56788  * Copyright(c) 2006-2007, Ext JS, LLC.
56789  *
56790  * Originally Released Under LGPL - original licence link has changed is not relivant.
56791  *
56792  * Fork - LGPL
56793  * <script type="text/javascript">
56794  */
56795
56796 // private - not really -- you end up using it !
56797 // This is a support class used internally by the Grid components
56798
56799 /**
56800  * @class Roo.grid.GridEditor
56801  * @extends Roo.Editor
56802  * Class for creating and editable grid elements.
56803  * @param {Object} config any settings (must include field)
56804  */
56805 Roo.grid.GridEditor = function(field, config){
56806     if (!config && field.field) {
56807         config = field;
56808         field = Roo.factory(config.field, Roo.form);
56809     }
56810     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56811     field.monitorTab = false;
56812 };
56813
56814 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56815     
56816     /**
56817      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56818      */
56819     
56820     alignment: "tl-tl",
56821     autoSize: "width",
56822     hideEl : false,
56823     cls: "x-small-editor x-grid-editor",
56824     shim:false,
56825     shadow:"frame"
56826 });/*
56827  * Based on:
56828  * Ext JS Library 1.1.1
56829  * Copyright(c) 2006-2007, Ext JS, LLC.
56830  *
56831  * Originally Released Under LGPL - original licence link has changed is not relivant.
56832  *
56833  * Fork - LGPL
56834  * <script type="text/javascript">
56835  */
56836   
56837
56838   
56839 Roo.grid.PropertyRecord = Roo.data.Record.create([
56840     {name:'name',type:'string'},  'value'
56841 ]);
56842
56843
56844 Roo.grid.PropertyStore = function(grid, source){
56845     this.grid = grid;
56846     this.store = new Roo.data.Store({
56847         recordType : Roo.grid.PropertyRecord
56848     });
56849     this.store.on('update', this.onUpdate,  this);
56850     if(source){
56851         this.setSource(source);
56852     }
56853     Roo.grid.PropertyStore.superclass.constructor.call(this);
56854 };
56855
56856
56857
56858 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56859     setSource : function(o){
56860         this.source = o;
56861         this.store.removeAll();
56862         var data = [];
56863         for(var k in o){
56864             if(this.isEditableValue(o[k])){
56865                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
56866             }
56867         }
56868         this.store.loadRecords({records: data}, {}, true);
56869     },
56870
56871     onUpdate : function(ds, record, type){
56872         if(type == Roo.data.Record.EDIT){
56873             var v = record.data['value'];
56874             var oldValue = record.modified['value'];
56875             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
56876                 this.source[record.id] = v;
56877                 record.commit();
56878                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
56879             }else{
56880                 record.reject();
56881             }
56882         }
56883     },
56884
56885     getProperty : function(row){
56886        return this.store.getAt(row);
56887     },
56888
56889     isEditableValue: function(val){
56890         if(val && val instanceof Date){
56891             return true;
56892         }else if(typeof val == 'object' || typeof val == 'function'){
56893             return false;
56894         }
56895         return true;
56896     },
56897
56898     setValue : function(prop, value){
56899         this.source[prop] = value;
56900         this.store.getById(prop).set('value', value);
56901     },
56902
56903     getSource : function(){
56904         return this.source;
56905     }
56906 });
56907
56908 Roo.grid.PropertyColumnModel = function(grid, store){
56909     this.grid = grid;
56910     var g = Roo.grid;
56911     g.PropertyColumnModel.superclass.constructor.call(this, [
56912         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
56913         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
56914     ]);
56915     this.store = store;
56916     this.bselect = Roo.DomHelper.append(document.body, {
56917         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
56918             {tag: 'option', value: 'true', html: 'true'},
56919             {tag: 'option', value: 'false', html: 'false'}
56920         ]
56921     });
56922     Roo.id(this.bselect);
56923     var f = Roo.form;
56924     this.editors = {
56925         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
56926         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
56927         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
56928         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
56929         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
56930     };
56931     this.renderCellDelegate = this.renderCell.createDelegate(this);
56932     this.renderPropDelegate = this.renderProp.createDelegate(this);
56933 };
56934
56935 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
56936     
56937     
56938     nameText : 'Name',
56939     valueText : 'Value',
56940     
56941     dateFormat : 'm/j/Y',
56942     
56943     
56944     renderDate : function(dateVal){
56945         return dateVal.dateFormat(this.dateFormat);
56946     },
56947
56948     renderBool : function(bVal){
56949         return bVal ? 'true' : 'false';
56950     },
56951
56952     isCellEditable : function(colIndex, rowIndex){
56953         return colIndex == 1;
56954     },
56955
56956     getRenderer : function(col){
56957         return col == 1 ?
56958             this.renderCellDelegate : this.renderPropDelegate;
56959     },
56960
56961     renderProp : function(v){
56962         return this.getPropertyName(v);
56963     },
56964
56965     renderCell : function(val){
56966         var rv = val;
56967         if(val instanceof Date){
56968             rv = this.renderDate(val);
56969         }else if(typeof val == 'boolean'){
56970             rv = this.renderBool(val);
56971         }
56972         return Roo.util.Format.htmlEncode(rv);
56973     },
56974
56975     getPropertyName : function(name){
56976         var pn = this.grid.propertyNames;
56977         return pn && pn[name] ? pn[name] : name;
56978     },
56979
56980     getCellEditor : function(colIndex, rowIndex){
56981         var p = this.store.getProperty(rowIndex);
56982         var n = p.data['name'], val = p.data['value'];
56983         
56984         if(typeof(this.grid.customEditors[n]) == 'string'){
56985             return this.editors[this.grid.customEditors[n]];
56986         }
56987         if(typeof(this.grid.customEditors[n]) != 'undefined'){
56988             return this.grid.customEditors[n];
56989         }
56990         if(val instanceof Date){
56991             return this.editors['date'];
56992         }else if(typeof val == 'number'){
56993             return this.editors['number'];
56994         }else if(typeof val == 'boolean'){
56995             return this.editors['boolean'];
56996         }else{
56997             return this.editors['string'];
56998         }
56999     }
57000 });
57001
57002 /**
57003  * @class Roo.grid.PropertyGrid
57004  * @extends Roo.grid.EditorGrid
57005  * This class represents the  interface of a component based property grid control.
57006  * <br><br>Usage:<pre><code>
57007  var grid = new Roo.grid.PropertyGrid("my-container-id", {
57008       
57009  });
57010  // set any options
57011  grid.render();
57012  * </code></pre>
57013   
57014  * @constructor
57015  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57016  * The container MUST have some type of size defined for the grid to fill. The container will be
57017  * automatically set to position relative if it isn't already.
57018  * @param {Object} config A config object that sets properties on this grid.
57019  */
57020 Roo.grid.PropertyGrid = function(container, config){
57021     config = config || {};
57022     var store = new Roo.grid.PropertyStore(this);
57023     this.store = store;
57024     var cm = new Roo.grid.PropertyColumnModel(this, store);
57025     store.store.sort('name', 'ASC');
57026     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
57027         ds: store.store,
57028         cm: cm,
57029         enableColLock:false,
57030         enableColumnMove:false,
57031         stripeRows:false,
57032         trackMouseOver: false,
57033         clicksToEdit:1
57034     }, config));
57035     this.getGridEl().addClass('x-props-grid');
57036     this.lastEditRow = null;
57037     this.on('columnresize', this.onColumnResize, this);
57038     this.addEvents({
57039          /**
57040              * @event beforepropertychange
57041              * Fires before a property changes (return false to stop?)
57042              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57043              * @param {String} id Record Id
57044              * @param {String} newval New Value
57045          * @param {String} oldval Old Value
57046              */
57047         "beforepropertychange": true,
57048         /**
57049              * @event propertychange
57050              * Fires after a property changes
57051              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57052              * @param {String} id Record Id
57053              * @param {String} newval New Value
57054          * @param {String} oldval Old Value
57055              */
57056         "propertychange": true
57057     });
57058     this.customEditors = this.customEditors || {};
57059 };
57060 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
57061     
57062      /**
57063      * @cfg {Object} customEditors map of colnames=> custom editors.
57064      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
57065      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
57066      * false disables editing of the field.
57067          */
57068     
57069       /**
57070      * @cfg {Object} propertyNames map of property Names to their displayed value
57071          */
57072     
57073     render : function(){
57074         Roo.grid.PropertyGrid.superclass.render.call(this);
57075         this.autoSize.defer(100, this);
57076     },
57077
57078     autoSize : function(){
57079         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
57080         if(this.view){
57081             this.view.fitColumns();
57082         }
57083     },
57084
57085     onColumnResize : function(){
57086         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
57087         this.autoSize();
57088     },
57089     /**
57090      * Sets the data for the Grid
57091      * accepts a Key => Value object of all the elements avaiable.
57092      * @param {Object} data  to appear in grid.
57093      */
57094     setSource : function(source){
57095         this.store.setSource(source);
57096         //this.autoSize();
57097     },
57098     /**
57099      * Gets all the data from the grid.
57100      * @return {Object} data  data stored in grid
57101      */
57102     getSource : function(){
57103         return this.store.getSource();
57104     }
57105 });/*
57106   
57107  * Licence LGPL
57108  
57109  */
57110  
57111 /**
57112  * @class Roo.grid.Calendar
57113  * @extends Roo.util.Grid
57114  * This class extends the Grid to provide a calendar widget
57115  * <br><br>Usage:<pre><code>
57116  var grid = new Roo.grid.Calendar("my-container-id", {
57117      ds: myDataStore,
57118      cm: myColModel,
57119      selModel: mySelectionModel,
57120      autoSizeColumns: true,
57121      monitorWindowResize: false,
57122      trackMouseOver: true
57123      eventstore : real data store..
57124  });
57125  // set any options
57126  grid.render();
57127   
57128   * @constructor
57129  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57130  * The container MUST have some type of size defined for the grid to fill. The container will be
57131  * automatically set to position relative if it isn't already.
57132  * @param {Object} config A config object that sets properties on this grid.
57133  */
57134 Roo.grid.Calendar = function(container, config){
57135         // initialize the container
57136         this.container = Roo.get(container);
57137         this.container.update("");
57138         this.container.setStyle("overflow", "hidden");
57139     this.container.addClass('x-grid-container');
57140
57141     this.id = this.container.id;
57142
57143     Roo.apply(this, config);
57144     // check and correct shorthanded configs
57145     
57146     var rows = [];
57147     var d =1;
57148     for (var r = 0;r < 6;r++) {
57149         
57150         rows[r]=[];
57151         for (var c =0;c < 7;c++) {
57152             rows[r][c]= '';
57153         }
57154     }
57155     if (this.eventStore) {
57156         this.eventStore= Roo.factory(this.eventStore, Roo.data);
57157         this.eventStore.on('load',this.onLoad, this);
57158         this.eventStore.on('beforeload',this.clearEvents, this);
57159          
57160     }
57161     
57162     this.dataSource = new Roo.data.Store({
57163             proxy: new Roo.data.MemoryProxy(rows),
57164             reader: new Roo.data.ArrayReader({}, [
57165                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
57166     });
57167
57168     this.dataSource.load();
57169     this.ds = this.dataSource;
57170     this.ds.xmodule = this.xmodule || false;
57171     
57172     
57173     var cellRender = function(v,x,r)
57174     {
57175         return String.format(
57176             '<div class="fc-day  fc-widget-content"><div>' +
57177                 '<div class="fc-event-container"></div>' +
57178                 '<div class="fc-day-number">{0}</div>'+
57179                 
57180                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
57181             '</div></div>', v);
57182     
57183     }
57184     
57185     
57186     this.colModel = new Roo.grid.ColumnModel( [
57187         {
57188             xtype: 'ColumnModel',
57189             xns: Roo.grid,
57190             dataIndex : 'weekday0',
57191             header : 'Sunday',
57192             renderer : cellRender
57193         },
57194         {
57195             xtype: 'ColumnModel',
57196             xns: Roo.grid,
57197             dataIndex : 'weekday1',
57198             header : 'Monday',
57199             renderer : cellRender
57200         },
57201         {
57202             xtype: 'ColumnModel',
57203             xns: Roo.grid,
57204             dataIndex : 'weekday2',
57205             header : 'Tuesday',
57206             renderer : cellRender
57207         },
57208         {
57209             xtype: 'ColumnModel',
57210             xns: Roo.grid,
57211             dataIndex : 'weekday3',
57212             header : 'Wednesday',
57213             renderer : cellRender
57214         },
57215         {
57216             xtype: 'ColumnModel',
57217             xns: Roo.grid,
57218             dataIndex : 'weekday4',
57219             header : 'Thursday',
57220             renderer : cellRender
57221         },
57222         {
57223             xtype: 'ColumnModel',
57224             xns: Roo.grid,
57225             dataIndex : 'weekday5',
57226             header : 'Friday',
57227             renderer : cellRender
57228         },
57229         {
57230             xtype: 'ColumnModel',
57231             xns: Roo.grid,
57232             dataIndex : 'weekday6',
57233             header : 'Saturday',
57234             renderer : cellRender
57235         }
57236     ]);
57237     this.cm = this.colModel;
57238     this.cm.xmodule = this.xmodule || false;
57239  
57240         
57241           
57242     //this.selModel = new Roo.grid.CellSelectionModel();
57243     //this.sm = this.selModel;
57244     //this.selModel.init(this);
57245     
57246     
57247     if(this.width){
57248         this.container.setWidth(this.width);
57249     }
57250
57251     if(this.height){
57252         this.container.setHeight(this.height);
57253     }
57254     /** @private */
57255         this.addEvents({
57256         // raw events
57257         /**
57258          * @event click
57259          * The raw click event for the entire grid.
57260          * @param {Roo.EventObject} e
57261          */
57262         "click" : true,
57263         /**
57264          * @event dblclick
57265          * The raw dblclick event for the entire grid.
57266          * @param {Roo.EventObject} e
57267          */
57268         "dblclick" : true,
57269         /**
57270          * @event contextmenu
57271          * The raw contextmenu event for the entire grid.
57272          * @param {Roo.EventObject} e
57273          */
57274         "contextmenu" : true,
57275         /**
57276          * @event mousedown
57277          * The raw mousedown event for the entire grid.
57278          * @param {Roo.EventObject} e
57279          */
57280         "mousedown" : true,
57281         /**
57282          * @event mouseup
57283          * The raw mouseup event for the entire grid.
57284          * @param {Roo.EventObject} e
57285          */
57286         "mouseup" : true,
57287         /**
57288          * @event mouseover
57289          * The raw mouseover event for the entire grid.
57290          * @param {Roo.EventObject} e
57291          */
57292         "mouseover" : true,
57293         /**
57294          * @event mouseout
57295          * The raw mouseout event for the entire grid.
57296          * @param {Roo.EventObject} e
57297          */
57298         "mouseout" : true,
57299         /**
57300          * @event keypress
57301          * The raw keypress event for the entire grid.
57302          * @param {Roo.EventObject} e
57303          */
57304         "keypress" : true,
57305         /**
57306          * @event keydown
57307          * The raw keydown event for the entire grid.
57308          * @param {Roo.EventObject} e
57309          */
57310         "keydown" : true,
57311
57312         // custom events
57313
57314         /**
57315          * @event cellclick
57316          * Fires when a cell is clicked
57317          * @param {Grid} this
57318          * @param {Number} rowIndex
57319          * @param {Number} columnIndex
57320          * @param {Roo.EventObject} e
57321          */
57322         "cellclick" : true,
57323         /**
57324          * @event celldblclick
57325          * Fires when a cell is double clicked
57326          * @param {Grid} this
57327          * @param {Number} rowIndex
57328          * @param {Number} columnIndex
57329          * @param {Roo.EventObject} e
57330          */
57331         "celldblclick" : true,
57332         /**
57333          * @event rowclick
57334          * Fires when a row is clicked
57335          * @param {Grid} this
57336          * @param {Number} rowIndex
57337          * @param {Roo.EventObject} e
57338          */
57339         "rowclick" : true,
57340         /**
57341          * @event rowdblclick
57342          * Fires when a row is double clicked
57343          * @param {Grid} this
57344          * @param {Number} rowIndex
57345          * @param {Roo.EventObject} e
57346          */
57347         "rowdblclick" : true,
57348         /**
57349          * @event headerclick
57350          * Fires when a header is clicked
57351          * @param {Grid} this
57352          * @param {Number} columnIndex
57353          * @param {Roo.EventObject} e
57354          */
57355         "headerclick" : true,
57356         /**
57357          * @event headerdblclick
57358          * Fires when a header cell is double clicked
57359          * @param {Grid} this
57360          * @param {Number} columnIndex
57361          * @param {Roo.EventObject} e
57362          */
57363         "headerdblclick" : true,
57364         /**
57365          * @event rowcontextmenu
57366          * Fires when a row is right clicked
57367          * @param {Grid} this
57368          * @param {Number} rowIndex
57369          * @param {Roo.EventObject} e
57370          */
57371         "rowcontextmenu" : true,
57372         /**
57373          * @event cellcontextmenu
57374          * Fires when a cell is right clicked
57375          * @param {Grid} this
57376          * @param {Number} rowIndex
57377          * @param {Number} cellIndex
57378          * @param {Roo.EventObject} e
57379          */
57380          "cellcontextmenu" : true,
57381         /**
57382          * @event headercontextmenu
57383          * Fires when a header is right clicked
57384          * @param {Grid} this
57385          * @param {Number} columnIndex
57386          * @param {Roo.EventObject} e
57387          */
57388         "headercontextmenu" : true,
57389         /**
57390          * @event bodyscroll
57391          * Fires when the body element is scrolled
57392          * @param {Number} scrollLeft
57393          * @param {Number} scrollTop
57394          */
57395         "bodyscroll" : true,
57396         /**
57397          * @event columnresize
57398          * Fires when the user resizes a column
57399          * @param {Number} columnIndex
57400          * @param {Number} newSize
57401          */
57402         "columnresize" : true,
57403         /**
57404          * @event columnmove
57405          * Fires when the user moves a column
57406          * @param {Number} oldIndex
57407          * @param {Number} newIndex
57408          */
57409         "columnmove" : true,
57410         /**
57411          * @event startdrag
57412          * Fires when row(s) start being dragged
57413          * @param {Grid} this
57414          * @param {Roo.GridDD} dd The drag drop object
57415          * @param {event} e The raw browser event
57416          */
57417         "startdrag" : true,
57418         /**
57419          * @event enddrag
57420          * Fires when a drag operation is complete
57421          * @param {Grid} this
57422          * @param {Roo.GridDD} dd The drag drop object
57423          * @param {event} e The raw browser event
57424          */
57425         "enddrag" : true,
57426         /**
57427          * @event dragdrop
57428          * Fires when dragged row(s) are dropped on a valid DD target
57429          * @param {Grid} this
57430          * @param {Roo.GridDD} dd The drag drop object
57431          * @param {String} targetId The target drag drop object
57432          * @param {event} e The raw browser event
57433          */
57434         "dragdrop" : true,
57435         /**
57436          * @event dragover
57437          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57438          * @param {Grid} this
57439          * @param {Roo.GridDD} dd The drag drop object
57440          * @param {String} targetId The target drag drop object
57441          * @param {event} e The raw browser event
57442          */
57443         "dragover" : true,
57444         /**
57445          * @event dragenter
57446          *  Fires when the dragged row(s) first cross another DD target while being dragged
57447          * @param {Grid} this
57448          * @param {Roo.GridDD} dd The drag drop object
57449          * @param {String} targetId The target drag drop object
57450          * @param {event} e The raw browser event
57451          */
57452         "dragenter" : true,
57453         /**
57454          * @event dragout
57455          * Fires when the dragged row(s) leave another DD target while being dragged
57456          * @param {Grid} this
57457          * @param {Roo.GridDD} dd The drag drop object
57458          * @param {String} targetId The target drag drop object
57459          * @param {event} e The raw browser event
57460          */
57461         "dragout" : true,
57462         /**
57463          * @event rowclass
57464          * Fires when a row is rendered, so you can change add a style to it.
57465          * @param {GridView} gridview   The grid view
57466          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57467          */
57468         'rowclass' : true,
57469
57470         /**
57471          * @event render
57472          * Fires when the grid is rendered
57473          * @param {Grid} grid
57474          */
57475         'render' : true,
57476             /**
57477              * @event select
57478              * Fires when a date is selected
57479              * @param {DatePicker} this
57480              * @param {Date} date The selected date
57481              */
57482         'select': true,
57483         /**
57484              * @event monthchange
57485              * Fires when the displayed month changes 
57486              * @param {DatePicker} this
57487              * @param {Date} date The selected month
57488              */
57489         'monthchange': true,
57490         /**
57491              * @event evententer
57492              * Fires when mouse over an event
57493              * @param {Calendar} this
57494              * @param {event} Event
57495              */
57496         'evententer': true,
57497         /**
57498              * @event eventleave
57499              * Fires when the mouse leaves an
57500              * @param {Calendar} this
57501              * @param {event}
57502              */
57503         'eventleave': true,
57504         /**
57505              * @event eventclick
57506              * Fires when the mouse click an
57507              * @param {Calendar} this
57508              * @param {event}
57509              */
57510         'eventclick': true,
57511         /**
57512              * @event eventrender
57513              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
57514              * @param {Calendar} this
57515              * @param {data} data to be modified
57516              */
57517         'eventrender': true
57518         
57519     });
57520
57521     Roo.grid.Grid.superclass.constructor.call(this);
57522     this.on('render', function() {
57523         this.view.el.addClass('x-grid-cal'); 
57524         
57525         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
57526
57527     },this);
57528     
57529     if (!Roo.grid.Calendar.style) {
57530         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
57531             
57532             
57533             '.x-grid-cal .x-grid-col' :  {
57534                 height: 'auto !important',
57535                 'vertical-align': 'top'
57536             },
57537             '.x-grid-cal  .fc-event-hori' : {
57538                 height: '14px'
57539             }
57540              
57541             
57542         }, Roo.id());
57543     }
57544
57545     
57546     
57547 };
57548 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
57549     /**
57550      * @cfg {Store} eventStore The store that loads events.
57551      */
57552     eventStore : 25,
57553
57554      
57555     activeDate : false,
57556     startDay : 0,
57557     autoWidth : true,
57558     monitorWindowResize : false,
57559
57560     
57561     resizeColumns : function() {
57562         var col = (this.view.el.getWidth() / 7) - 3;
57563         // loop through cols, and setWidth
57564         for(var i =0 ; i < 7 ; i++){
57565             this.cm.setColumnWidth(i, col);
57566         }
57567     },
57568      setDate :function(date) {
57569         
57570         Roo.log('setDate?');
57571         
57572         this.resizeColumns();
57573         var vd = this.activeDate;
57574         this.activeDate = date;
57575 //        if(vd && this.el){
57576 //            var t = date.getTime();
57577 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
57578 //                Roo.log('using add remove');
57579 //                
57580 //                this.fireEvent('monthchange', this, date);
57581 //                
57582 //                this.cells.removeClass("fc-state-highlight");
57583 //                this.cells.each(function(c){
57584 //                   if(c.dateValue == t){
57585 //                       c.addClass("fc-state-highlight");
57586 //                       setTimeout(function(){
57587 //                            try{c.dom.firstChild.focus();}catch(e){}
57588 //                       }, 50);
57589 //                       return false;
57590 //                   }
57591 //                   return true;
57592 //                });
57593 //                return;
57594 //            }
57595 //        }
57596         
57597         var days = date.getDaysInMonth();
57598         
57599         var firstOfMonth = date.getFirstDateOfMonth();
57600         var startingPos = firstOfMonth.getDay()-this.startDay;
57601         
57602         if(startingPos < this.startDay){
57603             startingPos += 7;
57604         }
57605         
57606         var pm = date.add(Date.MONTH, -1);
57607         var prevStart = pm.getDaysInMonth()-startingPos;
57608 //        
57609         
57610         
57611         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57612         
57613         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57614         //this.cells.addClassOnOver('fc-state-hover');
57615         
57616         var cells = this.cells.elements;
57617         var textEls = this.textNodes;
57618         
57619         //Roo.each(cells, function(cell){
57620         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57621         //});
57622         
57623         days += startingPos;
57624
57625         // convert everything to numbers so it's fast
57626         var day = 86400000;
57627         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57628         //Roo.log(d);
57629         //Roo.log(pm);
57630         //Roo.log(prevStart);
57631         
57632         var today = new Date().clearTime().getTime();
57633         var sel = date.clearTime().getTime();
57634         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57635         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57636         var ddMatch = this.disabledDatesRE;
57637         var ddText = this.disabledDatesText;
57638         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57639         var ddaysText = this.disabledDaysText;
57640         var format = this.format;
57641         
57642         var setCellClass = function(cal, cell){
57643             
57644             //Roo.log('set Cell Class');
57645             cell.title = "";
57646             var t = d.getTime();
57647             
57648             //Roo.log(d);
57649             
57650             
57651             cell.dateValue = t;
57652             if(t == today){
57653                 cell.className += " fc-today";
57654                 cell.className += " fc-state-highlight";
57655                 cell.title = cal.todayText;
57656             }
57657             if(t == sel){
57658                 // disable highlight in other month..
57659                 cell.className += " fc-state-highlight";
57660                 
57661             }
57662             // disabling
57663             if(t < min) {
57664                 //cell.className = " fc-state-disabled";
57665                 cell.title = cal.minText;
57666                 return;
57667             }
57668             if(t > max) {
57669                 //cell.className = " fc-state-disabled";
57670                 cell.title = cal.maxText;
57671                 return;
57672             }
57673             if(ddays){
57674                 if(ddays.indexOf(d.getDay()) != -1){
57675                     // cell.title = ddaysText;
57676                    // cell.className = " fc-state-disabled";
57677                 }
57678             }
57679             if(ddMatch && format){
57680                 var fvalue = d.dateFormat(format);
57681                 if(ddMatch.test(fvalue)){
57682                     cell.title = ddText.replace("%0", fvalue);
57683                    cell.className = " fc-state-disabled";
57684                 }
57685             }
57686             
57687             if (!cell.initialClassName) {
57688                 cell.initialClassName = cell.dom.className;
57689             }
57690             
57691             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57692         };
57693
57694         var i = 0;
57695         
57696         for(; i < startingPos; i++) {
57697             cells[i].dayName =  (++prevStart);
57698             Roo.log(textEls[i]);
57699             d.setDate(d.getDate()+1);
57700             
57701             //cells[i].className = "fc-past fc-other-month";
57702             setCellClass(this, cells[i]);
57703         }
57704         
57705         var intDay = 0;
57706         
57707         for(; i < days; i++){
57708             intDay = i - startingPos + 1;
57709             cells[i].dayName =  (intDay);
57710             d.setDate(d.getDate()+1);
57711             
57712             cells[i].className = ''; // "x-date-active";
57713             setCellClass(this, cells[i]);
57714         }
57715         var extraDays = 0;
57716         
57717         for(; i < 42; i++) {
57718             //textEls[i].innerHTML = (++extraDays);
57719             
57720             d.setDate(d.getDate()+1);
57721             cells[i].dayName = (++extraDays);
57722             cells[i].className = "fc-future fc-other-month";
57723             setCellClass(this, cells[i]);
57724         }
57725         
57726         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57727         
57728         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57729         
57730         // this will cause all the cells to mis
57731         var rows= [];
57732         var i =0;
57733         for (var r = 0;r < 6;r++) {
57734             for (var c =0;c < 7;c++) {
57735                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57736             }    
57737         }
57738         
57739         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57740         for(i=0;i<cells.length;i++) {
57741             
57742             this.cells.elements[i].dayName = cells[i].dayName ;
57743             this.cells.elements[i].className = cells[i].className;
57744             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57745             this.cells.elements[i].title = cells[i].title ;
57746             this.cells.elements[i].dateValue = cells[i].dateValue ;
57747         }
57748         
57749         
57750         
57751         
57752         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57753         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57754         
57755         ////if(totalRows != 6){
57756             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57757            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57758        // }
57759         
57760         this.fireEvent('monthchange', this, date);
57761         
57762         
57763     },
57764  /**
57765      * Returns the grid's SelectionModel.
57766      * @return {SelectionModel}
57767      */
57768     getSelectionModel : function(){
57769         if(!this.selModel){
57770             this.selModel = new Roo.grid.CellSelectionModel();
57771         }
57772         return this.selModel;
57773     },
57774
57775     load: function() {
57776         this.eventStore.load()
57777         
57778         
57779         
57780     },
57781     
57782     findCell : function(dt) {
57783         dt = dt.clearTime().getTime();
57784         var ret = false;
57785         this.cells.each(function(c){
57786             //Roo.log("check " +c.dateValue + '?=' + dt);
57787             if(c.dateValue == dt){
57788                 ret = c;
57789                 return false;
57790             }
57791             return true;
57792         });
57793         
57794         return ret;
57795     },
57796     
57797     findCells : function(rec) {
57798         var s = rec.data.start_dt.clone().clearTime().getTime();
57799        // Roo.log(s);
57800         var e= rec.data.end_dt.clone().clearTime().getTime();
57801        // Roo.log(e);
57802         var ret = [];
57803         this.cells.each(function(c){
57804              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57805             
57806             if(c.dateValue > e){
57807                 return ;
57808             }
57809             if(c.dateValue < s){
57810                 return ;
57811             }
57812             ret.push(c);
57813         });
57814         
57815         return ret;    
57816     },
57817     
57818     findBestRow: function(cells)
57819     {
57820         var ret = 0;
57821         
57822         for (var i =0 ; i < cells.length;i++) {
57823             ret  = Math.max(cells[i].rows || 0,ret);
57824         }
57825         return ret;
57826         
57827     },
57828     
57829     
57830     addItem : function(rec)
57831     {
57832         // look for vertical location slot in
57833         var cells = this.findCells(rec);
57834         
57835         rec.row = this.findBestRow(cells);
57836         
57837         // work out the location.
57838         
57839         var crow = false;
57840         var rows = [];
57841         for(var i =0; i < cells.length; i++) {
57842             if (!crow) {
57843                 crow = {
57844                     start : cells[i],
57845                     end :  cells[i]
57846                 };
57847                 continue;
57848             }
57849             if (crow.start.getY() == cells[i].getY()) {
57850                 // on same row.
57851                 crow.end = cells[i];
57852                 continue;
57853             }
57854             // different row.
57855             rows.push(crow);
57856             crow = {
57857                 start: cells[i],
57858                 end : cells[i]
57859             };
57860             
57861         }
57862         
57863         rows.push(crow);
57864         rec.els = [];
57865         rec.rows = rows;
57866         rec.cells = cells;
57867         for (var i = 0; i < cells.length;i++) {
57868             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
57869             
57870         }
57871         
57872         
57873     },
57874     
57875     clearEvents: function() {
57876         
57877         if (!this.eventStore.getCount()) {
57878             return;
57879         }
57880         // reset number of rows in cells.
57881         Roo.each(this.cells.elements, function(c){
57882             c.rows = 0;
57883         });
57884         
57885         this.eventStore.each(function(e) {
57886             this.clearEvent(e);
57887         },this);
57888         
57889     },
57890     
57891     clearEvent : function(ev)
57892     {
57893         if (ev.els) {
57894             Roo.each(ev.els, function(el) {
57895                 el.un('mouseenter' ,this.onEventEnter, this);
57896                 el.un('mouseleave' ,this.onEventLeave, this);
57897                 el.remove();
57898             },this);
57899             ev.els = [];
57900         }
57901     },
57902     
57903     
57904     renderEvent : function(ev,ctr) {
57905         if (!ctr) {
57906              ctr = this.view.el.select('.fc-event-container',true).first();
57907         }
57908         
57909          
57910         this.clearEvent(ev);
57911             //code
57912        
57913         
57914         
57915         ev.els = [];
57916         var cells = ev.cells;
57917         var rows = ev.rows;
57918         this.fireEvent('eventrender', this, ev);
57919         
57920         for(var i =0; i < rows.length; i++) {
57921             
57922             cls = '';
57923             if (i == 0) {
57924                 cls += ' fc-event-start';
57925             }
57926             if ((i+1) == rows.length) {
57927                 cls += ' fc-event-end';
57928             }
57929             
57930             //Roo.log(ev.data);
57931             // how many rows should it span..
57932             var cg = this.eventTmpl.append(ctr,Roo.apply({
57933                 fccls : cls
57934                 
57935             }, ev.data) , true);
57936             
57937             
57938             cg.on('mouseenter' ,this.onEventEnter, this, ev);
57939             cg.on('mouseleave' ,this.onEventLeave, this, ev);
57940             cg.on('click', this.onEventClick, this, ev);
57941             
57942             ev.els.push(cg);
57943             
57944             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
57945             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
57946             //Roo.log(cg);
57947              
57948             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
57949             cg.setWidth(ebox.right - sbox.x -2);
57950         }
57951     },
57952     
57953     renderEvents: function()
57954     {   
57955         // first make sure there is enough space..
57956         
57957         if (!this.eventTmpl) {
57958             this.eventTmpl = new Roo.Template(
57959                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57960                     '<div class="fc-event-inner">' +
57961                         '<span class="fc-event-time">{time}</span>' +
57962                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57963                     '</div>' +
57964                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57965                 '</div>'
57966             );
57967                 
57968         }
57969                
57970         
57971         
57972         this.cells.each(function(c) {
57973             //Roo.log(c.select('.fc-day-content div',true).first());
57974             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
57975         });
57976         
57977         var ctr = this.view.el.select('.fc-event-container',true).first();
57978         
57979         var cls;
57980         this.eventStore.each(function(ev){
57981             
57982             this.renderEvent(ev);
57983              
57984              
57985         }, this);
57986         this.view.layout();
57987         
57988     },
57989     
57990     onEventEnter: function (e, el,event,d) {
57991         this.fireEvent('evententer', this, el, event);
57992     },
57993     
57994     onEventLeave: function (e, el,event,d) {
57995         this.fireEvent('eventleave', this, el, event);
57996     },
57997     
57998     onEventClick: function (e, el,event,d) {
57999         this.fireEvent('eventclick', this, el, event);
58000     },
58001     
58002     onMonthChange: function () {
58003         this.store.load();
58004     },
58005     
58006     onLoad: function () {
58007         
58008         //Roo.log('calendar onload');
58009 //         
58010         if(this.eventStore.getCount() > 0){
58011             
58012            
58013             
58014             this.eventStore.each(function(d){
58015                 
58016                 
58017                 // FIXME..
58018                 var add =   d.data;
58019                 if (typeof(add.end_dt) == 'undefined')  {
58020                     Roo.log("Missing End time in calendar data: ");
58021                     Roo.log(d);
58022                     return;
58023                 }
58024                 if (typeof(add.start_dt) == 'undefined')  {
58025                     Roo.log("Missing Start time in calendar data: ");
58026                     Roo.log(d);
58027                     return;
58028                 }
58029                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
58030                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
58031                 add.id = add.id || d.id;
58032                 add.title = add.title || '??';
58033                 
58034                 this.addItem(d);
58035                 
58036              
58037             },this);
58038         }
58039         
58040         this.renderEvents();
58041     }
58042     
58043
58044 });
58045 /*
58046  grid : {
58047                 xtype: 'Grid',
58048                 xns: Roo.grid,
58049                 listeners : {
58050                     render : function ()
58051                     {
58052                         _this.grid = this;
58053                         
58054                         if (!this.view.el.hasClass('course-timesheet')) {
58055                             this.view.el.addClass('course-timesheet');
58056                         }
58057                         if (this.tsStyle) {
58058                             this.ds.load({});
58059                             return; 
58060                         }
58061                         Roo.log('width');
58062                         Roo.log(_this.grid.view.el.getWidth());
58063                         
58064                         
58065                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
58066                             '.course-timesheet .x-grid-row' : {
58067                                 height: '80px'
58068                             },
58069                             '.x-grid-row td' : {
58070                                 'vertical-align' : 0
58071                             },
58072                             '.course-edit-link' : {
58073                                 'color' : 'blue',
58074                                 'text-overflow' : 'ellipsis',
58075                                 'overflow' : 'hidden',
58076                                 'white-space' : 'nowrap',
58077                                 'cursor' : 'pointer'
58078                             },
58079                             '.sub-link' : {
58080                                 'color' : 'green'
58081                             },
58082                             '.de-act-sup-link' : {
58083                                 'color' : 'purple',
58084                                 'text-decoration' : 'line-through'
58085                             },
58086                             '.de-act-link' : {
58087                                 'color' : 'red',
58088                                 'text-decoration' : 'line-through'
58089                             },
58090                             '.course-timesheet .course-highlight' : {
58091                                 'border-top-style': 'dashed !important',
58092                                 'border-bottom-bottom': 'dashed !important'
58093                             },
58094                             '.course-timesheet .course-item' : {
58095                                 'font-family'   : 'tahoma, arial, helvetica',
58096                                 'font-size'     : '11px',
58097                                 'overflow'      : 'hidden',
58098                                 'padding-left'  : '10px',
58099                                 'padding-right' : '10px',
58100                                 'padding-top' : '10px' 
58101                             }
58102                             
58103                         }, Roo.id());
58104                                 this.ds.load({});
58105                     }
58106                 },
58107                 autoWidth : true,
58108                 monitorWindowResize : false,
58109                 cellrenderer : function(v,x,r)
58110                 {
58111                     return v;
58112                 },
58113                 sm : {
58114                     xtype: 'CellSelectionModel',
58115                     xns: Roo.grid
58116                 },
58117                 dataSource : {
58118                     xtype: 'Store',
58119                     xns: Roo.data,
58120                     listeners : {
58121                         beforeload : function (_self, options)
58122                         {
58123                             options.params = options.params || {};
58124                             options.params._month = _this.monthField.getValue();
58125                             options.params.limit = 9999;
58126                             options.params['sort'] = 'when_dt';    
58127                             options.params['dir'] = 'ASC';    
58128                             this.proxy.loadResponse = this.loadResponse;
58129                             Roo.log("load?");
58130                             //this.addColumns();
58131                         },
58132                         load : function (_self, records, options)
58133                         {
58134                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
58135                                 // if you click on the translation.. you can edit it...
58136                                 var el = Roo.get(this);
58137                                 var id = el.dom.getAttribute('data-id');
58138                                 var d = el.dom.getAttribute('data-date');
58139                                 var t = el.dom.getAttribute('data-time');
58140                                 //var id = this.child('span').dom.textContent;
58141                                 
58142                                 //Roo.log(this);
58143                                 Pman.Dialog.CourseCalendar.show({
58144                                     id : id,
58145                                     when_d : d,
58146                                     when_t : t,
58147                                     productitem_active : id ? 1 : 0
58148                                 }, function() {
58149                                     _this.grid.ds.load({});
58150                                 });
58151                            
58152                            });
58153                            
58154                            _this.panel.fireEvent('resize', [ '', '' ]);
58155                         }
58156                     },
58157                     loadResponse : function(o, success, response){
58158                             // this is overridden on before load..
58159                             
58160                             Roo.log("our code?");       
58161                             //Roo.log(success);
58162                             //Roo.log(response)
58163                             delete this.activeRequest;
58164                             if(!success){
58165                                 this.fireEvent("loadexception", this, o, response);
58166                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58167                                 return;
58168                             }
58169                             var result;
58170                             try {
58171                                 result = o.reader.read(response);
58172                             }catch(e){
58173                                 Roo.log("load exception?");
58174                                 this.fireEvent("loadexception", this, o, response, e);
58175                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58176                                 return;
58177                             }
58178                             Roo.log("ready...");        
58179                             // loop through result.records;
58180                             // and set this.tdate[date] = [] << array of records..
58181                             _this.tdata  = {};
58182                             Roo.each(result.records, function(r){
58183                                 //Roo.log(r.data);
58184                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
58185                                     _this.tdata[r.data.when_dt.format('j')] = [];
58186                                 }
58187                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
58188                             });
58189                             
58190                             //Roo.log(_this.tdata);
58191                             
58192                             result.records = [];
58193                             result.totalRecords = 6;
58194                     
58195                             // let's generate some duumy records for the rows.
58196                             //var st = _this.dateField.getValue();
58197                             
58198                             // work out monday..
58199                             //st = st.add(Date.DAY, -1 * st.format('w'));
58200                             
58201                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58202                             
58203                             var firstOfMonth = date.getFirstDayOfMonth();
58204                             var days = date.getDaysInMonth();
58205                             var d = 1;
58206                             var firstAdded = false;
58207                             for (var i = 0; i < result.totalRecords ; i++) {
58208                                 //var d= st.add(Date.DAY, i);
58209                                 var row = {};
58210                                 var added = 0;
58211                                 for(var w = 0 ; w < 7 ; w++){
58212                                     if(!firstAdded && firstOfMonth != w){
58213                                         continue;
58214                                     }
58215                                     if(d > days){
58216                                         continue;
58217                                     }
58218                                     firstAdded = true;
58219                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
58220                                     row['weekday'+w] = String.format(
58221                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
58222                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
58223                                                     d,
58224                                                     date.format('Y-m-')+dd
58225                                                 );
58226                                     added++;
58227                                     if(typeof(_this.tdata[d]) != 'undefined'){
58228                                         Roo.each(_this.tdata[d], function(r){
58229                                             var is_sub = '';
58230                                             var deactive = '';
58231                                             var id = r.id;
58232                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
58233                                             if(r.parent_id*1>0){
58234                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
58235                                                 id = r.parent_id;
58236                                             }
58237                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
58238                                                 deactive = 'de-act-link';
58239                                             }
58240                                             
58241                                             row['weekday'+w] += String.format(
58242                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
58243                                                     id, //0
58244                                                     r.product_id_name, //1
58245                                                     r.when_dt.format('h:ia'), //2
58246                                                     is_sub, //3
58247                                                     deactive, //4
58248                                                     desc // 5
58249                                             );
58250                                         });
58251                                     }
58252                                     d++;
58253                                 }
58254                                 
58255                                 // only do this if something added..
58256                                 if(added > 0){ 
58257                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
58258                                 }
58259                                 
58260                                 
58261                                 // push it twice. (second one with an hour..
58262                                 
58263                             }
58264                             //Roo.log(result);
58265                             this.fireEvent("load", this, o, o.request.arg);
58266                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
58267                         },
58268                     sortInfo : {field: 'when_dt', direction : 'ASC' },
58269                     proxy : {
58270                         xtype: 'HttpProxy',
58271                         xns: Roo.data,
58272                         method : 'GET',
58273                         url : baseURL + '/Roo/Shop_course.php'
58274                     },
58275                     reader : {
58276                         xtype: 'JsonReader',
58277                         xns: Roo.data,
58278                         id : 'id',
58279                         fields : [
58280                             {
58281                                 'name': 'id',
58282                                 'type': 'int'
58283                             },
58284                             {
58285                                 'name': 'when_dt',
58286                                 'type': 'string'
58287                             },
58288                             {
58289                                 'name': 'end_dt',
58290                                 'type': 'string'
58291                             },
58292                             {
58293                                 'name': 'parent_id',
58294                                 'type': 'int'
58295                             },
58296                             {
58297                                 'name': 'product_id',
58298                                 'type': 'int'
58299                             },
58300                             {
58301                                 'name': 'productitem_id',
58302                                 'type': 'int'
58303                             },
58304                             {
58305                                 'name': 'guid',
58306                                 'type': 'int'
58307                             }
58308                         ]
58309                     }
58310                 },
58311                 toolbar : {
58312                     xtype: 'Toolbar',
58313                     xns: Roo,
58314                     items : [
58315                         {
58316                             xtype: 'Button',
58317                             xns: Roo.Toolbar,
58318                             listeners : {
58319                                 click : function (_self, e)
58320                                 {
58321                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58322                                     sd.setMonth(sd.getMonth()-1);
58323                                     _this.monthField.setValue(sd.format('Y-m-d'));
58324                                     _this.grid.ds.load({});
58325                                 }
58326                             },
58327                             text : "Back"
58328                         },
58329                         {
58330                             xtype: 'Separator',
58331                             xns: Roo.Toolbar
58332                         },
58333                         {
58334                             xtype: 'MonthField',
58335                             xns: Roo.form,
58336                             listeners : {
58337                                 render : function (_self)
58338                                 {
58339                                     _this.monthField = _self;
58340                                    // _this.monthField.set  today
58341                                 },
58342                                 select : function (combo, date)
58343                                 {
58344                                     _this.grid.ds.load({});
58345                                 }
58346                             },
58347                             value : (function() { return new Date(); })()
58348                         },
58349                         {
58350                             xtype: 'Separator',
58351                             xns: Roo.Toolbar
58352                         },
58353                         {
58354                             xtype: 'TextItem',
58355                             xns: Roo.Toolbar,
58356                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
58357                         },
58358                         {
58359                             xtype: 'Fill',
58360                             xns: Roo.Toolbar
58361                         },
58362                         {
58363                             xtype: 'Button',
58364                             xns: Roo.Toolbar,
58365                             listeners : {
58366                                 click : function (_self, e)
58367                                 {
58368                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58369                                     sd.setMonth(sd.getMonth()+1);
58370                                     _this.monthField.setValue(sd.format('Y-m-d'));
58371                                     _this.grid.ds.load({});
58372                                 }
58373                             },
58374                             text : "Next"
58375                         }
58376                     ]
58377                 },
58378                  
58379             }
58380         };
58381         
58382         *//*
58383  * Based on:
58384  * Ext JS Library 1.1.1
58385  * Copyright(c) 2006-2007, Ext JS, LLC.
58386  *
58387  * Originally Released Under LGPL - original licence link has changed is not relivant.
58388  *
58389  * Fork - LGPL
58390  * <script type="text/javascript">
58391  */
58392  
58393 /**
58394  * @class Roo.LoadMask
58395  * A simple utility class for generically masking elements while loading data.  If the element being masked has
58396  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
58397  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
58398  * element's UpdateManager load indicator and will be destroyed after the initial load.
58399  * @constructor
58400  * Create a new LoadMask
58401  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
58402  * @param {Object} config The config object
58403  */
58404 Roo.LoadMask = function(el, config){
58405     this.el = Roo.get(el);
58406     Roo.apply(this, config);
58407     if(this.store){
58408         this.store.on('beforeload', this.onBeforeLoad, this);
58409         this.store.on('load', this.onLoad, this);
58410         this.store.on('loadexception', this.onLoadException, this);
58411         this.removeMask = false;
58412     }else{
58413         var um = this.el.getUpdateManager();
58414         um.showLoadIndicator = false; // disable the default indicator
58415         um.on('beforeupdate', this.onBeforeLoad, this);
58416         um.on('update', this.onLoad, this);
58417         um.on('failure', this.onLoad, this);
58418         this.removeMask = true;
58419     }
58420 };
58421
58422 Roo.LoadMask.prototype = {
58423     /**
58424      * @cfg {Boolean} removeMask
58425      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
58426      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
58427      */
58428     /**
58429      * @cfg {String} msg
58430      * The text to display in a centered loading message box (defaults to 'Loading...')
58431      */
58432     msg : 'Loading...',
58433     /**
58434      * @cfg {String} msgCls
58435      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
58436      */
58437     msgCls : 'x-mask-loading',
58438
58439     /**
58440      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
58441      * @type Boolean
58442      */
58443     disabled: false,
58444
58445     /**
58446      * Disables the mask to prevent it from being displayed
58447      */
58448     disable : function(){
58449        this.disabled = true;
58450     },
58451
58452     /**
58453      * Enables the mask so that it can be displayed
58454      */
58455     enable : function(){
58456         this.disabled = false;
58457     },
58458     
58459     onLoadException : function()
58460     {
58461         Roo.log(arguments);
58462         
58463         if (typeof(arguments[3]) != 'undefined') {
58464             Roo.MessageBox.alert("Error loading",arguments[3]);
58465         } 
58466         /*
58467         try {
58468             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
58469                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
58470             }   
58471         } catch(e) {
58472             
58473         }
58474         */
58475     
58476         
58477         
58478         this.el.unmask(this.removeMask);
58479     },
58480     // private
58481     onLoad : function()
58482     {
58483         this.el.unmask(this.removeMask);
58484     },
58485
58486     // private
58487     onBeforeLoad : function(){
58488         if(!this.disabled){
58489             this.el.mask(this.msg, this.msgCls);
58490         }
58491     },
58492
58493     // private
58494     destroy : function(){
58495         if(this.store){
58496             this.store.un('beforeload', this.onBeforeLoad, this);
58497             this.store.un('load', this.onLoad, this);
58498             this.store.un('loadexception', this.onLoadException, this);
58499         }else{
58500             var um = this.el.getUpdateManager();
58501             um.un('beforeupdate', this.onBeforeLoad, this);
58502             um.un('update', this.onLoad, this);
58503             um.un('failure', this.onLoad, this);
58504         }
58505     }
58506 };/*
58507  * Based on:
58508  * Ext JS Library 1.1.1
58509  * Copyright(c) 2006-2007, Ext JS, LLC.
58510  *
58511  * Originally Released Under LGPL - original licence link has changed is not relivant.
58512  *
58513  * Fork - LGPL
58514  * <script type="text/javascript">
58515  */
58516
58517
58518 /**
58519  * @class Roo.XTemplate
58520  * @extends Roo.Template
58521  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
58522 <pre><code>
58523 var t = new Roo.XTemplate(
58524         '&lt;select name="{name}"&gt;',
58525                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
58526         '&lt;/select&gt;'
58527 );
58528  
58529 // then append, applying the master template values
58530  </code></pre>
58531  *
58532  * Supported features:
58533  *
58534  *  Tags:
58535
58536 <pre><code>
58537       {a_variable} - output encoded.
58538       {a_variable.format:("Y-m-d")} - call a method on the variable
58539       {a_variable:raw} - unencoded output
58540       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
58541       {a_variable:this.method_on_template(...)} - call a method on the template object.
58542  
58543 </code></pre>
58544  *  The tpl tag:
58545 <pre><code>
58546         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
58547         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
58548         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
58549         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
58550   
58551         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
58552         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
58553 </code></pre>
58554  *      
58555  */
58556 Roo.XTemplate = function()
58557 {
58558     Roo.XTemplate.superclass.constructor.apply(this, arguments);
58559     if (this.html) {
58560         this.compile();
58561     }
58562 };
58563
58564
58565 Roo.extend(Roo.XTemplate, Roo.Template, {
58566
58567     /**
58568      * The various sub templates
58569      */
58570     tpls : false,
58571     /**
58572      *
58573      * basic tag replacing syntax
58574      * WORD:WORD()
58575      *
58576      * // you can fake an object call by doing this
58577      *  x.t:(test,tesT) 
58578      * 
58579      */
58580     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
58581
58582     /**
58583      * compile the template
58584      *
58585      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
58586      *
58587      */
58588     compile: function()
58589     {
58590         var s = this.html;
58591      
58592         s = ['<tpl>', s, '</tpl>'].join('');
58593     
58594         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
58595             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
58596             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
58597             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
58598             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
58599             m,
58600             id     = 0,
58601             tpls   = [];
58602     
58603         while(true == !!(m = s.match(re))){
58604             var forMatch   = m[0].match(nameRe),
58605                 ifMatch   = m[0].match(ifRe),
58606                 execMatch   = m[0].match(execRe),
58607                 namedMatch   = m[0].match(namedRe),
58608                 
58609                 exp  = null, 
58610                 fn   = null,
58611                 exec = null,
58612                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58613                 
58614             if (ifMatch) {
58615                 // if - puts fn into test..
58616                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58617                 if(exp){
58618                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58619                 }
58620             }
58621             
58622             if (execMatch) {
58623                 // exec - calls a function... returns empty if true is  returned.
58624                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58625                 if(exp){
58626                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58627                 }
58628             }
58629             
58630             
58631             if (name) {
58632                 // for = 
58633                 switch(name){
58634                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58635                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58636                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58637                 }
58638             }
58639             var uid = namedMatch ? namedMatch[1] : id;
58640             
58641             
58642             tpls.push({
58643                 id:     namedMatch ? namedMatch[1] : id,
58644                 target: name,
58645                 exec:   exec,
58646                 test:   fn,
58647                 body:   m[1] || ''
58648             });
58649             if (namedMatch) {
58650                 s = s.replace(m[0], '');
58651             } else { 
58652                 s = s.replace(m[0], '{xtpl'+ id + '}');
58653             }
58654             ++id;
58655         }
58656         this.tpls = [];
58657         for(var i = tpls.length-1; i >= 0; --i){
58658             this.compileTpl(tpls[i]);
58659             this.tpls[tpls[i].id] = tpls[i];
58660         }
58661         this.master = tpls[tpls.length-1];
58662         return this;
58663     },
58664     /**
58665      * same as applyTemplate, except it's done to one of the subTemplates
58666      * when using named templates, you can do:
58667      *
58668      * var str = pl.applySubTemplate('your-name', values);
58669      *
58670      * 
58671      * @param {Number} id of the template
58672      * @param {Object} values to apply to template
58673      * @param {Object} parent (normaly the instance of this object)
58674      */
58675     applySubTemplate : function(id, values, parent)
58676     {
58677         
58678         
58679         var t = this.tpls[id];
58680         
58681         
58682         try { 
58683             if(t.test && !t.test.call(this, values, parent)){
58684                 return '';
58685             }
58686         } catch(e) {
58687             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58688             Roo.log(e.toString());
58689             Roo.log(t.test);
58690             return ''
58691         }
58692         try { 
58693             
58694             if(t.exec && t.exec.call(this, values, parent)){
58695                 return '';
58696             }
58697         } catch(e) {
58698             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58699             Roo.log(e.toString());
58700             Roo.log(t.exec);
58701             return ''
58702         }
58703         try {
58704             var vs = t.target ? t.target.call(this, values, parent) : values;
58705             parent = t.target ? values : parent;
58706             if(t.target && vs instanceof Array){
58707                 var buf = [];
58708                 for(var i = 0, len = vs.length; i < len; i++){
58709                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58710                 }
58711                 return buf.join('');
58712             }
58713             return t.compiled.call(this, vs, parent);
58714         } catch (e) {
58715             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58716             Roo.log(e.toString());
58717             Roo.log(t.compiled);
58718             return '';
58719         }
58720     },
58721
58722     compileTpl : function(tpl)
58723     {
58724         var fm = Roo.util.Format;
58725         var useF = this.disableFormats !== true;
58726         var sep = Roo.isGecko ? "+" : ",";
58727         var undef = function(str) {
58728             Roo.log("Property not found :"  + str);
58729             return '';
58730         };
58731         
58732         var fn = function(m, name, format, args)
58733         {
58734             //Roo.log(arguments);
58735             args = args ? args.replace(/\\'/g,"'") : args;
58736             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58737             if (typeof(format) == 'undefined') {
58738                 format= 'htmlEncode';
58739             }
58740             if (format == 'raw' ) {
58741                 format = false;
58742             }
58743             
58744             if(name.substr(0, 4) == 'xtpl'){
58745                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58746             }
58747             
58748             // build an array of options to determine if value is undefined..
58749             
58750             // basically get 'xxxx.yyyy' then do
58751             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58752             //    (function () { Roo.log("Property not found"); return ''; })() :
58753             //    ......
58754             
58755             var udef_ar = [];
58756             var lookfor = '';
58757             Roo.each(name.split('.'), function(st) {
58758                 lookfor += (lookfor.length ? '.': '') + st;
58759                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58760             });
58761             
58762             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58763             
58764             
58765             if(format && useF){
58766                 
58767                 args = args ? ',' + args : "";
58768                  
58769                 if(format.substr(0, 5) != "this."){
58770                     format = "fm." + format + '(';
58771                 }else{
58772                     format = 'this.call("'+ format.substr(5) + '", ';
58773                     args = ", values";
58774                 }
58775                 
58776                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58777             }
58778              
58779             if (args.length) {
58780                 // called with xxyx.yuu:(test,test)
58781                 // change to ()
58782                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58783             }
58784             // raw.. - :raw modifier..
58785             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58786             
58787         };
58788         var body;
58789         // branched to use + in gecko and [].join() in others
58790         if(Roo.isGecko){
58791             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58792                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58793                     "';};};";
58794         }else{
58795             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58796             body.push(tpl.body.replace(/(\r\n|\n)/g,
58797                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58798             body.push("'].join('');};};");
58799             body = body.join('');
58800         }
58801         
58802         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58803        
58804         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58805         eval(body);
58806         
58807         return this;
58808     },
58809
58810     applyTemplate : function(values){
58811         return this.master.compiled.call(this, values, {});
58812         //var s = this.subs;
58813     },
58814
58815     apply : function(){
58816         return this.applyTemplate.apply(this, arguments);
58817     }
58818
58819  });
58820
58821 Roo.XTemplate.from = function(el){
58822     el = Roo.getDom(el);
58823     return new Roo.XTemplate(el.value || el.innerHTML);
58824 };