docs/default.css
[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.isTouch){
6588         cls.push("roo-touch");
6589     }
6590     if(Roo.isBorderBox){
6591         cls.push('roo-border-box');
6592     }
6593     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6594         var p = bd.dom.parentNode;
6595         if(p){
6596             p.className += ' roo-strict';
6597         }
6598     }
6599     bd.addClass(cls.join(' '));
6600 });
6601
6602 /**
6603  * @class Roo.EventObject
6604  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6605  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6606  * Example:
6607  * <pre><code>
6608  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6609     e.preventDefault();
6610     var target = e.getTarget();
6611     ...
6612  }
6613  var myDiv = Roo.get("myDiv");
6614  myDiv.on("click", handleClick);
6615  //or
6616  Roo.EventManager.on("myDiv", 'click', handleClick);
6617  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6618  </code></pre>
6619  * @singleton
6620  */
6621 Roo.EventObject = function(){
6622     
6623     var E = Roo.lib.Event;
6624     
6625     // safari keypress events for special keys return bad keycodes
6626     var safariKeys = {
6627         63234 : 37, // left
6628         63235 : 39, // right
6629         63232 : 38, // up
6630         63233 : 40, // down
6631         63276 : 33, // page up
6632         63277 : 34, // page down
6633         63272 : 46, // delete
6634         63273 : 36, // home
6635         63275 : 35  // end
6636     };
6637
6638     // normalize button clicks
6639     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6640                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6641
6642     Roo.EventObjectImpl = function(e){
6643         if(e){
6644             this.setEvent(e.browserEvent || e);
6645         }
6646     };
6647     Roo.EventObjectImpl.prototype = {
6648         /**
6649          * Used to fix doc tools.
6650          * @scope Roo.EventObject.prototype
6651          */
6652             
6653
6654         
6655         
6656         /** The normal browser event */
6657         browserEvent : null,
6658         /** The button pressed in a mouse event */
6659         button : -1,
6660         /** True if the shift key was down during the event */
6661         shiftKey : false,
6662         /** True if the control key was down during the event */
6663         ctrlKey : false,
6664         /** True if the alt key was down during the event */
6665         altKey : false,
6666
6667         /** Key constant 
6668         * @type Number */
6669         BACKSPACE : 8,
6670         /** Key constant 
6671         * @type Number */
6672         TAB : 9,
6673         /** Key constant 
6674         * @type Number */
6675         RETURN : 13,
6676         /** Key constant 
6677         * @type Number */
6678         ENTER : 13,
6679         /** Key constant 
6680         * @type Number */
6681         SHIFT : 16,
6682         /** Key constant 
6683         * @type Number */
6684         CONTROL : 17,
6685         /** Key constant 
6686         * @type Number */
6687         ESC : 27,
6688         /** Key constant 
6689         * @type Number */
6690         SPACE : 32,
6691         /** Key constant 
6692         * @type Number */
6693         PAGEUP : 33,
6694         /** Key constant 
6695         * @type Number */
6696         PAGEDOWN : 34,
6697         /** Key constant 
6698         * @type Number */
6699         END : 35,
6700         /** Key constant 
6701         * @type Number */
6702         HOME : 36,
6703         /** Key constant 
6704         * @type Number */
6705         LEFT : 37,
6706         /** Key constant 
6707         * @type Number */
6708         UP : 38,
6709         /** Key constant 
6710         * @type Number */
6711         RIGHT : 39,
6712         /** Key constant 
6713         * @type Number */
6714         DOWN : 40,
6715         /** Key constant 
6716         * @type Number */
6717         DELETE : 46,
6718         /** Key constant 
6719         * @type Number */
6720         F5 : 116,
6721
6722            /** @private */
6723         setEvent : function(e){
6724             if(e == this || (e && e.browserEvent)){ // already wrapped
6725                 return e;
6726             }
6727             this.browserEvent = e;
6728             if(e){
6729                 // normalize buttons
6730                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6731                 if(e.type == 'click' && this.button == -1){
6732                     this.button = 0;
6733                 }
6734                 this.type = e.type;
6735                 this.shiftKey = e.shiftKey;
6736                 // mac metaKey behaves like ctrlKey
6737                 this.ctrlKey = e.ctrlKey || e.metaKey;
6738                 this.altKey = e.altKey;
6739                 // in getKey these will be normalized for the mac
6740                 this.keyCode = e.keyCode;
6741                 // keyup warnings on firefox.
6742                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6743                 // cache the target for the delayed and or buffered events
6744                 this.target = E.getTarget(e);
6745                 // same for XY
6746                 this.xy = E.getXY(e);
6747             }else{
6748                 this.button = -1;
6749                 this.shiftKey = false;
6750                 this.ctrlKey = false;
6751                 this.altKey = false;
6752                 this.keyCode = 0;
6753                 this.charCode =0;
6754                 this.target = null;
6755                 this.xy = [0, 0];
6756             }
6757             return this;
6758         },
6759
6760         /**
6761          * Stop the event (preventDefault and stopPropagation)
6762          */
6763         stopEvent : function(){
6764             if(this.browserEvent){
6765                 if(this.browserEvent.type == 'mousedown'){
6766                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6767                 }
6768                 E.stopEvent(this.browserEvent);
6769             }
6770         },
6771
6772         /**
6773          * Prevents the browsers default handling of the event.
6774          */
6775         preventDefault : function(){
6776             if(this.browserEvent){
6777                 E.preventDefault(this.browserEvent);
6778             }
6779         },
6780
6781         /** @private */
6782         isNavKeyPress : function(){
6783             var k = this.keyCode;
6784             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6785             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6786         },
6787
6788         isSpecialKey : function(){
6789             var k = this.keyCode;
6790             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6791             (k == 16) || (k == 17) ||
6792             (k >= 18 && k <= 20) ||
6793             (k >= 33 && k <= 35) ||
6794             (k >= 36 && k <= 39) ||
6795             (k >= 44 && k <= 45);
6796         },
6797         /**
6798          * Cancels bubbling of the event.
6799          */
6800         stopPropagation : function(){
6801             if(this.browserEvent){
6802                 if(this.type == 'mousedown'){
6803                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6804                 }
6805                 E.stopPropagation(this.browserEvent);
6806             }
6807         },
6808
6809         /**
6810          * Gets the key code for the event.
6811          * @return {Number}
6812          */
6813         getCharCode : function(){
6814             return this.charCode || this.keyCode;
6815         },
6816
6817         /**
6818          * Returns a normalized keyCode for the event.
6819          * @return {Number} The key code
6820          */
6821         getKey : function(){
6822             var k = this.keyCode || this.charCode;
6823             return Roo.isSafari ? (safariKeys[k] || k) : k;
6824         },
6825
6826         /**
6827          * Gets the x coordinate of the event.
6828          * @return {Number}
6829          */
6830         getPageX : function(){
6831             return this.xy[0];
6832         },
6833
6834         /**
6835          * Gets the y coordinate of the event.
6836          * @return {Number}
6837          */
6838         getPageY : function(){
6839             return this.xy[1];
6840         },
6841
6842         /**
6843          * Gets the time of the event.
6844          * @return {Number}
6845          */
6846         getTime : function(){
6847             if(this.browserEvent){
6848                 return E.getTime(this.browserEvent);
6849             }
6850             return null;
6851         },
6852
6853         /**
6854          * Gets the page coordinates of the event.
6855          * @return {Array} The xy values like [x, y]
6856          */
6857         getXY : function(){
6858             return this.xy;
6859         },
6860
6861         /**
6862          * Gets the target for the event.
6863          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6864          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6865                 search as a number or element (defaults to 10 || document.body)
6866          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6867          * @return {HTMLelement}
6868          */
6869         getTarget : function(selector, maxDepth, returnEl){
6870             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6871         },
6872         /**
6873          * Gets the related target.
6874          * @return {HTMLElement}
6875          */
6876         getRelatedTarget : function(){
6877             if(this.browserEvent){
6878                 return E.getRelatedTarget(this.browserEvent);
6879             }
6880             return null;
6881         },
6882
6883         /**
6884          * Normalizes mouse wheel delta across browsers
6885          * @return {Number} The delta
6886          */
6887         getWheelDelta : function(){
6888             var e = this.browserEvent;
6889             var delta = 0;
6890             if(e.wheelDelta){ /* IE/Opera. */
6891                 delta = e.wheelDelta/120;
6892             }else if(e.detail){ /* Mozilla case. */
6893                 delta = -e.detail/3;
6894             }
6895             return delta;
6896         },
6897
6898         /**
6899          * Returns true if the control, meta, shift or alt key was pressed during this event.
6900          * @return {Boolean}
6901          */
6902         hasModifier : function(){
6903             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6904         },
6905
6906         /**
6907          * Returns true if the target of this event equals el or is a child of el
6908          * @param {String/HTMLElement/Element} el
6909          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6910          * @return {Boolean}
6911          */
6912         within : function(el, related){
6913             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6914             return t && Roo.fly(el).contains(t);
6915         },
6916
6917         getPoint : function(){
6918             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6919         }
6920     };
6921
6922     return new Roo.EventObjectImpl();
6923 }();
6924             
6925     /*
6926  * Based on:
6927  * Ext JS Library 1.1.1
6928  * Copyright(c) 2006-2007, Ext JS, LLC.
6929  *
6930  * Originally Released Under LGPL - original licence link has changed is not relivant.
6931  *
6932  * Fork - LGPL
6933  * <script type="text/javascript">
6934  */
6935
6936  
6937 // was in Composite Element!??!?!
6938  
6939 (function(){
6940     var D = Roo.lib.Dom;
6941     var E = Roo.lib.Event;
6942     var A = Roo.lib.Anim;
6943
6944     // local style camelizing for speed
6945     var propCache = {};
6946     var camelRe = /(-[a-z])/gi;
6947     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6948     var view = document.defaultView;
6949
6950 /**
6951  * @class Roo.Element
6952  * Represents an Element in the DOM.<br><br>
6953  * Usage:<br>
6954 <pre><code>
6955 var el = Roo.get("my-div");
6956
6957 // or with getEl
6958 var el = getEl("my-div");
6959
6960 // or with a DOM element
6961 var el = Roo.get(myDivElement);
6962 </code></pre>
6963  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6964  * each call instead of constructing a new one.<br><br>
6965  * <b>Animations</b><br />
6966  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6967  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6968 <pre>
6969 Option    Default   Description
6970 --------- --------  ---------------------------------------------
6971 duration  .35       The duration of the animation in seconds
6972 easing    easeOut   The YUI easing method
6973 callback  none      A function to execute when the anim completes
6974 scope     this      The scope (this) of the callback function
6975 </pre>
6976 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6977 * manipulate the animation. Here's an example:
6978 <pre><code>
6979 var el = Roo.get("my-div");
6980
6981 // no animation
6982 el.setWidth(100);
6983
6984 // default animation
6985 el.setWidth(100, true);
6986
6987 // animation with some options set
6988 el.setWidth(100, {
6989     duration: 1,
6990     callback: this.foo,
6991     scope: this
6992 });
6993
6994 // using the "anim" property to get the Anim object
6995 var opt = {
6996     duration: 1,
6997     callback: this.foo,
6998     scope: this
6999 };
7000 el.setWidth(100, opt);
7001 ...
7002 if(opt.anim.isAnimated()){
7003     opt.anim.stop();
7004 }
7005 </code></pre>
7006 * <b> Composite (Collections of) Elements</b><br />
7007  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7008  * @constructor Create a new Element directly.
7009  * @param {String/HTMLElement} element
7010  * @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).
7011  */
7012     Roo.Element = function(element, forceNew){
7013         var dom = typeof element == "string" ?
7014                 document.getElementById(element) : element;
7015         if(!dom){ // invalid id/element
7016             return null;
7017         }
7018         var id = dom.id;
7019         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7020             return Roo.Element.cache[id];
7021         }
7022
7023         /**
7024          * The DOM element
7025          * @type HTMLElement
7026          */
7027         this.dom = dom;
7028
7029         /**
7030          * The DOM element ID
7031          * @type String
7032          */
7033         this.id = id || Roo.id(dom);
7034     };
7035
7036     var El = Roo.Element;
7037
7038     El.prototype = {
7039         /**
7040          * The element's default display mode  (defaults to "")
7041          * @type String
7042          */
7043         originalDisplay : "",
7044
7045         visibilityMode : 1,
7046         /**
7047          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7048          * @type String
7049          */
7050         defaultUnit : "px",
7051         
7052         /**
7053          * Sets the element's visibility mode. When setVisible() is called it
7054          * will use this to determine whether to set the visibility or the display property.
7055          * @param visMode Element.VISIBILITY or Element.DISPLAY
7056          * @return {Roo.Element} this
7057          */
7058         setVisibilityMode : function(visMode){
7059             this.visibilityMode = visMode;
7060             return this;
7061         },
7062         /**
7063          * Convenience method for setVisibilityMode(Element.DISPLAY)
7064          * @param {String} display (optional) What to set display to when visible
7065          * @return {Roo.Element} this
7066          */
7067         enableDisplayMode : function(display){
7068             this.setVisibilityMode(El.DISPLAY);
7069             if(typeof display != "undefined") this.originalDisplay = display;
7070             return this;
7071         },
7072
7073         /**
7074          * 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)
7075          * @param {String} selector The simple selector to test
7076          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7077                 search as a number or element (defaults to 10 || document.body)
7078          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7079          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7080          */
7081         findParent : function(simpleSelector, maxDepth, returnEl){
7082             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7083             maxDepth = maxDepth || 50;
7084             if(typeof maxDepth != "number"){
7085                 stopEl = Roo.getDom(maxDepth);
7086                 maxDepth = 10;
7087             }
7088             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7089                 if(dq.is(p, simpleSelector)){
7090                     return returnEl ? Roo.get(p) : p;
7091                 }
7092                 depth++;
7093                 p = p.parentNode;
7094             }
7095             return null;
7096         },
7097
7098
7099         /**
7100          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7101          * @param {String} selector The simple selector to test
7102          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7103                 search as a number or element (defaults to 10 || document.body)
7104          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7105          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7106          */
7107         findParentNode : function(simpleSelector, maxDepth, returnEl){
7108             var p = Roo.fly(this.dom.parentNode, '_internal');
7109             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7110         },
7111
7112         /**
7113          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7114          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7115          * @param {String} selector The simple selector to test
7116          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7117                 search as a number or element (defaults to 10 || document.body)
7118          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7119          */
7120         up : function(simpleSelector, maxDepth){
7121             return this.findParentNode(simpleSelector, maxDepth, true);
7122         },
7123
7124
7125
7126         /**
7127          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7128          * @param {String} selector The simple selector to test
7129          * @return {Boolean} True if this element matches the selector, else false
7130          */
7131         is : function(simpleSelector){
7132             return Roo.DomQuery.is(this.dom, simpleSelector);
7133         },
7134
7135         /**
7136          * Perform animation on this element.
7137          * @param {Object} args The YUI animation control args
7138          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7139          * @param {Function} onComplete (optional) Function to call when animation completes
7140          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7141          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7142          * @return {Roo.Element} this
7143          */
7144         animate : function(args, duration, onComplete, easing, animType){
7145             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7146             return this;
7147         },
7148
7149         /*
7150          * @private Internal animation call
7151          */
7152         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7153             animType = animType || 'run';
7154             opt = opt || {};
7155             var anim = Roo.lib.Anim[animType](
7156                 this.dom, args,
7157                 (opt.duration || defaultDur) || .35,
7158                 (opt.easing || defaultEase) || 'easeOut',
7159                 function(){
7160                     Roo.callback(cb, this);
7161                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7162                 },
7163                 this
7164             );
7165             opt.anim = anim;
7166             return anim;
7167         },
7168
7169         // private legacy anim prep
7170         preanim : function(a, i){
7171             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7172         },
7173
7174         /**
7175          * Removes worthless text nodes
7176          * @param {Boolean} forceReclean (optional) By default the element
7177          * keeps track if it has been cleaned already so
7178          * you can call this over and over. However, if you update the element and
7179          * need to force a reclean, you can pass true.
7180          */
7181         clean : function(forceReclean){
7182             if(this.isCleaned && forceReclean !== true){
7183                 return this;
7184             }
7185             var ns = /\S/;
7186             var d = this.dom, n = d.firstChild, ni = -1;
7187             while(n){
7188                 var nx = n.nextSibling;
7189                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7190                     d.removeChild(n);
7191                 }else{
7192                     n.nodeIndex = ++ni;
7193                 }
7194                 n = nx;
7195             }
7196             this.isCleaned = true;
7197             return this;
7198         },
7199
7200         // private
7201         calcOffsetsTo : function(el){
7202             el = Roo.get(el);
7203             var d = el.dom;
7204             var restorePos = false;
7205             if(el.getStyle('position') == 'static'){
7206                 el.position('relative');
7207                 restorePos = true;
7208             }
7209             var x = 0, y =0;
7210             var op = this.dom;
7211             while(op && op != d && op.tagName != 'HTML'){
7212                 x+= op.offsetLeft;
7213                 y+= op.offsetTop;
7214                 op = op.offsetParent;
7215             }
7216             if(restorePos){
7217                 el.position('static');
7218             }
7219             return [x, y];
7220         },
7221
7222         /**
7223          * Scrolls this element into view within the passed container.
7224          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7225          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7226          * @return {Roo.Element} this
7227          */
7228         scrollIntoView : function(container, hscroll){
7229             var c = Roo.getDom(container) || document.body;
7230             var el = this.dom;
7231
7232             var o = this.calcOffsetsTo(c),
7233                 l = o[0],
7234                 t = o[1],
7235                 b = t+el.offsetHeight,
7236                 r = l+el.offsetWidth;
7237
7238             var ch = c.clientHeight;
7239             var ct = parseInt(c.scrollTop, 10);
7240             var cl = parseInt(c.scrollLeft, 10);
7241             var cb = ct + ch;
7242             var cr = cl + c.clientWidth;
7243
7244             if(t < ct){
7245                 c.scrollTop = t;
7246             }else if(b > cb){
7247                 c.scrollTop = b-ch;
7248             }
7249
7250             if(hscroll !== false){
7251                 if(l < cl){
7252                     c.scrollLeft = l;
7253                 }else if(r > cr){
7254                     c.scrollLeft = r-c.clientWidth;
7255                 }
7256             }
7257             return this;
7258         },
7259
7260         // private
7261         scrollChildIntoView : function(child, hscroll){
7262             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7263         },
7264
7265         /**
7266          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7267          * the new height may not be available immediately.
7268          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7269          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7270          * @param {Function} onComplete (optional) Function to call when animation completes
7271          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7272          * @return {Roo.Element} this
7273          */
7274         autoHeight : function(animate, duration, onComplete, easing){
7275             var oldHeight = this.getHeight();
7276             this.clip();
7277             this.setHeight(1); // force clipping
7278             setTimeout(function(){
7279                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7280                 if(!animate){
7281                     this.setHeight(height);
7282                     this.unclip();
7283                     if(typeof onComplete == "function"){
7284                         onComplete();
7285                     }
7286                 }else{
7287                     this.setHeight(oldHeight); // restore original height
7288                     this.setHeight(height, animate, duration, function(){
7289                         this.unclip();
7290                         if(typeof onComplete == "function") onComplete();
7291                     }.createDelegate(this), easing);
7292                 }
7293             }.createDelegate(this), 0);
7294             return this;
7295         },
7296
7297         /**
7298          * Returns true if this element is an ancestor of the passed element
7299          * @param {HTMLElement/String} el The element to check
7300          * @return {Boolean} True if this element is an ancestor of el, else false
7301          */
7302         contains : function(el){
7303             if(!el){return false;}
7304             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7305         },
7306
7307         /**
7308          * Checks whether the element is currently visible using both visibility and display properties.
7309          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7310          * @return {Boolean} True if the element is currently visible, else false
7311          */
7312         isVisible : function(deep) {
7313             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7314             if(deep !== true || !vis){
7315                 return vis;
7316             }
7317             var p = this.dom.parentNode;
7318             while(p && p.tagName.toLowerCase() != "body"){
7319                 if(!Roo.fly(p, '_isVisible').isVisible()){
7320                     return false;
7321                 }
7322                 p = p.parentNode;
7323             }
7324             return true;
7325         },
7326
7327         /**
7328          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7329          * @param {String} selector The CSS selector
7330          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7331          * @return {CompositeElement/CompositeElementLite} The composite element
7332          */
7333         select : function(selector, unique){
7334             return El.select(selector, unique, this.dom);
7335         },
7336
7337         /**
7338          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7339          * @param {String} selector The CSS selector
7340          * @return {Array} An array of the matched nodes
7341          */
7342         query : function(selector, unique){
7343             return Roo.DomQuery.select(selector, this.dom);
7344         },
7345
7346         /**
7347          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7348          * @param {String} selector The CSS selector
7349          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7350          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7351          */
7352         child : function(selector, returnDom){
7353             var n = Roo.DomQuery.selectNode(selector, this.dom);
7354             return returnDom ? n : Roo.get(n);
7355         },
7356
7357         /**
7358          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7359          * @param {String} selector The CSS selector
7360          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7361          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7362          */
7363         down : function(selector, returnDom){
7364             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7365             return returnDom ? n : Roo.get(n);
7366         },
7367
7368         /**
7369          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7370          * @param {String} group The group the DD object is member of
7371          * @param {Object} config The DD config object
7372          * @param {Object} overrides An object containing methods to override/implement on the DD object
7373          * @return {Roo.dd.DD} The DD object
7374          */
7375         initDD : function(group, config, overrides){
7376             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7377             return Roo.apply(dd, overrides);
7378         },
7379
7380         /**
7381          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7382          * @param {String} group The group the DDProxy object is member of
7383          * @param {Object} config The DDProxy config object
7384          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7385          * @return {Roo.dd.DDProxy} The DDProxy object
7386          */
7387         initDDProxy : function(group, config, overrides){
7388             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7389             return Roo.apply(dd, overrides);
7390         },
7391
7392         /**
7393          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7394          * @param {String} group The group the DDTarget object is member of
7395          * @param {Object} config The DDTarget config object
7396          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7397          * @return {Roo.dd.DDTarget} The DDTarget object
7398          */
7399         initDDTarget : function(group, config, overrides){
7400             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7401             return Roo.apply(dd, overrides);
7402         },
7403
7404         /**
7405          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7406          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7407          * @param {Boolean} visible Whether the element is visible
7408          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7409          * @return {Roo.Element} this
7410          */
7411          setVisible : function(visible, animate){
7412             if(!animate || !A){
7413                 if(this.visibilityMode == El.DISPLAY){
7414                     this.setDisplayed(visible);
7415                 }else{
7416                     this.fixDisplay();
7417                     this.dom.style.visibility = visible ? "visible" : "hidden";
7418                 }
7419             }else{
7420                 // closure for composites
7421                 var dom = this.dom;
7422                 var visMode = this.visibilityMode;
7423                 if(visible){
7424                     this.setOpacity(.01);
7425                     this.setVisible(true);
7426                 }
7427                 this.anim({opacity: { to: (visible?1:0) }},
7428                       this.preanim(arguments, 1),
7429                       null, .35, 'easeIn', function(){
7430                          if(!visible){
7431                              if(visMode == El.DISPLAY){
7432                                  dom.style.display = "none";
7433                              }else{
7434                                  dom.style.visibility = "hidden";
7435                              }
7436                              Roo.get(dom).setOpacity(1);
7437                          }
7438                      });
7439             }
7440             return this;
7441         },
7442
7443         /**
7444          * Returns true if display is not "none"
7445          * @return {Boolean}
7446          */
7447         isDisplayed : function() {
7448             return this.getStyle("display") != "none";
7449         },
7450
7451         /**
7452          * Toggles the element's visibility or display, depending on visibility mode.
7453          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7454          * @return {Roo.Element} this
7455          */
7456         toggle : function(animate){
7457             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7458             return this;
7459         },
7460
7461         /**
7462          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7463          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7464          * @return {Roo.Element} this
7465          */
7466         setDisplayed : function(value) {
7467             if(typeof value == "boolean"){
7468                value = value ? this.originalDisplay : "none";
7469             }
7470             this.setStyle("display", value);
7471             return this;
7472         },
7473
7474         /**
7475          * Tries to focus the element. Any exceptions are caught and ignored.
7476          * @return {Roo.Element} this
7477          */
7478         focus : function() {
7479             try{
7480                 this.dom.focus();
7481             }catch(e){}
7482             return this;
7483         },
7484
7485         /**
7486          * Tries to blur the element. Any exceptions are caught and ignored.
7487          * @return {Roo.Element} this
7488          */
7489         blur : function() {
7490             try{
7491                 this.dom.blur();
7492             }catch(e){}
7493             return this;
7494         },
7495
7496         /**
7497          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7498          * @param {String/Array} className The CSS class to add, or an array of classes
7499          * @return {Roo.Element} this
7500          */
7501         addClass : function(className){
7502             if(className instanceof Array){
7503                 for(var i = 0, len = className.length; i < len; i++) {
7504                     this.addClass(className[i]);
7505                 }
7506             }else{
7507                 if(className && !this.hasClass(className)){
7508                     this.dom.className = this.dom.className + " " + className;
7509                 }
7510             }
7511             return this;
7512         },
7513
7514         /**
7515          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7516          * @param {String/Array} className The CSS class to add, or an array of classes
7517          * @return {Roo.Element} this
7518          */
7519         radioClass : function(className){
7520             var siblings = this.dom.parentNode.childNodes;
7521             for(var i = 0; i < siblings.length; i++) {
7522                 var s = siblings[i];
7523                 if(s.nodeType == 1){
7524                     Roo.get(s).removeClass(className);
7525                 }
7526             }
7527             this.addClass(className);
7528             return this;
7529         },
7530
7531         /**
7532          * Removes one or more CSS classes from the element.
7533          * @param {String/Array} className The CSS class to remove, or an array of classes
7534          * @return {Roo.Element} this
7535          */
7536         removeClass : function(className){
7537             if(!className || !this.dom.className){
7538                 return this;
7539             }
7540             if(className instanceof Array){
7541                 for(var i = 0, len = className.length; i < len; i++) {
7542                     this.removeClass(className[i]);
7543                 }
7544             }else{
7545                 if(this.hasClass(className)){
7546                     var re = this.classReCache[className];
7547                     if (!re) {
7548                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7549                        this.classReCache[className] = re;
7550                     }
7551                     this.dom.className =
7552                         this.dom.className.replace(re, " ");
7553                 }
7554             }
7555             return this;
7556         },
7557
7558         // private
7559         classReCache: {},
7560
7561         /**
7562          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7563          * @param {String} className The CSS class to toggle
7564          * @return {Roo.Element} this
7565          */
7566         toggleClass : function(className){
7567             if(this.hasClass(className)){
7568                 this.removeClass(className);
7569             }else{
7570                 this.addClass(className);
7571             }
7572             return this;
7573         },
7574
7575         /**
7576          * Checks if the specified CSS class exists on this element's DOM node.
7577          * @param {String} className The CSS class to check for
7578          * @return {Boolean} True if the class exists, else false
7579          */
7580         hasClass : function(className){
7581             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7582         },
7583
7584         /**
7585          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7586          * @param {String} oldClassName The CSS class to replace
7587          * @param {String} newClassName The replacement CSS class
7588          * @return {Roo.Element} this
7589          */
7590         replaceClass : function(oldClassName, newClassName){
7591             this.removeClass(oldClassName);
7592             this.addClass(newClassName);
7593             return this;
7594         },
7595
7596         /**
7597          * Returns an object with properties matching the styles requested.
7598          * For example, el.getStyles('color', 'font-size', 'width') might return
7599          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7600          * @param {String} style1 A style name
7601          * @param {String} style2 A style name
7602          * @param {String} etc.
7603          * @return {Object} The style object
7604          */
7605         getStyles : function(){
7606             var a = arguments, len = a.length, r = {};
7607             for(var i = 0; i < len; i++){
7608                 r[a[i]] = this.getStyle(a[i]);
7609             }
7610             return r;
7611         },
7612
7613         /**
7614          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7615          * @param {String} property The style property whose value is returned.
7616          * @return {String} The current value of the style property for this element.
7617          */
7618         getStyle : function(){
7619             return view && view.getComputedStyle ?
7620                 function(prop){
7621                     var el = this.dom, v, cs, camel;
7622                     if(prop == 'float'){
7623                         prop = "cssFloat";
7624                     }
7625                     if(el.style && (v = el.style[prop])){
7626                         return v;
7627                     }
7628                     if(cs = view.getComputedStyle(el, "")){
7629                         if(!(camel = propCache[prop])){
7630                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7631                         }
7632                         return cs[camel];
7633                     }
7634                     return null;
7635                 } :
7636                 function(prop){
7637                     var el = this.dom, v, cs, camel;
7638                     if(prop == 'opacity'){
7639                         if(typeof el.style.filter == 'string'){
7640                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7641                             if(m){
7642                                 var fv = parseFloat(m[1]);
7643                                 if(!isNaN(fv)){
7644                                     return fv ? fv / 100 : 0;
7645                                 }
7646                             }
7647                         }
7648                         return 1;
7649                     }else if(prop == 'float'){
7650                         prop = "styleFloat";
7651                     }
7652                     if(!(camel = propCache[prop])){
7653                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7654                     }
7655                     if(v = el.style[camel]){
7656                         return v;
7657                     }
7658                     if(cs = el.currentStyle){
7659                         return cs[camel];
7660                     }
7661                     return null;
7662                 };
7663         }(),
7664
7665         /**
7666          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7667          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7668          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7669          * @return {Roo.Element} this
7670          */
7671         setStyle : function(prop, value){
7672             if(typeof prop == "string"){
7673                 
7674                 if (prop == 'float') {
7675                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7676                     return this;
7677                 }
7678                 
7679                 var camel;
7680                 if(!(camel = propCache[prop])){
7681                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7682                 }
7683                 
7684                 if(camel == 'opacity') {
7685                     this.setOpacity(value);
7686                 }else{
7687                     this.dom.style[camel] = value;
7688                 }
7689             }else{
7690                 for(var style in prop){
7691                     if(typeof prop[style] != "function"){
7692                        this.setStyle(style, prop[style]);
7693                     }
7694                 }
7695             }
7696             return this;
7697         },
7698
7699         /**
7700          * More flexible version of {@link #setStyle} for setting style properties.
7701          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7702          * a function which returns such a specification.
7703          * @return {Roo.Element} this
7704          */
7705         applyStyles : function(style){
7706             Roo.DomHelper.applyStyles(this.dom, style);
7707             return this;
7708         },
7709
7710         /**
7711           * 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).
7712           * @return {Number} The X position of the element
7713           */
7714         getX : function(){
7715             return D.getX(this.dom);
7716         },
7717
7718         /**
7719           * 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).
7720           * @return {Number} The Y position of the element
7721           */
7722         getY : function(){
7723             return D.getY(this.dom);
7724         },
7725
7726         /**
7727           * 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).
7728           * @return {Array} The XY position of the element
7729           */
7730         getXY : function(){
7731             return D.getXY(this.dom);
7732         },
7733
7734         /**
7735          * 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).
7736          * @param {Number} The X position of the element
7737          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7738          * @return {Roo.Element} this
7739          */
7740         setX : function(x, animate){
7741             if(!animate || !A){
7742                 D.setX(this.dom, x);
7743             }else{
7744                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7745             }
7746             return this;
7747         },
7748
7749         /**
7750          * 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).
7751          * @param {Number} The Y position of the element
7752          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7753          * @return {Roo.Element} this
7754          */
7755         setY : function(y, animate){
7756             if(!animate || !A){
7757                 D.setY(this.dom, y);
7758             }else{
7759                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7760             }
7761             return this;
7762         },
7763
7764         /**
7765          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7766          * @param {String} left The left CSS property value
7767          * @return {Roo.Element} this
7768          */
7769         setLeft : function(left){
7770             this.setStyle("left", this.addUnits(left));
7771             return this;
7772         },
7773
7774         /**
7775          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7776          * @param {String} top The top CSS property value
7777          * @return {Roo.Element} this
7778          */
7779         setTop : function(top){
7780             this.setStyle("top", this.addUnits(top));
7781             return this;
7782         },
7783
7784         /**
7785          * Sets the element's CSS right style.
7786          * @param {String} right The right CSS property value
7787          * @return {Roo.Element} this
7788          */
7789         setRight : function(right){
7790             this.setStyle("right", this.addUnits(right));
7791             return this;
7792         },
7793
7794         /**
7795          * Sets the element's CSS bottom style.
7796          * @param {String} bottom The bottom CSS property value
7797          * @return {Roo.Element} this
7798          */
7799         setBottom : function(bottom){
7800             this.setStyle("bottom", this.addUnits(bottom));
7801             return this;
7802         },
7803
7804         /**
7805          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7806          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7807          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7808          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7809          * @return {Roo.Element} this
7810          */
7811         setXY : function(pos, animate){
7812             if(!animate || !A){
7813                 D.setXY(this.dom, pos);
7814             }else{
7815                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7816             }
7817             return this;
7818         },
7819
7820         /**
7821          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7822          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7823          * @param {Number} x X value for new position (coordinates are page-based)
7824          * @param {Number} y Y value for new position (coordinates are page-based)
7825          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7826          * @return {Roo.Element} this
7827          */
7828         setLocation : function(x, y, animate){
7829             this.setXY([x, y], this.preanim(arguments, 2));
7830             return this;
7831         },
7832
7833         /**
7834          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7835          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7836          * @param {Number} x X value for new position (coordinates are page-based)
7837          * @param {Number} y Y value for new position (coordinates are page-based)
7838          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7839          * @return {Roo.Element} this
7840          */
7841         moveTo : function(x, y, animate){
7842             this.setXY([x, y], this.preanim(arguments, 2));
7843             return this;
7844         },
7845
7846         /**
7847          * Returns the region of the given element.
7848          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7849          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7850          */
7851         getRegion : function(){
7852             return D.getRegion(this.dom);
7853         },
7854
7855         /**
7856          * Returns the offset height of the element
7857          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7858          * @return {Number} The element's height
7859          */
7860         getHeight : function(contentHeight){
7861             var h = this.dom.offsetHeight || 0;
7862             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7863         },
7864
7865         /**
7866          * Returns the offset width of the element
7867          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7868          * @return {Number} The element's width
7869          */
7870         getWidth : function(contentWidth){
7871             var w = this.dom.offsetWidth || 0;
7872             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7873         },
7874
7875         /**
7876          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7877          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7878          * if a height has not been set using CSS.
7879          * @return {Number}
7880          */
7881         getComputedHeight : function(){
7882             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7883             if(!h){
7884                 h = parseInt(this.getStyle('height'), 10) || 0;
7885                 if(!this.isBorderBox()){
7886                     h += this.getFrameWidth('tb');
7887                 }
7888             }
7889             return h;
7890         },
7891
7892         /**
7893          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7894          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7895          * if a width has not been set using CSS.
7896          * @return {Number}
7897          */
7898         getComputedWidth : function(){
7899             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7900             if(!w){
7901                 w = parseInt(this.getStyle('width'), 10) || 0;
7902                 if(!this.isBorderBox()){
7903                     w += this.getFrameWidth('lr');
7904                 }
7905             }
7906             return w;
7907         },
7908
7909         /**
7910          * Returns the size of the element.
7911          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7912          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7913          */
7914         getSize : function(contentSize){
7915             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7916         },
7917
7918         /**
7919          * Returns the width and height of the viewport.
7920          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7921          */
7922         getViewSize : function(){
7923             var d = this.dom, doc = document, aw = 0, ah = 0;
7924             if(d == doc || d == doc.body){
7925                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7926             }else{
7927                 return {
7928                     width : d.clientWidth,
7929                     height: d.clientHeight
7930                 };
7931             }
7932         },
7933
7934         /**
7935          * Returns the value of the "value" attribute
7936          * @param {Boolean} asNumber true to parse the value as a number
7937          * @return {String/Number}
7938          */
7939         getValue : function(asNumber){
7940             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7941         },
7942
7943         // private
7944         adjustWidth : function(width){
7945             if(typeof width == "number"){
7946                 if(this.autoBoxAdjust && !this.isBorderBox()){
7947                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7948                 }
7949                 if(width < 0){
7950                     width = 0;
7951                 }
7952             }
7953             return width;
7954         },
7955
7956         // private
7957         adjustHeight : function(height){
7958             if(typeof height == "number"){
7959                if(this.autoBoxAdjust && !this.isBorderBox()){
7960                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7961                }
7962                if(height < 0){
7963                    height = 0;
7964                }
7965             }
7966             return height;
7967         },
7968
7969         /**
7970          * Set the width of the element
7971          * @param {Number} width The new width
7972          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7973          * @return {Roo.Element} this
7974          */
7975         setWidth : function(width, animate){
7976             width = this.adjustWidth(width);
7977             if(!animate || !A){
7978                 this.dom.style.width = this.addUnits(width);
7979             }else{
7980                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7981             }
7982             return this;
7983         },
7984
7985         /**
7986          * Set the height of the element
7987          * @param {Number} height The new height
7988          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7989          * @return {Roo.Element} this
7990          */
7991          setHeight : function(height, animate){
7992             height = this.adjustHeight(height);
7993             if(!animate || !A){
7994                 this.dom.style.height = this.addUnits(height);
7995             }else{
7996                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7997             }
7998             return this;
7999         },
8000
8001         /**
8002          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8003          * @param {Number} width The new width
8004          * @param {Number} height The new height
8005          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8006          * @return {Roo.Element} this
8007          */
8008          setSize : function(width, height, animate){
8009             if(typeof width == "object"){ // in case of object from getSize()
8010                 height = width.height; width = width.width;
8011             }
8012             width = this.adjustWidth(width); height = this.adjustHeight(height);
8013             if(!animate || !A){
8014                 this.dom.style.width = this.addUnits(width);
8015                 this.dom.style.height = this.addUnits(height);
8016             }else{
8017                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8018             }
8019             return this;
8020         },
8021
8022         /**
8023          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8024          * @param {Number} x X value for new position (coordinates are page-based)
8025          * @param {Number} y Y value for new position (coordinates are page-based)
8026          * @param {Number} width The new width
8027          * @param {Number} height The new height
8028          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8029          * @return {Roo.Element} this
8030          */
8031         setBounds : function(x, y, width, height, animate){
8032             if(!animate || !A){
8033                 this.setSize(width, height);
8034                 this.setLocation(x, y);
8035             }else{
8036                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8037                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8038                               this.preanim(arguments, 4), 'motion');
8039             }
8040             return this;
8041         },
8042
8043         /**
8044          * 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.
8045          * @param {Roo.lib.Region} region The region to fill
8046          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8047          * @return {Roo.Element} this
8048          */
8049         setRegion : function(region, animate){
8050             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8051             return this;
8052         },
8053
8054         /**
8055          * Appends an event handler
8056          *
8057          * @param {String}   eventName     The type of event to append
8058          * @param {Function} fn        The method the event invokes
8059          * @param {Object} scope       (optional) The scope (this object) of the fn
8060          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8061          */
8062         addListener : function(eventName, fn, scope, options){
8063             if (this.dom) {
8064                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8065             }
8066         },
8067
8068         /**
8069          * Removes an event handler from this element
8070          * @param {String} eventName the type of event to remove
8071          * @param {Function} fn the method the event invokes
8072          * @return {Roo.Element} this
8073          */
8074         removeListener : function(eventName, fn){
8075             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8076             return this;
8077         },
8078
8079         /**
8080          * Removes all previous added listeners from this element
8081          * @return {Roo.Element} this
8082          */
8083         removeAllListeners : function(){
8084             E.purgeElement(this.dom);
8085             return this;
8086         },
8087
8088         relayEvent : function(eventName, observable){
8089             this.on(eventName, function(e){
8090                 observable.fireEvent(eventName, e);
8091             });
8092         },
8093
8094         /**
8095          * Set the opacity of the element
8096          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8097          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8098          * @return {Roo.Element} this
8099          */
8100          setOpacity : function(opacity, animate){
8101             if(!animate || !A){
8102                 var s = this.dom.style;
8103                 if(Roo.isIE){
8104                     s.zoom = 1;
8105                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8106                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8107                 }else{
8108                     s.opacity = opacity;
8109                 }
8110             }else{
8111                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8112             }
8113             return this;
8114         },
8115
8116         /**
8117          * Gets the left X coordinate
8118          * @param {Boolean} local True to get the local css position instead of page coordinate
8119          * @return {Number}
8120          */
8121         getLeft : function(local){
8122             if(!local){
8123                 return this.getX();
8124             }else{
8125                 return parseInt(this.getStyle("left"), 10) || 0;
8126             }
8127         },
8128
8129         /**
8130          * Gets the right X coordinate of the element (element X position + element width)
8131          * @param {Boolean} local True to get the local css position instead of page coordinate
8132          * @return {Number}
8133          */
8134         getRight : function(local){
8135             if(!local){
8136                 return this.getX() + this.getWidth();
8137             }else{
8138                 return (this.getLeft(true) + this.getWidth()) || 0;
8139             }
8140         },
8141
8142         /**
8143          * Gets the top Y coordinate
8144          * @param {Boolean} local True to get the local css position instead of page coordinate
8145          * @return {Number}
8146          */
8147         getTop : function(local) {
8148             if(!local){
8149                 return this.getY();
8150             }else{
8151                 return parseInt(this.getStyle("top"), 10) || 0;
8152             }
8153         },
8154
8155         /**
8156          * Gets the bottom Y coordinate of the element (element Y position + element height)
8157          * @param {Boolean} local True to get the local css position instead of page coordinate
8158          * @return {Number}
8159          */
8160         getBottom : function(local){
8161             if(!local){
8162                 return this.getY() + this.getHeight();
8163             }else{
8164                 return (this.getTop(true) + this.getHeight()) || 0;
8165             }
8166         },
8167
8168         /**
8169         * Initializes positioning on this element. If a desired position is not passed, it will make the
8170         * the element positioned relative IF it is not already positioned.
8171         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8172         * @param {Number} zIndex (optional) The zIndex to apply
8173         * @param {Number} x (optional) Set the page X position
8174         * @param {Number} y (optional) Set the page Y position
8175         */
8176         position : function(pos, zIndex, x, y){
8177             if(!pos){
8178                if(this.getStyle('position') == 'static'){
8179                    this.setStyle('position', 'relative');
8180                }
8181             }else{
8182                 this.setStyle("position", pos);
8183             }
8184             if(zIndex){
8185                 this.setStyle("z-index", zIndex);
8186             }
8187             if(x !== undefined && y !== undefined){
8188                 this.setXY([x, y]);
8189             }else if(x !== undefined){
8190                 this.setX(x);
8191             }else if(y !== undefined){
8192                 this.setY(y);
8193             }
8194         },
8195
8196         /**
8197         * Clear positioning back to the default when the document was loaded
8198         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8199         * @return {Roo.Element} this
8200          */
8201         clearPositioning : function(value){
8202             value = value ||'';
8203             this.setStyle({
8204                 "left": value,
8205                 "right": value,
8206                 "top": value,
8207                 "bottom": value,
8208                 "z-index": "",
8209                 "position" : "static"
8210             });
8211             return this;
8212         },
8213
8214         /**
8215         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8216         * snapshot before performing an update and then restoring the element.
8217         * @return {Object}
8218         */
8219         getPositioning : function(){
8220             var l = this.getStyle("left");
8221             var t = this.getStyle("top");
8222             return {
8223                 "position" : this.getStyle("position"),
8224                 "left" : l,
8225                 "right" : l ? "" : this.getStyle("right"),
8226                 "top" : t,
8227                 "bottom" : t ? "" : this.getStyle("bottom"),
8228                 "z-index" : this.getStyle("z-index")
8229             };
8230         },
8231
8232         /**
8233          * Gets the width of the border(s) for the specified side(s)
8234          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8235          * passing lr would get the border (l)eft width + the border (r)ight width.
8236          * @return {Number} The width of the sides passed added together
8237          */
8238         getBorderWidth : function(side){
8239             return this.addStyles(side, El.borders);
8240         },
8241
8242         /**
8243          * Gets the width of the padding(s) for the specified side(s)
8244          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8245          * passing lr would get the padding (l)eft + the padding (r)ight.
8246          * @return {Number} The padding of the sides passed added together
8247          */
8248         getPadding : function(side){
8249             return this.addStyles(side, El.paddings);
8250         },
8251
8252         /**
8253         * Set positioning with an object returned by getPositioning().
8254         * @param {Object} posCfg
8255         * @return {Roo.Element} this
8256          */
8257         setPositioning : function(pc){
8258             this.applyStyles(pc);
8259             if(pc.right == "auto"){
8260                 this.dom.style.right = "";
8261             }
8262             if(pc.bottom == "auto"){
8263                 this.dom.style.bottom = "";
8264             }
8265             return this;
8266         },
8267
8268         // private
8269         fixDisplay : function(){
8270             if(this.getStyle("display") == "none"){
8271                 this.setStyle("visibility", "hidden");
8272                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8273                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8274                     this.setStyle("display", "block");
8275                 }
8276             }
8277         },
8278
8279         /**
8280          * Quick set left and top adding default units
8281          * @param {String} left The left CSS property value
8282          * @param {String} top The top CSS property value
8283          * @return {Roo.Element} this
8284          */
8285          setLeftTop : function(left, top){
8286             this.dom.style.left = this.addUnits(left);
8287             this.dom.style.top = this.addUnits(top);
8288             return this;
8289         },
8290
8291         /**
8292          * Move this element relative to its current position.
8293          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8294          * @param {Number} distance How far to move the element in pixels
8295          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8296          * @return {Roo.Element} this
8297          */
8298          move : function(direction, distance, animate){
8299             var xy = this.getXY();
8300             direction = direction.toLowerCase();
8301             switch(direction){
8302                 case "l":
8303                 case "left":
8304                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8305                     break;
8306                case "r":
8307                case "right":
8308                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8309                     break;
8310                case "t":
8311                case "top":
8312                case "up":
8313                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8314                     break;
8315                case "b":
8316                case "bottom":
8317                case "down":
8318                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8319                     break;
8320             }
8321             return this;
8322         },
8323
8324         /**
8325          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8326          * @return {Roo.Element} this
8327          */
8328         clip : function(){
8329             if(!this.isClipped){
8330                this.isClipped = true;
8331                this.originalClip = {
8332                    "o": this.getStyle("overflow"),
8333                    "x": this.getStyle("overflow-x"),
8334                    "y": this.getStyle("overflow-y")
8335                };
8336                this.setStyle("overflow", "hidden");
8337                this.setStyle("overflow-x", "hidden");
8338                this.setStyle("overflow-y", "hidden");
8339             }
8340             return this;
8341         },
8342
8343         /**
8344          *  Return clipping (overflow) to original clipping before clip() was called
8345          * @return {Roo.Element} this
8346          */
8347         unclip : function(){
8348             if(this.isClipped){
8349                 this.isClipped = false;
8350                 var o = this.originalClip;
8351                 if(o.o){this.setStyle("overflow", o.o);}
8352                 if(o.x){this.setStyle("overflow-x", o.x);}
8353                 if(o.y){this.setStyle("overflow-y", o.y);}
8354             }
8355             return this;
8356         },
8357
8358
8359         /**
8360          * Gets the x,y coordinates specified by the anchor position on the element.
8361          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8362          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8363          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8364          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8365          * @return {Array} [x, y] An array containing the element's x and y coordinates
8366          */
8367         getAnchorXY : function(anchor, local, s){
8368             //Passing a different size is useful for pre-calculating anchors,
8369             //especially for anchored animations that change the el size.
8370
8371             var w, h, vp = false;
8372             if(!s){
8373                 var d = this.dom;
8374                 if(d == document.body || d == document){
8375                     vp = true;
8376                     w = D.getViewWidth(); h = D.getViewHeight();
8377                 }else{
8378                     w = this.getWidth(); h = this.getHeight();
8379                 }
8380             }else{
8381                 w = s.width;  h = s.height;
8382             }
8383             var x = 0, y = 0, r = Math.round;
8384             switch((anchor || "tl").toLowerCase()){
8385                 case "c":
8386                     x = r(w*.5);
8387                     y = r(h*.5);
8388                 break;
8389                 case "t":
8390                     x = r(w*.5);
8391                     y = 0;
8392                 break;
8393                 case "l":
8394                     x = 0;
8395                     y = r(h*.5);
8396                 break;
8397                 case "r":
8398                     x = w;
8399                     y = r(h*.5);
8400                 break;
8401                 case "b":
8402                     x = r(w*.5);
8403                     y = h;
8404                 break;
8405                 case "tl":
8406                     x = 0;
8407                     y = 0;
8408                 break;
8409                 case "bl":
8410                     x = 0;
8411                     y = h;
8412                 break;
8413                 case "br":
8414                     x = w;
8415                     y = h;
8416                 break;
8417                 case "tr":
8418                     x = w;
8419                     y = 0;
8420                 break;
8421             }
8422             if(local === true){
8423                 return [x, y];
8424             }
8425             if(vp){
8426                 var sc = this.getScroll();
8427                 return [x + sc.left, y + sc.top];
8428             }
8429             //Add the element's offset xy
8430             var o = this.getXY();
8431             return [x+o[0], y+o[1]];
8432         },
8433
8434         /**
8435          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8436          * supported position values.
8437          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8438          * @param {String} position The position to align to.
8439          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8440          * @return {Array} [x, y]
8441          */
8442         getAlignToXY : function(el, p, o){
8443             el = Roo.get(el);
8444             var d = this.dom;
8445             if(!el.dom){
8446                 throw "Element.alignTo with an element that doesn't exist";
8447             }
8448             var c = false; //constrain to viewport
8449             var p1 = "", p2 = "";
8450             o = o || [0,0];
8451
8452             if(!p){
8453                 p = "tl-bl";
8454             }else if(p == "?"){
8455                 p = "tl-bl?";
8456             }else if(p.indexOf("-") == -1){
8457                 p = "tl-" + p;
8458             }
8459             p = p.toLowerCase();
8460             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8461             if(!m){
8462                throw "Element.alignTo with an invalid alignment " + p;
8463             }
8464             p1 = m[1]; p2 = m[2]; c = !!m[3];
8465
8466             //Subtract the aligned el's internal xy from the target's offset xy
8467             //plus custom offset to get the aligned el's new offset xy
8468             var a1 = this.getAnchorXY(p1, true);
8469             var a2 = el.getAnchorXY(p2, false);
8470             var x = a2[0] - a1[0] + o[0];
8471             var y = a2[1] - a1[1] + o[1];
8472             if(c){
8473                 //constrain the aligned el to viewport if necessary
8474                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8475                 // 5px of margin for ie
8476                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8477
8478                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8479                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8480                 //otherwise swap the aligned el to the opposite border of the target.
8481                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8482                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8483                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8484                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8485
8486                var doc = document;
8487                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8488                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8489
8490                if((x+w) > dw + scrollX){
8491                     x = swapX ? r.left-w : dw+scrollX-w;
8492                 }
8493                if(x < scrollX){
8494                    x = swapX ? r.right : scrollX;
8495                }
8496                if((y+h) > dh + scrollY){
8497                     y = swapY ? r.top-h : dh+scrollY-h;
8498                 }
8499                if (y < scrollY){
8500                    y = swapY ? r.bottom : scrollY;
8501                }
8502             }
8503             return [x,y];
8504         },
8505
8506         // private
8507         getConstrainToXY : function(){
8508             var os = {top:0, left:0, bottom:0, right: 0};
8509
8510             return function(el, local, offsets, proposedXY){
8511                 el = Roo.get(el);
8512                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8513
8514                 var vw, vh, vx = 0, vy = 0;
8515                 if(el.dom == document.body || el.dom == document){
8516                     vw = Roo.lib.Dom.getViewWidth();
8517                     vh = Roo.lib.Dom.getViewHeight();
8518                 }else{
8519                     vw = el.dom.clientWidth;
8520                     vh = el.dom.clientHeight;
8521                     if(!local){
8522                         var vxy = el.getXY();
8523                         vx = vxy[0];
8524                         vy = vxy[1];
8525                     }
8526                 }
8527
8528                 var s = el.getScroll();
8529
8530                 vx += offsets.left + s.left;
8531                 vy += offsets.top + s.top;
8532
8533                 vw -= offsets.right;
8534                 vh -= offsets.bottom;
8535
8536                 var vr = vx+vw;
8537                 var vb = vy+vh;
8538
8539                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8540                 var x = xy[0], y = xy[1];
8541                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8542
8543                 // only move it if it needs it
8544                 var moved = false;
8545
8546                 // first validate right/bottom
8547                 if((x + w) > vr){
8548                     x = vr - w;
8549                     moved = true;
8550                 }
8551                 if((y + h) > vb){
8552                     y = vb - h;
8553                     moved = true;
8554                 }
8555                 // then make sure top/left isn't negative
8556                 if(x < vx){
8557                     x = vx;
8558                     moved = true;
8559                 }
8560                 if(y < vy){
8561                     y = vy;
8562                     moved = true;
8563                 }
8564                 return moved ? [x, y] : false;
8565             };
8566         }(),
8567
8568         // private
8569         adjustForConstraints : function(xy, parent, offsets){
8570             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8571         },
8572
8573         /**
8574          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8575          * document it aligns it to the viewport.
8576          * The position parameter is optional, and can be specified in any one of the following formats:
8577          * <ul>
8578          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8579          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8580          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8581          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8582          *   <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
8583          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8584          * </ul>
8585          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8586          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8587          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8588          * that specified in order to enforce the viewport constraints.
8589          * Following are all of the supported anchor positions:
8590     <pre>
8591     Value  Description
8592     -----  -----------------------------
8593     tl     The top left corner (default)
8594     t      The center of the top edge
8595     tr     The top right corner
8596     l      The center of the left edge
8597     c      In the center of the element
8598     r      The center of the right edge
8599     bl     The bottom left corner
8600     b      The center of the bottom edge
8601     br     The bottom right corner
8602     </pre>
8603     Example Usage:
8604     <pre><code>
8605     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8606     el.alignTo("other-el");
8607
8608     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8609     el.alignTo("other-el", "tr?");
8610
8611     // align the bottom right corner of el with the center left edge of other-el
8612     el.alignTo("other-el", "br-l?");
8613
8614     // align the center of el with the bottom left corner of other-el and
8615     // adjust the x position by -6 pixels (and the y position by 0)
8616     el.alignTo("other-el", "c-bl", [-6, 0]);
8617     </code></pre>
8618          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8619          * @param {String} position The position to align to.
8620          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8621          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8622          * @return {Roo.Element} this
8623          */
8624         alignTo : function(element, position, offsets, animate){
8625             var xy = this.getAlignToXY(element, position, offsets);
8626             this.setXY(xy, this.preanim(arguments, 3));
8627             return this;
8628         },
8629
8630         /**
8631          * Anchors an element to another element and realigns it when the window is resized.
8632          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8633          * @param {String} position The position to align to.
8634          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8635          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8636          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8637          * is a number, it is used as the buffer delay (defaults to 50ms).
8638          * @param {Function} callback The function to call after the animation finishes
8639          * @return {Roo.Element} this
8640          */
8641         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8642             var action = function(){
8643                 this.alignTo(el, alignment, offsets, animate);
8644                 Roo.callback(callback, this);
8645             };
8646             Roo.EventManager.onWindowResize(action, this);
8647             var tm = typeof monitorScroll;
8648             if(tm != 'undefined'){
8649                 Roo.EventManager.on(window, 'scroll', action, this,
8650                     {buffer: tm == 'number' ? monitorScroll : 50});
8651             }
8652             action.call(this); // align immediately
8653             return this;
8654         },
8655         /**
8656          * Clears any opacity settings from this element. Required in some cases for IE.
8657          * @return {Roo.Element} this
8658          */
8659         clearOpacity : function(){
8660             if (window.ActiveXObject) {
8661                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8662                     this.dom.style.filter = "";
8663                 }
8664             } else {
8665                 this.dom.style.opacity = "";
8666                 this.dom.style["-moz-opacity"] = "";
8667                 this.dom.style["-khtml-opacity"] = "";
8668             }
8669             return this;
8670         },
8671
8672         /**
8673          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8674          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8675          * @return {Roo.Element} this
8676          */
8677         hide : function(animate){
8678             this.setVisible(false, this.preanim(arguments, 0));
8679             return this;
8680         },
8681
8682         /**
8683         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8684         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8685          * @return {Roo.Element} this
8686          */
8687         show : function(animate){
8688             this.setVisible(true, this.preanim(arguments, 0));
8689             return this;
8690         },
8691
8692         /**
8693          * @private Test if size has a unit, otherwise appends the default
8694          */
8695         addUnits : function(size){
8696             return Roo.Element.addUnits(size, this.defaultUnit);
8697         },
8698
8699         /**
8700          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8701          * @return {Roo.Element} this
8702          */
8703         beginMeasure : function(){
8704             var el = this.dom;
8705             if(el.offsetWidth || el.offsetHeight){
8706                 return this; // offsets work already
8707             }
8708             var changed = [];
8709             var p = this.dom, b = document.body; // start with this element
8710             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8711                 var pe = Roo.get(p);
8712                 if(pe.getStyle('display') == 'none'){
8713                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8714                     p.style.visibility = "hidden";
8715                     p.style.display = "block";
8716                 }
8717                 p = p.parentNode;
8718             }
8719             this._measureChanged = changed;
8720             return this;
8721
8722         },
8723
8724         /**
8725          * Restores displays to before beginMeasure was called
8726          * @return {Roo.Element} this
8727          */
8728         endMeasure : function(){
8729             var changed = this._measureChanged;
8730             if(changed){
8731                 for(var i = 0, len = changed.length; i < len; i++) {
8732                     var r = changed[i];
8733                     r.el.style.visibility = r.visibility;
8734                     r.el.style.display = "none";
8735                 }
8736                 this._measureChanged = null;
8737             }
8738             return this;
8739         },
8740
8741         /**
8742         * Update the innerHTML of this element, optionally searching for and processing scripts
8743         * @param {String} html The new HTML
8744         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8745         * @param {Function} callback For async script loading you can be noticed when the update completes
8746         * @return {Roo.Element} this
8747          */
8748         update : function(html, loadScripts, callback){
8749             if(typeof html == "undefined"){
8750                 html = "";
8751             }
8752             if(loadScripts !== true){
8753                 this.dom.innerHTML = html;
8754                 if(typeof callback == "function"){
8755                     callback();
8756                 }
8757                 return this;
8758             }
8759             var id = Roo.id();
8760             var dom = this.dom;
8761
8762             html += '<span id="' + id + '"></span>';
8763
8764             E.onAvailable(id, function(){
8765                 var hd = document.getElementsByTagName("head")[0];
8766                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8767                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8768                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8769
8770                 var match;
8771                 while(match = re.exec(html)){
8772                     var attrs = match[1];
8773                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8774                     if(srcMatch && srcMatch[2]){
8775                        var s = document.createElement("script");
8776                        s.src = srcMatch[2];
8777                        var typeMatch = attrs.match(typeRe);
8778                        if(typeMatch && typeMatch[2]){
8779                            s.type = typeMatch[2];
8780                        }
8781                        hd.appendChild(s);
8782                     }else if(match[2] && match[2].length > 0){
8783                         if(window.execScript) {
8784                            window.execScript(match[2]);
8785                         } else {
8786                             /**
8787                              * eval:var:id
8788                              * eval:var:dom
8789                              * eval:var:html
8790                              * 
8791                              */
8792                            window.eval(match[2]);
8793                         }
8794                     }
8795                 }
8796                 var el = document.getElementById(id);
8797                 if(el){el.parentNode.removeChild(el);}
8798                 if(typeof callback == "function"){
8799                     callback();
8800                 }
8801             });
8802             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8803             return this;
8804         },
8805
8806         /**
8807          * Direct access to the UpdateManager update() method (takes the same parameters).
8808          * @param {String/Function} url The url for this request or a function to call to get the url
8809          * @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}
8810          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8811          * @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.
8812          * @return {Roo.Element} this
8813          */
8814         load : function(){
8815             var um = this.getUpdateManager();
8816             um.update.apply(um, arguments);
8817             return this;
8818         },
8819
8820         /**
8821         * Gets this element's UpdateManager
8822         * @return {Roo.UpdateManager} The UpdateManager
8823         */
8824         getUpdateManager : function(){
8825             if(!this.updateManager){
8826                 this.updateManager = new Roo.UpdateManager(this);
8827             }
8828             return this.updateManager;
8829         },
8830
8831         /**
8832          * Disables text selection for this element (normalized across browsers)
8833          * @return {Roo.Element} this
8834          */
8835         unselectable : function(){
8836             this.dom.unselectable = "on";
8837             this.swallowEvent("selectstart", true);
8838             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8839             this.addClass("x-unselectable");
8840             return this;
8841         },
8842
8843         /**
8844         * Calculates the x, y to center this element on the screen
8845         * @return {Array} The x, y values [x, y]
8846         */
8847         getCenterXY : function(){
8848             return this.getAlignToXY(document, 'c-c');
8849         },
8850
8851         /**
8852         * Centers the Element in either the viewport, or another Element.
8853         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8854         */
8855         center : function(centerIn){
8856             this.alignTo(centerIn || document, 'c-c');
8857             return this;
8858         },
8859
8860         /**
8861          * Tests various css rules/browsers to determine if this element uses a border box
8862          * @return {Boolean}
8863          */
8864         isBorderBox : function(){
8865             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8866         },
8867
8868         /**
8869          * Return a box {x, y, width, height} that can be used to set another elements
8870          * size/location to match this element.
8871          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8872          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8873          * @return {Object} box An object in the format {x, y, width, height}
8874          */
8875         getBox : function(contentBox, local){
8876             var xy;
8877             if(!local){
8878                 xy = this.getXY();
8879             }else{
8880                 var left = parseInt(this.getStyle("left"), 10) || 0;
8881                 var top = parseInt(this.getStyle("top"), 10) || 0;
8882                 xy = [left, top];
8883             }
8884             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8885             if(!contentBox){
8886                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8887             }else{
8888                 var l = this.getBorderWidth("l")+this.getPadding("l");
8889                 var r = this.getBorderWidth("r")+this.getPadding("r");
8890                 var t = this.getBorderWidth("t")+this.getPadding("t");
8891                 var b = this.getBorderWidth("b")+this.getPadding("b");
8892                 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)};
8893             }
8894             bx.right = bx.x + bx.width;
8895             bx.bottom = bx.y + bx.height;
8896             return bx;
8897         },
8898
8899         /**
8900          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8901          for more information about the sides.
8902          * @param {String} sides
8903          * @return {Number}
8904          */
8905         getFrameWidth : function(sides, onlyContentBox){
8906             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8907         },
8908
8909         /**
8910          * 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.
8911          * @param {Object} box The box to fill {x, y, width, height}
8912          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8913          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8914          * @return {Roo.Element} this
8915          */
8916         setBox : function(box, adjust, animate){
8917             var w = box.width, h = box.height;
8918             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8919                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8920                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8921             }
8922             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8923             return this;
8924         },
8925
8926         /**
8927          * Forces the browser to repaint this element
8928          * @return {Roo.Element} this
8929          */
8930          repaint : function(){
8931             var dom = this.dom;
8932             this.addClass("x-repaint");
8933             setTimeout(function(){
8934                 Roo.get(dom).removeClass("x-repaint");
8935             }, 1);
8936             return this;
8937         },
8938
8939         /**
8940          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8941          * then it returns the calculated width of the sides (see getPadding)
8942          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8943          * @return {Object/Number}
8944          */
8945         getMargins : function(side){
8946             if(!side){
8947                 return {
8948                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8949                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8950                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8951                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8952                 };
8953             }else{
8954                 return this.addStyles(side, El.margins);
8955              }
8956         },
8957
8958         // private
8959         addStyles : function(sides, styles){
8960             var val = 0, v, w;
8961             for(var i = 0, len = sides.length; i < len; i++){
8962                 v = this.getStyle(styles[sides.charAt(i)]);
8963                 if(v){
8964                      w = parseInt(v, 10);
8965                      if(w){ val += w; }
8966                 }
8967             }
8968             return val;
8969         },
8970
8971         /**
8972          * Creates a proxy element of this element
8973          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8974          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8975          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8976          * @return {Roo.Element} The new proxy element
8977          */
8978         createProxy : function(config, renderTo, matchBox){
8979             if(renderTo){
8980                 renderTo = Roo.getDom(renderTo);
8981             }else{
8982                 renderTo = document.body;
8983             }
8984             config = typeof config == "object" ?
8985                 config : {tag : "div", cls: config};
8986             var proxy = Roo.DomHelper.append(renderTo, config, true);
8987             if(matchBox){
8988                proxy.setBox(this.getBox());
8989             }
8990             return proxy;
8991         },
8992
8993         /**
8994          * Puts a mask over this element to disable user interaction. Requires core.css.
8995          * This method can only be applied to elements which accept child nodes.
8996          * @param {String} msg (optional) A message to display in the mask
8997          * @param {String} msgCls (optional) A css class to apply to the msg element
8998          * @return {Element} The mask  element
8999          */
9000         mask : function(msg, msgCls)
9001         {
9002             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9003                 this.setStyle("position", "relative");
9004             }
9005             if(!this._mask){
9006                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9007             }
9008             this.addClass("x-masked");
9009             this._mask.setDisplayed(true);
9010             
9011             // we wander
9012             var z = 0;
9013             var dom = this.dom;
9014             while (dom && dom.style) {
9015                 if (!isNaN(parseInt(dom.style.zIndex))) {
9016                     z = Math.max(z, parseInt(dom.style.zIndex));
9017                 }
9018                 dom = dom.parentNode;
9019             }
9020             // if we are masking the body - then it hides everything..
9021             if (this.dom == document.body) {
9022                 z = 1000000;
9023                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9024                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9025             }
9026            
9027             if(typeof msg == 'string'){
9028                 if(!this._maskMsg){
9029                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9030                 }
9031                 var mm = this._maskMsg;
9032                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9033                 if (mm.dom.firstChild) { // weird IE issue?
9034                     mm.dom.firstChild.innerHTML = msg;
9035                 }
9036                 mm.setDisplayed(true);
9037                 mm.center(this);
9038                 mm.setStyle('z-index', z + 102);
9039             }
9040             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9041                 this._mask.setHeight(this.getHeight());
9042             }
9043             this._mask.setStyle('z-index', z + 100);
9044             
9045             return this._mask;
9046         },
9047
9048         /**
9049          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9050          * it is cached for reuse.
9051          */
9052         unmask : function(removeEl){
9053             if(this._mask){
9054                 if(removeEl === true){
9055                     this._mask.remove();
9056                     delete this._mask;
9057                     if(this._maskMsg){
9058                         this._maskMsg.remove();
9059                         delete this._maskMsg;
9060                     }
9061                 }else{
9062                     this._mask.setDisplayed(false);
9063                     if(this._maskMsg){
9064                         this._maskMsg.setDisplayed(false);
9065                     }
9066                 }
9067             }
9068             this.removeClass("x-masked");
9069         },
9070
9071         /**
9072          * Returns true if this element is masked
9073          * @return {Boolean}
9074          */
9075         isMasked : function(){
9076             return this._mask && this._mask.isVisible();
9077         },
9078
9079         /**
9080          * Creates an iframe shim for this element to keep selects and other windowed objects from
9081          * showing through.
9082          * @return {Roo.Element} The new shim element
9083          */
9084         createShim : function(){
9085             var el = document.createElement('iframe');
9086             el.frameBorder = 'no';
9087             el.className = 'roo-shim';
9088             if(Roo.isIE && Roo.isSecure){
9089                 el.src = Roo.SSL_SECURE_URL;
9090             }
9091             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9092             shim.autoBoxAdjust = false;
9093             return shim;
9094         },
9095
9096         /**
9097          * Removes this element from the DOM and deletes it from the cache
9098          */
9099         remove : function(){
9100             if(this.dom.parentNode){
9101                 this.dom.parentNode.removeChild(this.dom);
9102             }
9103             delete El.cache[this.dom.id];
9104         },
9105
9106         /**
9107          * Sets up event handlers to add and remove a css class when the mouse is over this element
9108          * @param {String} className
9109          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9110          * mouseout events for children elements
9111          * @return {Roo.Element} this
9112          */
9113         addClassOnOver : function(className, preventFlicker){
9114             this.on("mouseover", function(){
9115                 Roo.fly(this, '_internal').addClass(className);
9116             }, this.dom);
9117             var removeFn = function(e){
9118                 if(preventFlicker !== true || !e.within(this, true)){
9119                     Roo.fly(this, '_internal').removeClass(className);
9120                 }
9121             };
9122             this.on("mouseout", removeFn, this.dom);
9123             return this;
9124         },
9125
9126         /**
9127          * Sets up event handlers to add and remove a css class when this element has the focus
9128          * @param {String} className
9129          * @return {Roo.Element} this
9130          */
9131         addClassOnFocus : function(className){
9132             this.on("focus", function(){
9133                 Roo.fly(this, '_internal').addClass(className);
9134             }, this.dom);
9135             this.on("blur", function(){
9136                 Roo.fly(this, '_internal').removeClass(className);
9137             }, this.dom);
9138             return this;
9139         },
9140         /**
9141          * 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)
9142          * @param {String} className
9143          * @return {Roo.Element} this
9144          */
9145         addClassOnClick : function(className){
9146             var dom = this.dom;
9147             this.on("mousedown", function(){
9148                 Roo.fly(dom, '_internal').addClass(className);
9149                 var d = Roo.get(document);
9150                 var fn = function(){
9151                     Roo.fly(dom, '_internal').removeClass(className);
9152                     d.removeListener("mouseup", fn);
9153                 };
9154                 d.on("mouseup", fn);
9155             });
9156             return this;
9157         },
9158
9159         /**
9160          * Stops the specified event from bubbling and optionally prevents the default action
9161          * @param {String} eventName
9162          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9163          * @return {Roo.Element} this
9164          */
9165         swallowEvent : function(eventName, preventDefault){
9166             var fn = function(e){
9167                 e.stopPropagation();
9168                 if(preventDefault){
9169                     e.preventDefault();
9170                 }
9171             };
9172             if(eventName instanceof Array){
9173                 for(var i = 0, len = eventName.length; i < len; i++){
9174                      this.on(eventName[i], fn);
9175                 }
9176                 return this;
9177             }
9178             this.on(eventName, fn);
9179             return this;
9180         },
9181
9182         /**
9183          * @private
9184          */
9185       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9186
9187         /**
9188          * Sizes this element to its parent element's dimensions performing
9189          * neccessary box adjustments.
9190          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9191          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9192          * @return {Roo.Element} this
9193          */
9194         fitToParent : function(monitorResize, targetParent) {
9195           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9196           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9197           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9198             return;
9199           }
9200           var p = Roo.get(targetParent || this.dom.parentNode);
9201           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9202           if (monitorResize === true) {
9203             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9204             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9205           }
9206           return this;
9207         },
9208
9209         /**
9210          * Gets the next sibling, skipping text nodes
9211          * @return {HTMLElement} The next sibling or null
9212          */
9213         getNextSibling : function(){
9214             var n = this.dom.nextSibling;
9215             while(n && n.nodeType != 1){
9216                 n = n.nextSibling;
9217             }
9218             return n;
9219         },
9220
9221         /**
9222          * Gets the previous sibling, skipping text nodes
9223          * @return {HTMLElement} The previous sibling or null
9224          */
9225         getPrevSibling : function(){
9226             var n = this.dom.previousSibling;
9227             while(n && n.nodeType != 1){
9228                 n = n.previousSibling;
9229             }
9230             return n;
9231         },
9232
9233
9234         /**
9235          * Appends the passed element(s) to this element
9236          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9237          * @return {Roo.Element} this
9238          */
9239         appendChild: function(el){
9240             el = Roo.get(el);
9241             el.appendTo(this);
9242             return this;
9243         },
9244
9245         /**
9246          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9247          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9248          * automatically generated with the specified attributes.
9249          * @param {HTMLElement} insertBefore (optional) a child element of this element
9250          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9251          * @return {Roo.Element} The new child element
9252          */
9253         createChild: function(config, insertBefore, returnDom){
9254             config = config || {tag:'div'};
9255             if(insertBefore){
9256                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9257             }
9258             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9259         },
9260
9261         /**
9262          * Appends this element to the passed element
9263          * @param {String/HTMLElement/Element} el The new parent element
9264          * @return {Roo.Element} this
9265          */
9266         appendTo: function(el){
9267             el = Roo.getDom(el);
9268             el.appendChild(this.dom);
9269             return this;
9270         },
9271
9272         /**
9273          * Inserts this element before the passed element in the DOM
9274          * @param {String/HTMLElement/Element} el The element to insert before
9275          * @return {Roo.Element} this
9276          */
9277         insertBefore: function(el){
9278             el = Roo.getDom(el);
9279             el.parentNode.insertBefore(this.dom, el);
9280             return this;
9281         },
9282
9283         /**
9284          * Inserts this element after the passed element in the DOM
9285          * @param {String/HTMLElement/Element} el The element to insert after
9286          * @return {Roo.Element} this
9287          */
9288         insertAfter: function(el){
9289             el = Roo.getDom(el);
9290             el.parentNode.insertBefore(this.dom, el.nextSibling);
9291             return this;
9292         },
9293
9294         /**
9295          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9296          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9297          * @return {Roo.Element} The new child
9298          */
9299         insertFirst: function(el, returnDom){
9300             el = el || {};
9301             if(typeof el == 'object' && !el.nodeType){ // dh config
9302                 return this.createChild(el, this.dom.firstChild, returnDom);
9303             }else{
9304                 el = Roo.getDom(el);
9305                 this.dom.insertBefore(el, this.dom.firstChild);
9306                 return !returnDom ? Roo.get(el) : el;
9307             }
9308         },
9309
9310         /**
9311          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9312          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9313          * @param {String} where (optional) 'before' or 'after' defaults to before
9314          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9315          * @return {Roo.Element} the inserted Element
9316          */
9317         insertSibling: function(el, where, returnDom){
9318             where = where ? where.toLowerCase() : 'before';
9319             el = el || {};
9320             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9321
9322             if(typeof el == 'object' && !el.nodeType){ // dh config
9323                 if(where == 'after' && !this.dom.nextSibling){
9324                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9325                 }else{
9326                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9327                 }
9328
9329             }else{
9330                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9331                             where == 'before' ? this.dom : this.dom.nextSibling);
9332                 if(!returnDom){
9333                     rt = Roo.get(rt);
9334                 }
9335             }
9336             return rt;
9337         },
9338
9339         /**
9340          * Creates and wraps this element with another element
9341          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9342          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9343          * @return {HTMLElement/Element} The newly created wrapper element
9344          */
9345         wrap: function(config, returnDom){
9346             if(!config){
9347                 config = {tag: "div"};
9348             }
9349             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9350             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9351             return newEl;
9352         },
9353
9354         /**
9355          * Replaces the passed element with this element
9356          * @param {String/HTMLElement/Element} el The element to replace
9357          * @return {Roo.Element} this
9358          */
9359         replace: function(el){
9360             el = Roo.get(el);
9361             this.insertBefore(el);
9362             el.remove();
9363             return this;
9364         },
9365
9366         /**
9367          * Inserts an html fragment into this element
9368          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9369          * @param {String} html The HTML fragment
9370          * @param {Boolean} returnEl True to return an Roo.Element
9371          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9372          */
9373         insertHtml : function(where, html, returnEl){
9374             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9375             return returnEl ? Roo.get(el) : el;
9376         },
9377
9378         /**
9379          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9380          * @param {Object} o The object with the attributes
9381          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9382          * @return {Roo.Element} this
9383          */
9384         set : function(o, useSet){
9385             var el = this.dom;
9386             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9387             for(var attr in o){
9388                 if(attr == "style" || typeof o[attr] == "function") continue;
9389                 if(attr=="cls"){
9390                     el.className = o["cls"];
9391                 }else{
9392                     if(useSet) el.setAttribute(attr, o[attr]);
9393                     else el[attr] = o[attr];
9394                 }
9395             }
9396             if(o.style){
9397                 Roo.DomHelper.applyStyles(el, o.style);
9398             }
9399             return this;
9400         },
9401
9402         /**
9403          * Convenience method for constructing a KeyMap
9404          * @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:
9405          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9406          * @param {Function} fn The function to call
9407          * @param {Object} scope (optional) The scope of the function
9408          * @return {Roo.KeyMap} The KeyMap created
9409          */
9410         addKeyListener : function(key, fn, scope){
9411             var config;
9412             if(typeof key != "object" || key instanceof Array){
9413                 config = {
9414                     key: key,
9415                     fn: fn,
9416                     scope: scope
9417                 };
9418             }else{
9419                 config = {
9420                     key : key.key,
9421                     shift : key.shift,
9422                     ctrl : key.ctrl,
9423                     alt : key.alt,
9424                     fn: fn,
9425                     scope: scope
9426                 };
9427             }
9428             return new Roo.KeyMap(this, config);
9429         },
9430
9431         /**
9432          * Creates a KeyMap for this element
9433          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9434          * @return {Roo.KeyMap} The KeyMap created
9435          */
9436         addKeyMap : function(config){
9437             return new Roo.KeyMap(this, config);
9438         },
9439
9440         /**
9441          * Returns true if this element is scrollable.
9442          * @return {Boolean}
9443          */
9444          isScrollable : function(){
9445             var dom = this.dom;
9446             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9447         },
9448
9449         /**
9450          * 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().
9451          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9452          * @param {Number} value The new scroll value
9453          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9454          * @return {Element} this
9455          */
9456
9457         scrollTo : function(side, value, animate){
9458             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9459             if(!animate || !A){
9460                 this.dom[prop] = value;
9461             }else{
9462                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9463                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9464             }
9465             return this;
9466         },
9467
9468         /**
9469          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9470          * within this element's scrollable range.
9471          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9472          * @param {Number} distance How far to scroll the element in pixels
9473          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9474          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9475          * was scrolled as far as it could go.
9476          */
9477          scroll : function(direction, distance, animate){
9478              if(!this.isScrollable()){
9479                  return;
9480              }
9481              var el = this.dom;
9482              var l = el.scrollLeft, t = el.scrollTop;
9483              var w = el.scrollWidth, h = el.scrollHeight;
9484              var cw = el.clientWidth, ch = el.clientHeight;
9485              direction = direction.toLowerCase();
9486              var scrolled = false;
9487              var a = this.preanim(arguments, 2);
9488              switch(direction){
9489                  case "l":
9490                  case "left":
9491                      if(w - l > cw){
9492                          var v = Math.min(l + distance, w-cw);
9493                          this.scrollTo("left", v, a);
9494                          scrolled = true;
9495                      }
9496                      break;
9497                 case "r":
9498                 case "right":
9499                      if(l > 0){
9500                          var v = Math.max(l - distance, 0);
9501                          this.scrollTo("left", v, a);
9502                          scrolled = true;
9503                      }
9504                      break;
9505                 case "t":
9506                 case "top":
9507                 case "up":
9508                      if(t > 0){
9509                          var v = Math.max(t - distance, 0);
9510                          this.scrollTo("top", v, a);
9511                          scrolled = true;
9512                      }
9513                      break;
9514                 case "b":
9515                 case "bottom":
9516                 case "down":
9517                      if(h - t > ch){
9518                          var v = Math.min(t + distance, h-ch);
9519                          this.scrollTo("top", v, a);
9520                          scrolled = true;
9521                      }
9522                      break;
9523              }
9524              return scrolled;
9525         },
9526
9527         /**
9528          * Translates the passed page coordinates into left/top css values for this element
9529          * @param {Number/Array} x The page x or an array containing [x, y]
9530          * @param {Number} y The page y
9531          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9532          */
9533         translatePoints : function(x, y){
9534             if(typeof x == 'object' || x instanceof Array){
9535                 y = x[1]; x = x[0];
9536             }
9537             var p = this.getStyle('position');
9538             var o = this.getXY();
9539
9540             var l = parseInt(this.getStyle('left'), 10);
9541             var t = parseInt(this.getStyle('top'), 10);
9542
9543             if(isNaN(l)){
9544                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9545             }
9546             if(isNaN(t)){
9547                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9548             }
9549
9550             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9551         },
9552
9553         /**
9554          * Returns the current scroll position of the element.
9555          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9556          */
9557         getScroll : function(){
9558             var d = this.dom, doc = document;
9559             if(d == doc || d == doc.body){
9560                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9561                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9562                 return {left: l, top: t};
9563             }else{
9564                 return {left: d.scrollLeft, top: d.scrollTop};
9565             }
9566         },
9567
9568         /**
9569          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9570          * are convert to standard 6 digit hex color.
9571          * @param {String} attr The css attribute
9572          * @param {String} defaultValue The default value to use when a valid color isn't found
9573          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9574          * YUI color anims.
9575          */
9576         getColor : function(attr, defaultValue, prefix){
9577             var v = this.getStyle(attr);
9578             if(!v || v == "transparent" || v == "inherit") {
9579                 return defaultValue;
9580             }
9581             var color = typeof prefix == "undefined" ? "#" : prefix;
9582             if(v.substr(0, 4) == "rgb("){
9583                 var rvs = v.slice(4, v.length -1).split(",");
9584                 for(var i = 0; i < 3; i++){
9585                     var h = parseInt(rvs[i]).toString(16);
9586                     if(h < 16){
9587                         h = "0" + h;
9588                     }
9589                     color += h;
9590                 }
9591             } else {
9592                 if(v.substr(0, 1) == "#"){
9593                     if(v.length == 4) {
9594                         for(var i = 1; i < 4; i++){
9595                             var c = v.charAt(i);
9596                             color +=  c + c;
9597                         }
9598                     }else if(v.length == 7){
9599                         color += v.substr(1);
9600                     }
9601                 }
9602             }
9603             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9604         },
9605
9606         /**
9607          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9608          * gradient background, rounded corners and a 4-way shadow.
9609          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9610          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9611          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9612          * @return {Roo.Element} this
9613          */
9614         boxWrap : function(cls){
9615             cls = cls || 'x-box';
9616             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9617             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9618             return el;
9619         },
9620
9621         /**
9622          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9623          * @param {String} namespace The namespace in which to look for the attribute
9624          * @param {String} name The attribute name
9625          * @return {String} The attribute value
9626          */
9627         getAttributeNS : Roo.isIE ? function(ns, name){
9628             var d = this.dom;
9629             var type = typeof d[ns+":"+name];
9630             if(type != 'undefined' && type != 'unknown'){
9631                 return d[ns+":"+name];
9632             }
9633             return d[name];
9634         } : function(ns, name){
9635             var d = this.dom;
9636             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9637         },
9638         
9639         
9640         /**
9641          * Sets or Returns the value the dom attribute value
9642          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9643          * @param {String} value (optional) The value to set the attribute to
9644          * @return {String} The attribute value
9645          */
9646         attr : function(name){
9647             if (arguments.length > 1) {
9648                 this.dom.setAttribute(name, arguments[1]);
9649                 return arguments[1];
9650             }
9651             if (typeof(name) == 'object') {
9652                 for(var i in name) {
9653                     this.attr(i, name[i]);
9654                 }
9655                 return name;
9656             }
9657             
9658             
9659             if (!this.dom.hasAttribute(name)) {
9660                 return undefined;
9661             }
9662             return this.dom.getAttribute(name);
9663         }
9664         
9665         
9666         
9667     };
9668
9669     var ep = El.prototype;
9670
9671     /**
9672      * Appends an event handler (Shorthand for addListener)
9673      * @param {String}   eventName     The type of event to append
9674      * @param {Function} fn        The method the event invokes
9675      * @param {Object} scope       (optional) The scope (this object) of the fn
9676      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9677      * @method
9678      */
9679     ep.on = ep.addListener;
9680         // backwards compat
9681     ep.mon = ep.addListener;
9682
9683     /**
9684      * Removes an event handler from this element (shorthand for removeListener)
9685      * @param {String} eventName the type of event to remove
9686      * @param {Function} fn the method the event invokes
9687      * @return {Roo.Element} this
9688      * @method
9689      */
9690     ep.un = ep.removeListener;
9691
9692     /**
9693      * true to automatically adjust width and height settings for box-model issues (default to true)
9694      */
9695     ep.autoBoxAdjust = true;
9696
9697     // private
9698     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9699
9700     // private
9701     El.addUnits = function(v, defaultUnit){
9702         if(v === "" || v == "auto"){
9703             return v;
9704         }
9705         if(v === undefined){
9706             return '';
9707         }
9708         if(typeof v == "number" || !El.unitPattern.test(v)){
9709             return v + (defaultUnit || 'px');
9710         }
9711         return v;
9712     };
9713
9714     // special markup used throughout Roo when box wrapping elements
9715     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>';
9716     /**
9717      * Visibility mode constant - Use visibility to hide element
9718      * @static
9719      * @type Number
9720      */
9721     El.VISIBILITY = 1;
9722     /**
9723      * Visibility mode constant - Use display to hide element
9724      * @static
9725      * @type Number
9726      */
9727     El.DISPLAY = 2;
9728
9729     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9730     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9731     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9732
9733
9734
9735     /**
9736      * @private
9737      */
9738     El.cache = {};
9739
9740     var docEl;
9741
9742     /**
9743      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9744      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9745      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9746      * @return {Element} The Element object
9747      * @static
9748      */
9749     El.get = function(el){
9750         var ex, elm, id;
9751         if(!el){ return null; }
9752         if(typeof el == "string"){ // element id
9753             if(!(elm = document.getElementById(el))){
9754                 return null;
9755             }
9756             if(ex = El.cache[el]){
9757                 ex.dom = elm;
9758             }else{
9759                 ex = El.cache[el] = new El(elm);
9760             }
9761             return ex;
9762         }else if(el.tagName){ // dom element
9763             if(!(id = el.id)){
9764                 id = Roo.id(el);
9765             }
9766             if(ex = El.cache[id]){
9767                 ex.dom = el;
9768             }else{
9769                 ex = El.cache[id] = new El(el);
9770             }
9771             return ex;
9772         }else if(el instanceof El){
9773             if(el != docEl){
9774                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9775                                                               // catch case where it hasn't been appended
9776                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9777             }
9778             return el;
9779         }else if(el.isComposite){
9780             return el;
9781         }else if(el instanceof Array){
9782             return El.select(el);
9783         }else if(el == document){
9784             // create a bogus element object representing the document object
9785             if(!docEl){
9786                 var f = function(){};
9787                 f.prototype = El.prototype;
9788                 docEl = new f();
9789                 docEl.dom = document;
9790             }
9791             return docEl;
9792         }
9793         return null;
9794     };
9795
9796     // private
9797     El.uncache = function(el){
9798         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9799             if(a[i]){
9800                 delete El.cache[a[i].id || a[i]];
9801             }
9802         }
9803     };
9804
9805     // private
9806     // Garbage collection - uncache elements/purge listeners on orphaned elements
9807     // so we don't hold a reference and cause the browser to retain them
9808     El.garbageCollect = function(){
9809         if(!Roo.enableGarbageCollector){
9810             clearInterval(El.collectorThread);
9811             return;
9812         }
9813         for(var eid in El.cache){
9814             var el = El.cache[eid], d = el.dom;
9815             // -------------------------------------------------------
9816             // Determining what is garbage:
9817             // -------------------------------------------------------
9818             // !d
9819             // dom node is null, definitely garbage
9820             // -------------------------------------------------------
9821             // !d.parentNode
9822             // no parentNode == direct orphan, definitely garbage
9823             // -------------------------------------------------------
9824             // !d.offsetParent && !document.getElementById(eid)
9825             // display none elements have no offsetParent so we will
9826             // also try to look it up by it's id. However, check
9827             // offsetParent first so we don't do unneeded lookups.
9828             // This enables collection of elements that are not orphans
9829             // directly, but somewhere up the line they have an orphan
9830             // parent.
9831             // -------------------------------------------------------
9832             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9833                 delete El.cache[eid];
9834                 if(d && Roo.enableListenerCollection){
9835                     E.purgeElement(d);
9836                 }
9837             }
9838         }
9839     }
9840     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9841
9842
9843     // dom is optional
9844     El.Flyweight = function(dom){
9845         this.dom = dom;
9846     };
9847     El.Flyweight.prototype = El.prototype;
9848
9849     El._flyweights = {};
9850     /**
9851      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9852      * the dom node can be overwritten by other code.
9853      * @param {String/HTMLElement} el The dom node or id
9854      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9855      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9856      * @static
9857      * @return {Element} The shared Element object
9858      */
9859     El.fly = function(el, named){
9860         named = named || '_global';
9861         el = Roo.getDom(el);
9862         if(!el){
9863             return null;
9864         }
9865         if(!El._flyweights[named]){
9866             El._flyweights[named] = new El.Flyweight();
9867         }
9868         El._flyweights[named].dom = el;
9869         return El._flyweights[named];
9870     };
9871
9872     /**
9873      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9874      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9875      * Shorthand of {@link Roo.Element#get}
9876      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9877      * @return {Element} The Element object
9878      * @member Roo
9879      * @method get
9880      */
9881     Roo.get = El.get;
9882     /**
9883      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9884      * the dom node can be overwritten by other code.
9885      * Shorthand of {@link Roo.Element#fly}
9886      * @param {String/HTMLElement} el The dom node or id
9887      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9888      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9889      * @static
9890      * @return {Element} The shared Element object
9891      * @member Roo
9892      * @method fly
9893      */
9894     Roo.fly = El.fly;
9895
9896     // speedy lookup for elements never to box adjust
9897     var noBoxAdjust = Roo.isStrict ? {
9898         select:1
9899     } : {
9900         input:1, select:1, textarea:1
9901     };
9902     if(Roo.isIE || Roo.isGecko){
9903         noBoxAdjust['button'] = 1;
9904     }
9905
9906
9907     Roo.EventManager.on(window, 'unload', function(){
9908         delete El.cache;
9909         delete El._flyweights;
9910     });
9911 })();
9912
9913
9914
9915
9916 if(Roo.DomQuery){
9917     Roo.Element.selectorFunction = Roo.DomQuery.select;
9918 }
9919
9920 Roo.Element.select = function(selector, unique, root){
9921     var els;
9922     if(typeof selector == "string"){
9923         els = Roo.Element.selectorFunction(selector, root);
9924     }else if(selector.length !== undefined){
9925         els = selector;
9926     }else{
9927         throw "Invalid selector";
9928     }
9929     if(unique === true){
9930         return new Roo.CompositeElement(els);
9931     }else{
9932         return new Roo.CompositeElementLite(els);
9933     }
9934 };
9935 /**
9936  * Selects elements based on the passed CSS selector to enable working on them as 1.
9937  * @param {String/Array} selector The CSS selector or an array of elements
9938  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9939  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9940  * @return {CompositeElementLite/CompositeElement}
9941  * @member Roo
9942  * @method select
9943  */
9944 Roo.select = Roo.Element.select;
9945
9946
9947
9948
9949
9950
9951
9952
9953
9954
9955
9956
9957
9958
9959 /*
9960  * Based on:
9961  * Ext JS Library 1.1.1
9962  * Copyright(c) 2006-2007, Ext JS, LLC.
9963  *
9964  * Originally Released Under LGPL - original licence link has changed is not relivant.
9965  *
9966  * Fork - LGPL
9967  * <script type="text/javascript">
9968  */
9969
9970
9971
9972 //Notifies Element that fx methods are available
9973 Roo.enableFx = true;
9974
9975 /**
9976  * @class Roo.Fx
9977  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9978  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9979  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9980  * Element effects to work.</p><br/>
9981  *
9982  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9983  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9984  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9985  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9986  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9987  * expected results and should be done with care.</p><br/>
9988  *
9989  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9990  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9991 <pre>
9992 Value  Description
9993 -----  -----------------------------
9994 tl     The top left corner
9995 t      The center of the top edge
9996 tr     The top right corner
9997 l      The center of the left edge
9998 r      The center of the right edge
9999 bl     The bottom left corner
10000 b      The center of the bottom edge
10001 br     The bottom right corner
10002 </pre>
10003  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10004  * below are common options that can be passed to any Fx method.</b>
10005  * @cfg {Function} callback A function called when the effect is finished
10006  * @cfg {Object} scope The scope of the effect function
10007  * @cfg {String} easing A valid Easing value for the effect
10008  * @cfg {String} afterCls A css class to apply after the effect
10009  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10010  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10011  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10012  * effects that end with the element being visually hidden, ignored otherwise)
10013  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10014  * a function which returns such a specification that will be applied to the Element after the effect finishes
10015  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10016  * @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
10017  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10018  */
10019 Roo.Fx = {
10020         /**
10021          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10022          * origin for the slide effect.  This function automatically handles wrapping the element with
10023          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10024          * Usage:
10025          *<pre><code>
10026 // default: slide the element in from the top
10027 el.slideIn();
10028
10029 // custom: slide the element in from the right with a 2-second duration
10030 el.slideIn('r', { duration: 2 });
10031
10032 // common config options shown with default values
10033 el.slideIn('t', {
10034     easing: 'easeOut',
10035     duration: .5
10036 });
10037 </code></pre>
10038          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10039          * @param {Object} options (optional) Object literal with any of the Fx config options
10040          * @return {Roo.Element} The Element
10041          */
10042     slideIn : function(anchor, o){
10043         var el = this.getFxEl();
10044         o = o || {};
10045
10046         el.queueFx(o, function(){
10047
10048             anchor = anchor || "t";
10049
10050             // fix display to visibility
10051             this.fixDisplay();
10052
10053             // restore values after effect
10054             var r = this.getFxRestore();
10055             var b = this.getBox();
10056             // fixed size for slide
10057             this.setSize(b);
10058
10059             // wrap if needed
10060             var wrap = this.fxWrap(r.pos, o, "hidden");
10061
10062             var st = this.dom.style;
10063             st.visibility = "visible";
10064             st.position = "absolute";
10065
10066             // clear out temp styles after slide and unwrap
10067             var after = function(){
10068                 el.fxUnwrap(wrap, r.pos, o);
10069                 st.width = r.width;
10070                 st.height = r.height;
10071                 el.afterFx(o);
10072             };
10073             // time to calc the positions
10074             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10075
10076             switch(anchor.toLowerCase()){
10077                 case "t":
10078                     wrap.setSize(b.width, 0);
10079                     st.left = st.bottom = "0";
10080                     a = {height: bh};
10081                 break;
10082                 case "l":
10083                     wrap.setSize(0, b.height);
10084                     st.right = st.top = "0";
10085                     a = {width: bw};
10086                 break;
10087                 case "r":
10088                     wrap.setSize(0, b.height);
10089                     wrap.setX(b.right);
10090                     st.left = st.top = "0";
10091                     a = {width: bw, points: pt};
10092                 break;
10093                 case "b":
10094                     wrap.setSize(b.width, 0);
10095                     wrap.setY(b.bottom);
10096                     st.left = st.top = "0";
10097                     a = {height: bh, points: pt};
10098                 break;
10099                 case "tl":
10100                     wrap.setSize(0, 0);
10101                     st.right = st.bottom = "0";
10102                     a = {width: bw, height: bh};
10103                 break;
10104                 case "bl":
10105                     wrap.setSize(0, 0);
10106                     wrap.setY(b.y+b.height);
10107                     st.right = st.top = "0";
10108                     a = {width: bw, height: bh, points: pt};
10109                 break;
10110                 case "br":
10111                     wrap.setSize(0, 0);
10112                     wrap.setXY([b.right, b.bottom]);
10113                     st.left = st.top = "0";
10114                     a = {width: bw, height: bh, points: pt};
10115                 break;
10116                 case "tr":
10117                     wrap.setSize(0, 0);
10118                     wrap.setX(b.x+b.width);
10119                     st.left = st.bottom = "0";
10120                     a = {width: bw, height: bh, points: pt};
10121                 break;
10122             }
10123             this.dom.style.visibility = "visible";
10124             wrap.show();
10125
10126             arguments.callee.anim = wrap.fxanim(a,
10127                 o,
10128                 'motion',
10129                 .5,
10130                 'easeOut', after);
10131         });
10132         return this;
10133     },
10134     
10135         /**
10136          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10137          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10138          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10139          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10140          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10141          * Usage:
10142          *<pre><code>
10143 // default: slide the element out to the top
10144 el.slideOut();
10145
10146 // custom: slide the element out to the right with a 2-second duration
10147 el.slideOut('r', { duration: 2 });
10148
10149 // common config options shown with default values
10150 el.slideOut('t', {
10151     easing: 'easeOut',
10152     duration: .5,
10153     remove: false,
10154     useDisplay: false
10155 });
10156 </code></pre>
10157          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10158          * @param {Object} options (optional) Object literal with any of the Fx config options
10159          * @return {Roo.Element} The Element
10160          */
10161     slideOut : function(anchor, o){
10162         var el = this.getFxEl();
10163         o = o || {};
10164
10165         el.queueFx(o, function(){
10166
10167             anchor = anchor || "t";
10168
10169             // restore values after effect
10170             var r = this.getFxRestore();
10171             
10172             var b = this.getBox();
10173             // fixed size for slide
10174             this.setSize(b);
10175
10176             // wrap if needed
10177             var wrap = this.fxWrap(r.pos, o, "visible");
10178
10179             var st = this.dom.style;
10180             st.visibility = "visible";
10181             st.position = "absolute";
10182
10183             wrap.setSize(b);
10184
10185             var after = function(){
10186                 if(o.useDisplay){
10187                     el.setDisplayed(false);
10188                 }else{
10189                     el.hide();
10190                 }
10191
10192                 el.fxUnwrap(wrap, r.pos, o);
10193
10194                 st.width = r.width;
10195                 st.height = r.height;
10196
10197                 el.afterFx(o);
10198             };
10199
10200             var a, zero = {to: 0};
10201             switch(anchor.toLowerCase()){
10202                 case "t":
10203                     st.left = st.bottom = "0";
10204                     a = {height: zero};
10205                 break;
10206                 case "l":
10207                     st.right = st.top = "0";
10208                     a = {width: zero};
10209                 break;
10210                 case "r":
10211                     st.left = st.top = "0";
10212                     a = {width: zero, points: {to:[b.right, b.y]}};
10213                 break;
10214                 case "b":
10215                     st.left = st.top = "0";
10216                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10217                 break;
10218                 case "tl":
10219                     st.right = st.bottom = "0";
10220                     a = {width: zero, height: zero};
10221                 break;
10222                 case "bl":
10223                     st.right = st.top = "0";
10224                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10225                 break;
10226                 case "br":
10227                     st.left = st.top = "0";
10228                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10229                 break;
10230                 case "tr":
10231                     st.left = st.bottom = "0";
10232                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10233                 break;
10234             }
10235
10236             arguments.callee.anim = wrap.fxanim(a,
10237                 o,
10238                 'motion',
10239                 .5,
10240                 "easeOut", after);
10241         });
10242         return this;
10243     },
10244
10245         /**
10246          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10247          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10248          * The element must be removed from the DOM using the 'remove' config option if desired.
10249          * Usage:
10250          *<pre><code>
10251 // default
10252 el.puff();
10253
10254 // common config options shown with default values
10255 el.puff({
10256     easing: 'easeOut',
10257     duration: .5,
10258     remove: false,
10259     useDisplay: false
10260 });
10261 </code></pre>
10262          * @param {Object} options (optional) Object literal with any of the Fx config options
10263          * @return {Roo.Element} The Element
10264          */
10265     puff : function(o){
10266         var el = this.getFxEl();
10267         o = o || {};
10268
10269         el.queueFx(o, function(){
10270             this.clearOpacity();
10271             this.show();
10272
10273             // restore values after effect
10274             var r = this.getFxRestore();
10275             var st = this.dom.style;
10276
10277             var after = function(){
10278                 if(o.useDisplay){
10279                     el.setDisplayed(false);
10280                 }else{
10281                     el.hide();
10282                 }
10283
10284                 el.clearOpacity();
10285
10286                 el.setPositioning(r.pos);
10287                 st.width = r.width;
10288                 st.height = r.height;
10289                 st.fontSize = '';
10290                 el.afterFx(o);
10291             };
10292
10293             var width = this.getWidth();
10294             var height = this.getHeight();
10295
10296             arguments.callee.anim = this.fxanim({
10297                     width : {to: this.adjustWidth(width * 2)},
10298                     height : {to: this.adjustHeight(height * 2)},
10299                     points : {by: [-(width * .5), -(height * .5)]},
10300                     opacity : {to: 0},
10301                     fontSize: {to:200, unit: "%"}
10302                 },
10303                 o,
10304                 'motion',
10305                 .5,
10306                 "easeOut", after);
10307         });
10308         return this;
10309     },
10310
10311         /**
10312          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10313          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10314          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10315          * Usage:
10316          *<pre><code>
10317 // default
10318 el.switchOff();
10319
10320 // all config options shown with default values
10321 el.switchOff({
10322     easing: 'easeIn',
10323     duration: .3,
10324     remove: false,
10325     useDisplay: false
10326 });
10327 </code></pre>
10328          * @param {Object} options (optional) Object literal with any of the Fx config options
10329          * @return {Roo.Element} The Element
10330          */
10331     switchOff : function(o){
10332         var el = this.getFxEl();
10333         o = o || {};
10334
10335         el.queueFx(o, function(){
10336             this.clearOpacity();
10337             this.clip();
10338
10339             // restore values after effect
10340             var r = this.getFxRestore();
10341             var st = this.dom.style;
10342
10343             var after = function(){
10344                 if(o.useDisplay){
10345                     el.setDisplayed(false);
10346                 }else{
10347                     el.hide();
10348                 }
10349
10350                 el.clearOpacity();
10351                 el.setPositioning(r.pos);
10352                 st.width = r.width;
10353                 st.height = r.height;
10354
10355                 el.afterFx(o);
10356             };
10357
10358             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10359                 this.clearOpacity();
10360                 (function(){
10361                     this.fxanim({
10362                         height:{to:1},
10363                         points:{by:[0, this.getHeight() * .5]}
10364                     }, o, 'motion', 0.3, 'easeIn', after);
10365                 }).defer(100, this);
10366             });
10367         });
10368         return this;
10369     },
10370
10371     /**
10372      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10373      * changed using the "attr" config option) and then fading back to the original color. If no original
10374      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10375      * Usage:
10376 <pre><code>
10377 // default: highlight background to yellow
10378 el.highlight();
10379
10380 // custom: highlight foreground text to blue for 2 seconds
10381 el.highlight("0000ff", { attr: 'color', duration: 2 });
10382
10383 // common config options shown with default values
10384 el.highlight("ffff9c", {
10385     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10386     endColor: (current color) or "ffffff",
10387     easing: 'easeIn',
10388     duration: 1
10389 });
10390 </code></pre>
10391      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10392      * @param {Object} options (optional) Object literal with any of the Fx config options
10393      * @return {Roo.Element} The Element
10394      */ 
10395     highlight : function(color, o){
10396         var el = this.getFxEl();
10397         o = o || {};
10398
10399         el.queueFx(o, function(){
10400             color = color || "ffff9c";
10401             attr = o.attr || "backgroundColor";
10402
10403             this.clearOpacity();
10404             this.show();
10405
10406             var origColor = this.getColor(attr);
10407             var restoreColor = this.dom.style[attr];
10408             endColor = (o.endColor || origColor) || "ffffff";
10409
10410             var after = function(){
10411                 el.dom.style[attr] = restoreColor;
10412                 el.afterFx(o);
10413             };
10414
10415             var a = {};
10416             a[attr] = {from: color, to: endColor};
10417             arguments.callee.anim = this.fxanim(a,
10418                 o,
10419                 'color',
10420                 1,
10421                 'easeIn', after);
10422         });
10423         return this;
10424     },
10425
10426    /**
10427     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10428     * Usage:
10429 <pre><code>
10430 // default: a single light blue ripple
10431 el.frame();
10432
10433 // custom: 3 red ripples lasting 3 seconds total
10434 el.frame("ff0000", 3, { duration: 3 });
10435
10436 // common config options shown with default values
10437 el.frame("C3DAF9", 1, {
10438     duration: 1 //duration of entire animation (not each individual ripple)
10439     // Note: Easing is not configurable and will be ignored if included
10440 });
10441 </code></pre>
10442     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10443     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10444     * @param {Object} options (optional) Object literal with any of the Fx config options
10445     * @return {Roo.Element} The Element
10446     */
10447     frame : function(color, count, o){
10448         var el = this.getFxEl();
10449         o = o || {};
10450
10451         el.queueFx(o, function(){
10452             color = color || "#C3DAF9";
10453             if(color.length == 6){
10454                 color = "#" + color;
10455             }
10456             count = count || 1;
10457             duration = o.duration || 1;
10458             this.show();
10459
10460             var b = this.getBox();
10461             var animFn = function(){
10462                 var proxy = this.createProxy({
10463
10464                      style:{
10465                         visbility:"hidden",
10466                         position:"absolute",
10467                         "z-index":"35000", // yee haw
10468                         border:"0px solid " + color
10469                      }
10470                   });
10471                 var scale = Roo.isBorderBox ? 2 : 1;
10472                 proxy.animate({
10473                     top:{from:b.y, to:b.y - 20},
10474                     left:{from:b.x, to:b.x - 20},
10475                     borderWidth:{from:0, to:10},
10476                     opacity:{from:1, to:0},
10477                     height:{from:b.height, to:(b.height + (20*scale))},
10478                     width:{from:b.width, to:(b.width + (20*scale))}
10479                 }, duration, function(){
10480                     proxy.remove();
10481                 });
10482                 if(--count > 0){
10483                      animFn.defer((duration/2)*1000, this);
10484                 }else{
10485                     el.afterFx(o);
10486                 }
10487             };
10488             animFn.call(this);
10489         });
10490         return this;
10491     },
10492
10493    /**
10494     * Creates a pause before any subsequent queued effects begin.  If there are
10495     * no effects queued after the pause it will have no effect.
10496     * Usage:
10497 <pre><code>
10498 el.pause(1);
10499 </code></pre>
10500     * @param {Number} seconds The length of time to pause (in seconds)
10501     * @return {Roo.Element} The Element
10502     */
10503     pause : function(seconds){
10504         var el = this.getFxEl();
10505         var o = {};
10506
10507         el.queueFx(o, function(){
10508             setTimeout(function(){
10509                 el.afterFx(o);
10510             }, seconds * 1000);
10511         });
10512         return this;
10513     },
10514
10515    /**
10516     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10517     * using the "endOpacity" config option.
10518     * Usage:
10519 <pre><code>
10520 // default: fade in from opacity 0 to 100%
10521 el.fadeIn();
10522
10523 // custom: fade in from opacity 0 to 75% over 2 seconds
10524 el.fadeIn({ endOpacity: .75, duration: 2});
10525
10526 // common config options shown with default values
10527 el.fadeIn({
10528     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10529     easing: 'easeOut',
10530     duration: .5
10531 });
10532 </code></pre>
10533     * @param {Object} options (optional) Object literal with any of the Fx config options
10534     * @return {Roo.Element} The Element
10535     */
10536     fadeIn : function(o){
10537         var el = this.getFxEl();
10538         o = o || {};
10539         el.queueFx(o, function(){
10540             this.setOpacity(0);
10541             this.fixDisplay();
10542             this.dom.style.visibility = 'visible';
10543             var to = o.endOpacity || 1;
10544             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10545                 o, null, .5, "easeOut", function(){
10546                 if(to == 1){
10547                     this.clearOpacity();
10548                 }
10549                 el.afterFx(o);
10550             });
10551         });
10552         return this;
10553     },
10554
10555    /**
10556     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10557     * using the "endOpacity" config option.
10558     * Usage:
10559 <pre><code>
10560 // default: fade out from the element's current opacity to 0
10561 el.fadeOut();
10562
10563 // custom: fade out from the element's current opacity to 25% over 2 seconds
10564 el.fadeOut({ endOpacity: .25, duration: 2});
10565
10566 // common config options shown with default values
10567 el.fadeOut({
10568     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10569     easing: 'easeOut',
10570     duration: .5
10571     remove: false,
10572     useDisplay: false
10573 });
10574 </code></pre>
10575     * @param {Object} options (optional) Object literal with any of the Fx config options
10576     * @return {Roo.Element} The Element
10577     */
10578     fadeOut : function(o){
10579         var el = this.getFxEl();
10580         o = o || {};
10581         el.queueFx(o, function(){
10582             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10583                 o, null, .5, "easeOut", function(){
10584                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10585                      this.dom.style.display = "none";
10586                 }else{
10587                      this.dom.style.visibility = "hidden";
10588                 }
10589                 this.clearOpacity();
10590                 el.afterFx(o);
10591             });
10592         });
10593         return this;
10594     },
10595
10596    /**
10597     * Animates the transition of an element's dimensions from a starting height/width
10598     * to an ending height/width.
10599     * Usage:
10600 <pre><code>
10601 // change height and width to 100x100 pixels
10602 el.scale(100, 100);
10603
10604 // common config options shown with default values.  The height and width will default to
10605 // the element's existing values if passed as null.
10606 el.scale(
10607     [element's width],
10608     [element's height], {
10609     easing: 'easeOut',
10610     duration: .35
10611 });
10612 </code></pre>
10613     * @param {Number} width  The new width (pass undefined to keep the original width)
10614     * @param {Number} height  The new height (pass undefined to keep the original height)
10615     * @param {Object} options (optional) Object literal with any of the Fx config options
10616     * @return {Roo.Element} The Element
10617     */
10618     scale : function(w, h, o){
10619         this.shift(Roo.apply({}, o, {
10620             width: w,
10621             height: h
10622         }));
10623         return this;
10624     },
10625
10626    /**
10627     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10628     * Any of these properties not specified in the config object will not be changed.  This effect 
10629     * requires that at least one new dimension, position or opacity setting must be passed in on
10630     * the config object in order for the function to have any effect.
10631     * Usage:
10632 <pre><code>
10633 // slide the element horizontally to x position 200 while changing the height and opacity
10634 el.shift({ x: 200, height: 50, opacity: .8 });
10635
10636 // common config options shown with default values.
10637 el.shift({
10638     width: [element's width],
10639     height: [element's height],
10640     x: [element's x position],
10641     y: [element's y position],
10642     opacity: [element's opacity],
10643     easing: 'easeOut',
10644     duration: .35
10645 });
10646 </code></pre>
10647     * @param {Object} options  Object literal with any of the Fx config options
10648     * @return {Roo.Element} The Element
10649     */
10650     shift : function(o){
10651         var el = this.getFxEl();
10652         o = o || {};
10653         el.queueFx(o, function(){
10654             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10655             if(w !== undefined){
10656                 a.width = {to: this.adjustWidth(w)};
10657             }
10658             if(h !== undefined){
10659                 a.height = {to: this.adjustHeight(h)};
10660             }
10661             if(x !== undefined || y !== undefined){
10662                 a.points = {to: [
10663                     x !== undefined ? x : this.getX(),
10664                     y !== undefined ? y : this.getY()
10665                 ]};
10666             }
10667             if(op !== undefined){
10668                 a.opacity = {to: op};
10669             }
10670             if(o.xy !== undefined){
10671                 a.points = {to: o.xy};
10672             }
10673             arguments.callee.anim = this.fxanim(a,
10674                 o, 'motion', .35, "easeOut", function(){
10675                 el.afterFx(o);
10676             });
10677         });
10678         return this;
10679     },
10680
10681         /**
10682          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10683          * ending point of the effect.
10684          * Usage:
10685          *<pre><code>
10686 // default: slide the element downward while fading out
10687 el.ghost();
10688
10689 // custom: slide the element out to the right with a 2-second duration
10690 el.ghost('r', { duration: 2 });
10691
10692 // common config options shown with default values
10693 el.ghost('b', {
10694     easing: 'easeOut',
10695     duration: .5
10696     remove: false,
10697     useDisplay: false
10698 });
10699 </code></pre>
10700          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10701          * @param {Object} options (optional) Object literal with any of the Fx config options
10702          * @return {Roo.Element} The Element
10703          */
10704     ghost : function(anchor, o){
10705         var el = this.getFxEl();
10706         o = o || {};
10707
10708         el.queueFx(o, function(){
10709             anchor = anchor || "b";
10710
10711             // restore values after effect
10712             var r = this.getFxRestore();
10713             var w = this.getWidth(),
10714                 h = this.getHeight();
10715
10716             var st = this.dom.style;
10717
10718             var after = function(){
10719                 if(o.useDisplay){
10720                     el.setDisplayed(false);
10721                 }else{
10722                     el.hide();
10723                 }
10724
10725                 el.clearOpacity();
10726                 el.setPositioning(r.pos);
10727                 st.width = r.width;
10728                 st.height = r.height;
10729
10730                 el.afterFx(o);
10731             };
10732
10733             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10734             switch(anchor.toLowerCase()){
10735                 case "t":
10736                     pt.by = [0, -h];
10737                 break;
10738                 case "l":
10739                     pt.by = [-w, 0];
10740                 break;
10741                 case "r":
10742                     pt.by = [w, 0];
10743                 break;
10744                 case "b":
10745                     pt.by = [0, h];
10746                 break;
10747                 case "tl":
10748                     pt.by = [-w, -h];
10749                 break;
10750                 case "bl":
10751                     pt.by = [-w, h];
10752                 break;
10753                 case "br":
10754                     pt.by = [w, h];
10755                 break;
10756                 case "tr":
10757                     pt.by = [w, -h];
10758                 break;
10759             }
10760
10761             arguments.callee.anim = this.fxanim(a,
10762                 o,
10763                 'motion',
10764                 .5,
10765                 "easeOut", after);
10766         });
10767         return this;
10768     },
10769
10770         /**
10771          * Ensures that all effects queued after syncFx is called on the element are
10772          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10773          * @return {Roo.Element} The Element
10774          */
10775     syncFx : function(){
10776         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10777             block : false,
10778             concurrent : true,
10779             stopFx : false
10780         });
10781         return this;
10782     },
10783
10784         /**
10785          * Ensures that all effects queued after sequenceFx is called on the element are
10786          * run in sequence.  This is the opposite of {@link #syncFx}.
10787          * @return {Roo.Element} The Element
10788          */
10789     sequenceFx : function(){
10790         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10791             block : false,
10792             concurrent : false,
10793             stopFx : false
10794         });
10795         return this;
10796     },
10797
10798         /* @private */
10799     nextFx : function(){
10800         var ef = this.fxQueue[0];
10801         if(ef){
10802             ef.call(this);
10803         }
10804     },
10805
10806         /**
10807          * Returns true if the element has any effects actively running or queued, else returns false.
10808          * @return {Boolean} True if element has active effects, else false
10809          */
10810     hasActiveFx : function(){
10811         return this.fxQueue && this.fxQueue[0];
10812     },
10813
10814         /**
10815          * Stops any running effects and clears the element's internal effects queue if it contains
10816          * any additional effects that haven't started yet.
10817          * @return {Roo.Element} The Element
10818          */
10819     stopFx : function(){
10820         if(this.hasActiveFx()){
10821             var cur = this.fxQueue[0];
10822             if(cur && cur.anim && cur.anim.isAnimated()){
10823                 this.fxQueue = [cur]; // clear out others
10824                 cur.anim.stop(true);
10825             }
10826         }
10827         return this;
10828     },
10829
10830         /* @private */
10831     beforeFx : function(o){
10832         if(this.hasActiveFx() && !o.concurrent){
10833            if(o.stopFx){
10834                this.stopFx();
10835                return true;
10836            }
10837            return false;
10838         }
10839         return true;
10840     },
10841
10842         /**
10843          * Returns true if the element is currently blocking so that no other effect can be queued
10844          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10845          * used to ensure that an effect initiated by a user action runs to completion prior to the
10846          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10847          * @return {Boolean} True if blocking, else false
10848          */
10849     hasFxBlock : function(){
10850         var q = this.fxQueue;
10851         return q && q[0] && q[0].block;
10852     },
10853
10854         /* @private */
10855     queueFx : function(o, fn){
10856         if(!this.fxQueue){
10857             this.fxQueue = [];
10858         }
10859         if(!this.hasFxBlock()){
10860             Roo.applyIf(o, this.fxDefaults);
10861             if(!o.concurrent){
10862                 var run = this.beforeFx(o);
10863                 fn.block = o.block;
10864                 this.fxQueue.push(fn);
10865                 if(run){
10866                     this.nextFx();
10867                 }
10868             }else{
10869                 fn.call(this);
10870             }
10871         }
10872         return this;
10873     },
10874
10875         /* @private */
10876     fxWrap : function(pos, o, vis){
10877         var wrap;
10878         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10879             var wrapXY;
10880             if(o.fixPosition){
10881                 wrapXY = this.getXY();
10882             }
10883             var div = document.createElement("div");
10884             div.style.visibility = vis;
10885             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10886             wrap.setPositioning(pos);
10887             if(wrap.getStyle("position") == "static"){
10888                 wrap.position("relative");
10889             }
10890             this.clearPositioning('auto');
10891             wrap.clip();
10892             wrap.dom.appendChild(this.dom);
10893             if(wrapXY){
10894                 wrap.setXY(wrapXY);
10895             }
10896         }
10897         return wrap;
10898     },
10899
10900         /* @private */
10901     fxUnwrap : function(wrap, pos, o){
10902         this.clearPositioning();
10903         this.setPositioning(pos);
10904         if(!o.wrap){
10905             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10906             wrap.remove();
10907         }
10908     },
10909
10910         /* @private */
10911     getFxRestore : function(){
10912         var st = this.dom.style;
10913         return {pos: this.getPositioning(), width: st.width, height : st.height};
10914     },
10915
10916         /* @private */
10917     afterFx : function(o){
10918         if(o.afterStyle){
10919             this.applyStyles(o.afterStyle);
10920         }
10921         if(o.afterCls){
10922             this.addClass(o.afterCls);
10923         }
10924         if(o.remove === true){
10925             this.remove();
10926         }
10927         Roo.callback(o.callback, o.scope, [this]);
10928         if(!o.concurrent){
10929             this.fxQueue.shift();
10930             this.nextFx();
10931         }
10932     },
10933
10934         /* @private */
10935     getFxEl : function(){ // support for composite element fx
10936         return Roo.get(this.dom);
10937     },
10938
10939         /* @private */
10940     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10941         animType = animType || 'run';
10942         opt = opt || {};
10943         var anim = Roo.lib.Anim[animType](
10944             this.dom, args,
10945             (opt.duration || defaultDur) || .35,
10946             (opt.easing || defaultEase) || 'easeOut',
10947             function(){
10948                 Roo.callback(cb, this);
10949             },
10950             this
10951         );
10952         opt.anim = anim;
10953         return anim;
10954     }
10955 };
10956
10957 // backwords compat
10958 Roo.Fx.resize = Roo.Fx.scale;
10959
10960 //When included, Roo.Fx is automatically applied to Element so that all basic
10961 //effects are available directly via the Element API
10962 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10963  * Based on:
10964  * Ext JS Library 1.1.1
10965  * Copyright(c) 2006-2007, Ext JS, LLC.
10966  *
10967  * Originally Released Under LGPL - original licence link has changed is not relivant.
10968  *
10969  * Fork - LGPL
10970  * <script type="text/javascript">
10971  */
10972
10973
10974 /**
10975  * @class Roo.CompositeElement
10976  * Standard composite class. Creates a Roo.Element for every element in the collection.
10977  * <br><br>
10978  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10979  * actions will be performed on all the elements in this collection.</b>
10980  * <br><br>
10981  * All methods return <i>this</i> and can be chained.
10982  <pre><code>
10983  var els = Roo.select("#some-el div.some-class", true);
10984  // or select directly from an existing element
10985  var el = Roo.get('some-el');
10986  el.select('div.some-class', true);
10987
10988  els.setWidth(100); // all elements become 100 width
10989  els.hide(true); // all elements fade out and hide
10990  // or
10991  els.setWidth(100).hide(true);
10992  </code></pre>
10993  */
10994 Roo.CompositeElement = function(els){
10995     this.elements = [];
10996     this.addElements(els);
10997 };
10998 Roo.CompositeElement.prototype = {
10999     isComposite: true,
11000     addElements : function(els){
11001         if(!els) return this;
11002         if(typeof els == "string"){
11003             els = Roo.Element.selectorFunction(els);
11004         }
11005         var yels = this.elements;
11006         var index = yels.length-1;
11007         for(var i = 0, len = els.length; i < len; i++) {
11008                 yels[++index] = Roo.get(els[i]);
11009         }
11010         return this;
11011     },
11012
11013     /**
11014     * Clears this composite and adds the elements returned by the passed selector.
11015     * @param {String/Array} els A string CSS selector, an array of elements or an element
11016     * @return {CompositeElement} this
11017     */
11018     fill : function(els){
11019         this.elements = [];
11020         this.add(els);
11021         return this;
11022     },
11023
11024     /**
11025     * Filters this composite to only elements that match the passed selector.
11026     * @param {String} selector A string CSS selector
11027     * @param {Boolean} inverse return inverse filter (not matches)
11028     * @return {CompositeElement} this
11029     */
11030     filter : function(selector, inverse){
11031         var els = [];
11032         inverse = inverse || false;
11033         this.each(function(el){
11034             var match = inverse ? !el.is(selector) : el.is(selector);
11035             if(match){
11036                 els[els.length] = el.dom;
11037             }
11038         });
11039         this.fill(els);
11040         return this;
11041     },
11042
11043     invoke : function(fn, args){
11044         var els = this.elements;
11045         for(var i = 0, len = els.length; i < len; i++) {
11046                 Roo.Element.prototype[fn].apply(els[i], args);
11047         }
11048         return this;
11049     },
11050     /**
11051     * Adds elements to this composite.
11052     * @param {String/Array} els A string CSS selector, an array of elements or an element
11053     * @return {CompositeElement} this
11054     */
11055     add : function(els){
11056         if(typeof els == "string"){
11057             this.addElements(Roo.Element.selectorFunction(els));
11058         }else if(els.length !== undefined){
11059             this.addElements(els);
11060         }else{
11061             this.addElements([els]);
11062         }
11063         return this;
11064     },
11065     /**
11066     * Calls the passed function passing (el, this, index) for each element in this composite.
11067     * @param {Function} fn The function to call
11068     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11069     * @return {CompositeElement} this
11070     */
11071     each : function(fn, scope){
11072         var els = this.elements;
11073         for(var i = 0, len = els.length; i < len; i++){
11074             if(fn.call(scope || els[i], els[i], this, i) === false) {
11075                 break;
11076             }
11077         }
11078         return this;
11079     },
11080
11081     /**
11082      * Returns the Element object at the specified index
11083      * @param {Number} index
11084      * @return {Roo.Element}
11085      */
11086     item : function(index){
11087         return this.elements[index] || null;
11088     },
11089
11090     /**
11091      * Returns the first Element
11092      * @return {Roo.Element}
11093      */
11094     first : function(){
11095         return this.item(0);
11096     },
11097
11098     /**
11099      * Returns the last Element
11100      * @return {Roo.Element}
11101      */
11102     last : function(){
11103         return this.item(this.elements.length-1);
11104     },
11105
11106     /**
11107      * Returns the number of elements in this composite
11108      * @return Number
11109      */
11110     getCount : function(){
11111         return this.elements.length;
11112     },
11113
11114     /**
11115      * Returns true if this composite contains the passed element
11116      * @return Boolean
11117      */
11118     contains : function(el){
11119         return this.indexOf(el) !== -1;
11120     },
11121
11122     /**
11123      * Returns true if this composite contains the passed element
11124      * @return Boolean
11125      */
11126     indexOf : function(el){
11127         return this.elements.indexOf(Roo.get(el));
11128     },
11129
11130
11131     /**
11132     * Removes the specified element(s).
11133     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11134     * or an array of any of those.
11135     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11136     * @return {CompositeElement} this
11137     */
11138     removeElement : function(el, removeDom){
11139         if(el instanceof Array){
11140             for(var i = 0, len = el.length; i < len; i++){
11141                 this.removeElement(el[i]);
11142             }
11143             return this;
11144         }
11145         var index = typeof el == 'number' ? el : this.indexOf(el);
11146         if(index !== -1){
11147             if(removeDom){
11148                 var d = this.elements[index];
11149                 if(d.dom){
11150                     d.remove();
11151                 }else{
11152                     d.parentNode.removeChild(d);
11153                 }
11154             }
11155             this.elements.splice(index, 1);
11156         }
11157         return this;
11158     },
11159
11160     /**
11161     * Replaces the specified element with the passed element.
11162     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11163     * to replace.
11164     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11165     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11166     * @return {CompositeElement} this
11167     */
11168     replaceElement : function(el, replacement, domReplace){
11169         var index = typeof el == 'number' ? el : this.indexOf(el);
11170         if(index !== -1){
11171             if(domReplace){
11172                 this.elements[index].replaceWith(replacement);
11173             }else{
11174                 this.elements.splice(index, 1, Roo.get(replacement))
11175             }
11176         }
11177         return this;
11178     },
11179
11180     /**
11181      * Removes all elements.
11182      */
11183     clear : function(){
11184         this.elements = [];
11185     }
11186 };
11187 (function(){
11188     Roo.CompositeElement.createCall = function(proto, fnName){
11189         if(!proto[fnName]){
11190             proto[fnName] = function(){
11191                 return this.invoke(fnName, arguments);
11192             };
11193         }
11194     };
11195     for(var fnName in Roo.Element.prototype){
11196         if(typeof Roo.Element.prototype[fnName] == "function"){
11197             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11198         }
11199     };
11200 })();
11201 /*
11202  * Based on:
11203  * Ext JS Library 1.1.1
11204  * Copyright(c) 2006-2007, Ext JS, LLC.
11205  *
11206  * Originally Released Under LGPL - original licence link has changed is not relivant.
11207  *
11208  * Fork - LGPL
11209  * <script type="text/javascript">
11210  */
11211
11212 /**
11213  * @class Roo.CompositeElementLite
11214  * @extends Roo.CompositeElement
11215  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11216  <pre><code>
11217  var els = Roo.select("#some-el div.some-class");
11218  // or select directly from an existing element
11219  var el = Roo.get('some-el');
11220  el.select('div.some-class');
11221
11222  els.setWidth(100); // all elements become 100 width
11223  els.hide(true); // all elements fade out and hide
11224  // or
11225  els.setWidth(100).hide(true);
11226  </code></pre><br><br>
11227  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11228  * actions will be performed on all the elements in this collection.</b>
11229  */
11230 Roo.CompositeElementLite = function(els){
11231     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11232     this.el = new Roo.Element.Flyweight();
11233 };
11234 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11235     addElements : function(els){
11236         if(els){
11237             if(els instanceof Array){
11238                 this.elements = this.elements.concat(els);
11239             }else{
11240                 var yels = this.elements;
11241                 var index = yels.length-1;
11242                 for(var i = 0, len = els.length; i < len; i++) {
11243                     yels[++index] = els[i];
11244                 }
11245             }
11246         }
11247         return this;
11248     },
11249     invoke : function(fn, args){
11250         var els = this.elements;
11251         var el = this.el;
11252         for(var i = 0, len = els.length; i < len; i++) {
11253             el.dom = els[i];
11254                 Roo.Element.prototype[fn].apply(el, args);
11255         }
11256         return this;
11257     },
11258     /**
11259      * Returns a flyweight Element of the dom element object at the specified index
11260      * @param {Number} index
11261      * @return {Roo.Element}
11262      */
11263     item : function(index){
11264         if(!this.elements[index]){
11265             return null;
11266         }
11267         this.el.dom = this.elements[index];
11268         return this.el;
11269     },
11270
11271     // fixes scope with flyweight
11272     addListener : function(eventName, handler, scope, opt){
11273         var els = this.elements;
11274         for(var i = 0, len = els.length; i < len; i++) {
11275             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11276         }
11277         return this;
11278     },
11279
11280     /**
11281     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11282     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11283     * a reference to the dom node, use el.dom.</b>
11284     * @param {Function} fn The function to call
11285     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11286     * @return {CompositeElement} this
11287     */
11288     each : function(fn, scope){
11289         var els = this.elements;
11290         var el = this.el;
11291         for(var i = 0, len = els.length; i < len; i++){
11292             el.dom = els[i];
11293                 if(fn.call(scope || el, el, this, i) === false){
11294                 break;
11295             }
11296         }
11297         return this;
11298     },
11299
11300     indexOf : function(el){
11301         return this.elements.indexOf(Roo.getDom(el));
11302     },
11303
11304     replaceElement : function(el, replacement, domReplace){
11305         var index = typeof el == 'number' ? el : this.indexOf(el);
11306         if(index !== -1){
11307             replacement = Roo.getDom(replacement);
11308             if(domReplace){
11309                 var d = this.elements[index];
11310                 d.parentNode.insertBefore(replacement, d);
11311                 d.parentNode.removeChild(d);
11312             }
11313             this.elements.splice(index, 1, replacement);
11314         }
11315         return this;
11316     }
11317 });
11318 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11319
11320 /*
11321  * Based on:
11322  * Ext JS Library 1.1.1
11323  * Copyright(c) 2006-2007, Ext JS, LLC.
11324  *
11325  * Originally Released Under LGPL - original licence link has changed is not relivant.
11326  *
11327  * Fork - LGPL
11328  * <script type="text/javascript">
11329  */
11330
11331  
11332
11333 /**
11334  * @class Roo.data.Connection
11335  * @extends Roo.util.Observable
11336  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11337  * either to a configured URL, or to a URL specified at request time.<br><br>
11338  * <p>
11339  * Requests made by this class are asynchronous, and will return immediately. No data from
11340  * the server will be available to the statement immediately following the {@link #request} call.
11341  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11342  * <p>
11343  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11344  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11345  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11346  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11347  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11348  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11349  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11350  * standard DOM methods.
11351  * @constructor
11352  * @param {Object} config a configuration object.
11353  */
11354 Roo.data.Connection = function(config){
11355     Roo.apply(this, config);
11356     this.addEvents({
11357         /**
11358          * @event beforerequest
11359          * Fires before a network request is made to retrieve a data object.
11360          * @param {Connection} conn This Connection object.
11361          * @param {Object} options The options config object passed to the {@link #request} method.
11362          */
11363         "beforerequest" : true,
11364         /**
11365          * @event requestcomplete
11366          * Fires if the request was successfully completed.
11367          * @param {Connection} conn This Connection object.
11368          * @param {Object} response The XHR object containing the response data.
11369          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11370          * @param {Object} options The options config object passed to the {@link #request} method.
11371          */
11372         "requestcomplete" : true,
11373         /**
11374          * @event requestexception
11375          * Fires if an error HTTP status was returned from the server.
11376          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11377          * @param {Connection} conn This Connection object.
11378          * @param {Object} response The XHR object containing the response data.
11379          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11380          * @param {Object} options The options config object passed to the {@link #request} method.
11381          */
11382         "requestexception" : true
11383     });
11384     Roo.data.Connection.superclass.constructor.call(this);
11385 };
11386
11387 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11388     /**
11389      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11390      */
11391     /**
11392      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11393      * extra parameters to each request made by this object. (defaults to undefined)
11394      */
11395     /**
11396      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11397      *  to each request made by this object. (defaults to undefined)
11398      */
11399     /**
11400      * @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)
11401      */
11402     /**
11403      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11404      */
11405     timeout : 30000,
11406     /**
11407      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11408      * @type Boolean
11409      */
11410     autoAbort:false,
11411
11412     /**
11413      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11414      * @type Boolean
11415      */
11416     disableCaching: true,
11417
11418     /**
11419      * Sends an HTTP request to a remote server.
11420      * @param {Object} options An object which may contain the following properties:<ul>
11421      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11422      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11423      * request, a url encoded string or a function to call to get either.</li>
11424      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11425      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11426      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11427      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11428      * <li>options {Object} The parameter to the request call.</li>
11429      * <li>success {Boolean} True if the request succeeded.</li>
11430      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11431      * </ul></li>
11432      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11433      * The callback is passed the following parameters:<ul>
11434      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11435      * <li>options {Object} The parameter to the request call.</li>
11436      * </ul></li>
11437      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11438      * The callback is passed the following parameters:<ul>
11439      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11440      * <li>options {Object} The parameter to the request call.</li>
11441      * </ul></li>
11442      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11443      * for the callback function. Defaults to the browser window.</li>
11444      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11445      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11446      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11447      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11448      * params for the post data. Any params will be appended to the URL.</li>
11449      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11450      * </ul>
11451      * @return {Number} transactionId
11452      */
11453     request : function(o){
11454         if(this.fireEvent("beforerequest", this, o) !== false){
11455             var p = o.params;
11456
11457             if(typeof p == "function"){
11458                 p = p.call(o.scope||window, o);
11459             }
11460             if(typeof p == "object"){
11461                 p = Roo.urlEncode(o.params);
11462             }
11463             if(this.extraParams){
11464                 var extras = Roo.urlEncode(this.extraParams);
11465                 p = p ? (p + '&' + extras) : extras;
11466             }
11467
11468             var url = o.url || this.url;
11469             if(typeof url == 'function'){
11470                 url = url.call(o.scope||window, o);
11471             }
11472
11473             if(o.form){
11474                 var form = Roo.getDom(o.form);
11475                 url = url || form.action;
11476
11477                 var enctype = form.getAttribute("enctype");
11478                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11479                     return this.doFormUpload(o, p, url);
11480                 }
11481                 var f = Roo.lib.Ajax.serializeForm(form);
11482                 p = p ? (p + '&' + f) : f;
11483             }
11484
11485             var hs = o.headers;
11486             if(this.defaultHeaders){
11487                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11488                 if(!o.headers){
11489                     o.headers = hs;
11490                 }
11491             }
11492
11493             var cb = {
11494                 success: this.handleResponse,
11495                 failure: this.handleFailure,
11496                 scope: this,
11497                 argument: {options: o},
11498                 timeout : o.timeout || this.timeout
11499             };
11500
11501             var method = o.method||this.method||(p ? "POST" : "GET");
11502
11503             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11504                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11505             }
11506
11507             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11508                 if(o.autoAbort){
11509                     this.abort();
11510                 }
11511             }else if(this.autoAbort !== false){
11512                 this.abort();
11513             }
11514
11515             if((method == 'GET' && p) || o.xmlData){
11516                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11517                 p = '';
11518             }
11519             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11520             return this.transId;
11521         }else{
11522             Roo.callback(o.callback, o.scope, [o, null, null]);
11523             return null;
11524         }
11525     },
11526
11527     /**
11528      * Determine whether this object has a request outstanding.
11529      * @param {Number} transactionId (Optional) defaults to the last transaction
11530      * @return {Boolean} True if there is an outstanding request.
11531      */
11532     isLoading : function(transId){
11533         if(transId){
11534             return Roo.lib.Ajax.isCallInProgress(transId);
11535         }else{
11536             return this.transId ? true : false;
11537         }
11538     },
11539
11540     /**
11541      * Aborts any outstanding request.
11542      * @param {Number} transactionId (Optional) defaults to the last transaction
11543      */
11544     abort : function(transId){
11545         if(transId || this.isLoading()){
11546             Roo.lib.Ajax.abort(transId || this.transId);
11547         }
11548     },
11549
11550     // private
11551     handleResponse : function(response){
11552         this.transId = false;
11553         var options = response.argument.options;
11554         response.argument = options ? options.argument : null;
11555         this.fireEvent("requestcomplete", this, response, options);
11556         Roo.callback(options.success, options.scope, [response, options]);
11557         Roo.callback(options.callback, options.scope, [options, true, response]);
11558     },
11559
11560     // private
11561     handleFailure : function(response, e){
11562         this.transId = false;
11563         var options = response.argument.options;
11564         response.argument = options ? options.argument : null;
11565         this.fireEvent("requestexception", this, response, options, e);
11566         Roo.callback(options.failure, options.scope, [response, options]);
11567         Roo.callback(options.callback, options.scope, [options, false, response]);
11568     },
11569
11570     // private
11571     doFormUpload : function(o, ps, url){
11572         var id = Roo.id();
11573         var frame = document.createElement('iframe');
11574         frame.id = id;
11575         frame.name = id;
11576         frame.className = 'x-hidden';
11577         if(Roo.isIE){
11578             frame.src = Roo.SSL_SECURE_URL;
11579         }
11580         document.body.appendChild(frame);
11581
11582         if(Roo.isIE){
11583            document.frames[id].name = id;
11584         }
11585
11586         var form = Roo.getDom(o.form);
11587         form.target = id;
11588         form.method = 'POST';
11589         form.enctype = form.encoding = 'multipart/form-data';
11590         if(url){
11591             form.action = url;
11592         }
11593
11594         var hiddens, hd;
11595         if(ps){ // add dynamic params
11596             hiddens = [];
11597             ps = Roo.urlDecode(ps, false);
11598             for(var k in ps){
11599                 if(ps.hasOwnProperty(k)){
11600                     hd = document.createElement('input');
11601                     hd.type = 'hidden';
11602                     hd.name = k;
11603                     hd.value = ps[k];
11604                     form.appendChild(hd);
11605                     hiddens.push(hd);
11606                 }
11607             }
11608         }
11609
11610         function cb(){
11611             var r = {  // bogus response object
11612                 responseText : '',
11613                 responseXML : null
11614             };
11615
11616             r.argument = o ? o.argument : null;
11617
11618             try { //
11619                 var doc;
11620                 if(Roo.isIE){
11621                     doc = frame.contentWindow.document;
11622                 }else {
11623                     doc = (frame.contentDocument || window.frames[id].document);
11624                 }
11625                 if(doc && doc.body){
11626                     r.responseText = doc.body.innerHTML;
11627                 }
11628                 if(doc && doc.XMLDocument){
11629                     r.responseXML = doc.XMLDocument;
11630                 }else {
11631                     r.responseXML = doc;
11632                 }
11633             }
11634             catch(e) {
11635                 // ignore
11636             }
11637
11638             Roo.EventManager.removeListener(frame, 'load', cb, this);
11639
11640             this.fireEvent("requestcomplete", this, r, o);
11641             Roo.callback(o.success, o.scope, [r, o]);
11642             Roo.callback(o.callback, o.scope, [o, true, r]);
11643
11644             setTimeout(function(){document.body.removeChild(frame);}, 100);
11645         }
11646
11647         Roo.EventManager.on(frame, 'load', cb, this);
11648         form.submit();
11649
11650         if(hiddens){ // remove dynamic params
11651             for(var i = 0, len = hiddens.length; i < len; i++){
11652                 form.removeChild(hiddens[i]);
11653             }
11654         }
11655     }
11656 });
11657 /*
11658  * Based on:
11659  * Ext JS Library 1.1.1
11660  * Copyright(c) 2006-2007, Ext JS, LLC.
11661  *
11662  * Originally Released Under LGPL - original licence link has changed is not relivant.
11663  *
11664  * Fork - LGPL
11665  * <script type="text/javascript">
11666  */
11667  
11668 /**
11669  * Global Ajax request class.
11670  * 
11671  * @class Roo.Ajax
11672  * @extends Roo.data.Connection
11673  * @static
11674  * 
11675  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11676  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11677  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11678  * @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)
11679  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11680  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11681  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11682  */
11683 Roo.Ajax = new Roo.data.Connection({
11684     // fix up the docs
11685     /**
11686      * @scope Roo.Ajax
11687      * @type {Boolear} 
11688      */
11689     autoAbort : false,
11690
11691     /**
11692      * Serialize the passed form into a url encoded string
11693      * @scope Roo.Ajax
11694      * @param {String/HTMLElement} form
11695      * @return {String}
11696      */
11697     serializeForm : function(form){
11698         return Roo.lib.Ajax.serializeForm(form);
11699     }
11700 });/*
11701  * Based on:
11702  * Ext JS Library 1.1.1
11703  * Copyright(c) 2006-2007, Ext JS, LLC.
11704  *
11705  * Originally Released Under LGPL - original licence link has changed is not relivant.
11706  *
11707  * Fork - LGPL
11708  * <script type="text/javascript">
11709  */
11710
11711  
11712 /**
11713  * @class Roo.UpdateManager
11714  * @extends Roo.util.Observable
11715  * Provides AJAX-style update for Element object.<br><br>
11716  * Usage:<br>
11717  * <pre><code>
11718  * // Get it from a Roo.Element object
11719  * var el = Roo.get("foo");
11720  * var mgr = el.getUpdateManager();
11721  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11722  * ...
11723  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11724  * <br>
11725  * // or directly (returns the same UpdateManager instance)
11726  * var mgr = new Roo.UpdateManager("myElementId");
11727  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11728  * mgr.on("update", myFcnNeedsToKnow);
11729  * <br>
11730    // short handed call directly from the element object
11731    Roo.get("foo").load({
11732         url: "bar.php",
11733         scripts:true,
11734         params: "for=bar",
11735         text: "Loading Foo..."
11736    });
11737  * </code></pre>
11738  * @constructor
11739  * Create new UpdateManager directly.
11740  * @param {String/HTMLElement/Roo.Element} el The element to update
11741  * @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).
11742  */
11743 Roo.UpdateManager = function(el, forceNew){
11744     el = Roo.get(el);
11745     if(!forceNew && el.updateManager){
11746         return el.updateManager;
11747     }
11748     /**
11749      * The Element object
11750      * @type Roo.Element
11751      */
11752     this.el = el;
11753     /**
11754      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11755      * @type String
11756      */
11757     this.defaultUrl = null;
11758
11759     this.addEvents({
11760         /**
11761          * @event beforeupdate
11762          * Fired before an update is made, return false from your handler and the update is cancelled.
11763          * @param {Roo.Element} el
11764          * @param {String/Object/Function} url
11765          * @param {String/Object} params
11766          */
11767         "beforeupdate": true,
11768         /**
11769          * @event update
11770          * Fired after successful update is made.
11771          * @param {Roo.Element} el
11772          * @param {Object} oResponseObject The response Object
11773          */
11774         "update": true,
11775         /**
11776          * @event failure
11777          * Fired on update failure.
11778          * @param {Roo.Element} el
11779          * @param {Object} oResponseObject The response Object
11780          */
11781         "failure": true
11782     });
11783     var d = Roo.UpdateManager.defaults;
11784     /**
11785      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11786      * @type String
11787      */
11788     this.sslBlankUrl = d.sslBlankUrl;
11789     /**
11790      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11791      * @type Boolean
11792      */
11793     this.disableCaching = d.disableCaching;
11794     /**
11795      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11796      * @type String
11797      */
11798     this.indicatorText = d.indicatorText;
11799     /**
11800      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11801      * @type String
11802      */
11803     this.showLoadIndicator = d.showLoadIndicator;
11804     /**
11805      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11806      * @type Number
11807      */
11808     this.timeout = d.timeout;
11809
11810     /**
11811      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11812      * @type Boolean
11813      */
11814     this.loadScripts = d.loadScripts;
11815
11816     /**
11817      * Transaction object of current executing transaction
11818      */
11819     this.transaction = null;
11820
11821     /**
11822      * @private
11823      */
11824     this.autoRefreshProcId = null;
11825     /**
11826      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11827      * @type Function
11828      */
11829     this.refreshDelegate = this.refresh.createDelegate(this);
11830     /**
11831      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11832      * @type Function
11833      */
11834     this.updateDelegate = this.update.createDelegate(this);
11835     /**
11836      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11837      * @type Function
11838      */
11839     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11840     /**
11841      * @private
11842      */
11843     this.successDelegate = this.processSuccess.createDelegate(this);
11844     /**
11845      * @private
11846      */
11847     this.failureDelegate = this.processFailure.createDelegate(this);
11848
11849     if(!this.renderer){
11850      /**
11851       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11852       */
11853     this.renderer = new Roo.UpdateManager.BasicRenderer();
11854     }
11855     
11856     Roo.UpdateManager.superclass.constructor.call(this);
11857 };
11858
11859 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11860     /**
11861      * Get the Element this UpdateManager is bound to
11862      * @return {Roo.Element} The element
11863      */
11864     getEl : function(){
11865         return this.el;
11866     },
11867     /**
11868      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11869      * @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:
11870 <pre><code>
11871 um.update({<br/>
11872     url: "your-url.php",<br/>
11873     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11874     callback: yourFunction,<br/>
11875     scope: yourObject, //(optional scope)  <br/>
11876     discardUrl: false, <br/>
11877     nocache: false,<br/>
11878     text: "Loading...",<br/>
11879     timeout: 30,<br/>
11880     scripts: false<br/>
11881 });
11882 </code></pre>
11883      * The only required property is url. The optional properties nocache, text and scripts
11884      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11885      * @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}
11886      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11887      * @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.
11888      */
11889     update : function(url, params, callback, discardUrl){
11890         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11891             var method = this.method,
11892                 cfg;
11893             if(typeof url == "object"){ // must be config object
11894                 cfg = url;
11895                 url = cfg.url;
11896                 params = params || cfg.params;
11897                 callback = callback || cfg.callback;
11898                 discardUrl = discardUrl || cfg.discardUrl;
11899                 if(callback && cfg.scope){
11900                     callback = callback.createDelegate(cfg.scope);
11901                 }
11902                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11903                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11904                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11905                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11906                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11907             }
11908             this.showLoading();
11909             if(!discardUrl){
11910                 this.defaultUrl = url;
11911             }
11912             if(typeof url == "function"){
11913                 url = url.call(this);
11914             }
11915
11916             method = method || (params ? "POST" : "GET");
11917             if(method == "GET"){
11918                 url = this.prepareUrl(url);
11919             }
11920
11921             var o = Roo.apply(cfg ||{}, {
11922                 url : url,
11923                 params: params,
11924                 success: this.successDelegate,
11925                 failure: this.failureDelegate,
11926                 callback: undefined,
11927                 timeout: (this.timeout*1000),
11928                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11929             });
11930             Roo.log("updated manager called with timeout of " + o.timeout);
11931             this.transaction = Roo.Ajax.request(o);
11932         }
11933     },
11934
11935     /**
11936      * 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.
11937      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11938      * @param {String/HTMLElement} form The form Id or form element
11939      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11940      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11941      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11942      */
11943     formUpdate : function(form, url, reset, callback){
11944         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11945             if(typeof url == "function"){
11946                 url = url.call(this);
11947             }
11948             form = Roo.getDom(form);
11949             this.transaction = Roo.Ajax.request({
11950                 form: form,
11951                 url:url,
11952                 success: this.successDelegate,
11953                 failure: this.failureDelegate,
11954                 timeout: (this.timeout*1000),
11955                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11956             });
11957             this.showLoading.defer(1, this);
11958         }
11959     },
11960
11961     /**
11962      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11963      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11964      */
11965     refresh : function(callback){
11966         if(this.defaultUrl == null){
11967             return;
11968         }
11969         this.update(this.defaultUrl, null, callback, true);
11970     },
11971
11972     /**
11973      * Set this element to auto refresh.
11974      * @param {Number} interval How often to update (in seconds).
11975      * @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)
11976      * @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}
11977      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11978      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11979      */
11980     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11981         if(refreshNow){
11982             this.update(url || this.defaultUrl, params, callback, true);
11983         }
11984         if(this.autoRefreshProcId){
11985             clearInterval(this.autoRefreshProcId);
11986         }
11987         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11988     },
11989
11990     /**
11991      * Stop auto refresh on this element.
11992      */
11993      stopAutoRefresh : function(){
11994         if(this.autoRefreshProcId){
11995             clearInterval(this.autoRefreshProcId);
11996             delete this.autoRefreshProcId;
11997         }
11998     },
11999
12000     isAutoRefreshing : function(){
12001        return this.autoRefreshProcId ? true : false;
12002     },
12003     /**
12004      * Called to update the element to "Loading" state. Override to perform custom action.
12005      */
12006     showLoading : function(){
12007         if(this.showLoadIndicator){
12008             this.el.update(this.indicatorText);
12009         }
12010     },
12011
12012     /**
12013      * Adds unique parameter to query string if disableCaching = true
12014      * @private
12015      */
12016     prepareUrl : function(url){
12017         if(this.disableCaching){
12018             var append = "_dc=" + (new Date().getTime());
12019             if(url.indexOf("?") !== -1){
12020                 url += "&" + append;
12021             }else{
12022                 url += "?" + append;
12023             }
12024         }
12025         return url;
12026     },
12027
12028     /**
12029      * @private
12030      */
12031     processSuccess : function(response){
12032         this.transaction = null;
12033         if(response.argument.form && response.argument.reset){
12034             try{ // put in try/catch since some older FF releases had problems with this
12035                 response.argument.form.reset();
12036             }catch(e){}
12037         }
12038         if(this.loadScripts){
12039             this.renderer.render(this.el, response, this,
12040                 this.updateComplete.createDelegate(this, [response]));
12041         }else{
12042             this.renderer.render(this.el, response, this);
12043             this.updateComplete(response);
12044         }
12045     },
12046
12047     updateComplete : function(response){
12048         this.fireEvent("update", this.el, response);
12049         if(typeof response.argument.callback == "function"){
12050             response.argument.callback(this.el, true, response);
12051         }
12052     },
12053
12054     /**
12055      * @private
12056      */
12057     processFailure : function(response){
12058         this.transaction = null;
12059         this.fireEvent("failure", this.el, response);
12060         if(typeof response.argument.callback == "function"){
12061             response.argument.callback(this.el, false, response);
12062         }
12063     },
12064
12065     /**
12066      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12067      * @param {Object} renderer The object implementing the render() method
12068      */
12069     setRenderer : function(renderer){
12070         this.renderer = renderer;
12071     },
12072
12073     getRenderer : function(){
12074        return this.renderer;
12075     },
12076
12077     /**
12078      * Set the defaultUrl used for updates
12079      * @param {String/Function} defaultUrl The url or a function to call to get the url
12080      */
12081     setDefaultUrl : function(defaultUrl){
12082         this.defaultUrl = defaultUrl;
12083     },
12084
12085     /**
12086      * Aborts the executing transaction
12087      */
12088     abort : function(){
12089         if(this.transaction){
12090             Roo.Ajax.abort(this.transaction);
12091         }
12092     },
12093
12094     /**
12095      * Returns true if an update is in progress
12096      * @return {Boolean}
12097      */
12098     isUpdating : function(){
12099         if(this.transaction){
12100             return Roo.Ajax.isLoading(this.transaction);
12101         }
12102         return false;
12103     }
12104 });
12105
12106 /**
12107  * @class Roo.UpdateManager.defaults
12108  * @static (not really - but it helps the doc tool)
12109  * The defaults collection enables customizing the default properties of UpdateManager
12110  */
12111    Roo.UpdateManager.defaults = {
12112        /**
12113          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12114          * @type Number
12115          */
12116          timeout : 30,
12117
12118          /**
12119          * True to process scripts by default (Defaults to false).
12120          * @type Boolean
12121          */
12122         loadScripts : false,
12123
12124         /**
12125         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12126         * @type String
12127         */
12128         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12129         /**
12130          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12131          * @type Boolean
12132          */
12133         disableCaching : false,
12134         /**
12135          * Whether to show indicatorText when loading (Defaults to true).
12136          * @type Boolean
12137          */
12138         showLoadIndicator : true,
12139         /**
12140          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12141          * @type String
12142          */
12143         indicatorText : '<div class="loading-indicator">Loading...</div>'
12144    };
12145
12146 /**
12147  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12148  *Usage:
12149  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12150  * @param {String/HTMLElement/Roo.Element} el The element to update
12151  * @param {String} url The url
12152  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12153  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12154  * @static
12155  * @deprecated
12156  * @member Roo.UpdateManager
12157  */
12158 Roo.UpdateManager.updateElement = function(el, url, params, options){
12159     var um = Roo.get(el, true).getUpdateManager();
12160     Roo.apply(um, options);
12161     um.update(url, params, options ? options.callback : null);
12162 };
12163 // alias for backwards compat
12164 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12165 /**
12166  * @class Roo.UpdateManager.BasicRenderer
12167  * Default Content renderer. Updates the elements innerHTML with the responseText.
12168  */
12169 Roo.UpdateManager.BasicRenderer = function(){};
12170
12171 Roo.UpdateManager.BasicRenderer.prototype = {
12172     /**
12173      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12174      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12175      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12176      * @param {Roo.Element} el The element being rendered
12177      * @param {Object} response The YUI Connect response object
12178      * @param {UpdateManager} updateManager The calling update manager
12179      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12180      */
12181      render : function(el, response, updateManager, callback){
12182         el.update(response.responseText, updateManager.loadScripts, callback);
12183     }
12184 };
12185 /*
12186  * Based on:
12187  * Roo JS
12188  * (c)) Alan Knowles
12189  * Licence : LGPL
12190  */
12191
12192
12193 /**
12194  * @class Roo.DomTemplate
12195  * @extends Roo.Template
12196  * An effort at a dom based template engine..
12197  *
12198  * Similar to XTemplate, except it uses dom parsing to create the template..
12199  *
12200  * Supported features:
12201  *
12202  *  Tags:
12203
12204 <pre><code>
12205       {a_variable} - output encoded.
12206       {a_variable.format:("Y-m-d")} - call a method on the variable
12207       {a_variable:raw} - unencoded output
12208       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12209       {a_variable:this.method_on_template(...)} - call a method on the template object.
12210  
12211 </code></pre>
12212  *  The tpl tag:
12213 <pre><code>
12214         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12215         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12216         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12217         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12218   
12219 </code></pre>
12220  *      
12221  */
12222 Roo.DomTemplate = function()
12223 {
12224      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12225      if (this.html) {
12226         this.compile();
12227      }
12228 };
12229
12230
12231 Roo.extend(Roo.DomTemplate, Roo.Template, {
12232     /**
12233      * id counter for sub templates.
12234      */
12235     id : 0,
12236     /**
12237      * flag to indicate if dom parser is inside a pre,
12238      * it will strip whitespace if not.
12239      */
12240     inPre : false,
12241     
12242     /**
12243      * The various sub templates
12244      */
12245     tpls : false,
12246     
12247     
12248     
12249     /**
12250      *
12251      * basic tag replacing syntax
12252      * WORD:WORD()
12253      *
12254      * // you can fake an object call by doing this
12255      *  x.t:(test,tesT) 
12256      * 
12257      */
12258     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12259     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12260     
12261     iterChild : function (node, method) {
12262         
12263         var oldPre = this.inPre;
12264         if (node.tagName == 'PRE') {
12265             this.inPre = true;
12266         }
12267         for( var i = 0; i < node.childNodes.length; i++) {
12268             method.call(this, node.childNodes[i]);
12269         }
12270         this.inPre = oldPre;
12271     },
12272     
12273     
12274     
12275     /**
12276      * compile the template
12277      *
12278      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12279      *
12280      */
12281     compile: function()
12282     {
12283         var s = this.html;
12284         
12285         // covert the html into DOM...
12286         var doc = false;
12287         var div =false;
12288         try {
12289             doc = document.implementation.createHTMLDocument("");
12290             doc.documentElement.innerHTML =   this.html  ;
12291             div = doc.documentElement;
12292         } catch (e) {
12293             // old IE... - nasty -- it causes all sorts of issues.. with
12294             // images getting pulled from server..
12295             div = document.createElement('div');
12296             div.innerHTML = this.html;
12297         }
12298         //doc.documentElement.innerHTML = htmlBody
12299          
12300         
12301         
12302         this.tpls = [];
12303         var _t = this;
12304         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12305         
12306         var tpls = this.tpls;
12307         
12308         // create a top level template from the snippet..
12309         
12310         //Roo.log(div.innerHTML);
12311         
12312         var tpl = {
12313             uid : 'master',
12314             id : this.id++,
12315             attr : false,
12316             value : false,
12317             body : div.innerHTML,
12318             
12319             forCall : false,
12320             execCall : false,
12321             dom : div,
12322             isTop : true
12323             
12324         };
12325         tpls.unshift(tpl);
12326         
12327         
12328         // compile them...
12329         this.tpls = [];
12330         Roo.each(tpls, function(tp){
12331             this.compileTpl(tp);
12332             this.tpls[tp.id] = tp;
12333         }, this);
12334         
12335         this.master = tpls[0];
12336         return this;
12337         
12338         
12339     },
12340     
12341     compileNode : function(node, istop) {
12342         // test for
12343         //Roo.log(node);
12344         
12345         
12346         // skip anything not a tag..
12347         if (node.nodeType != 1) {
12348             if (node.nodeType == 3 && !this.inPre) {
12349                 // reduce white space..
12350                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12351                 
12352             }
12353             return;
12354         }
12355         
12356         var tpl = {
12357             uid : false,
12358             id : false,
12359             attr : false,
12360             value : false,
12361             body : '',
12362             
12363             forCall : false,
12364             execCall : false,
12365             dom : false,
12366             isTop : istop
12367             
12368             
12369         };
12370         
12371         
12372         switch(true) {
12373             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12374             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12375             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12376             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12377             // no default..
12378         }
12379         
12380         
12381         if (!tpl.attr) {
12382             // just itterate children..
12383             this.iterChild(node,this.compileNode);
12384             return;
12385         }
12386         tpl.uid = this.id++;
12387         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12388         node.removeAttribute('roo-'+ tpl.attr);
12389         if (tpl.attr != 'name') {
12390             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12391             node.parentNode.replaceChild(placeholder,  node);
12392         } else {
12393             
12394             var placeholder =  document.createElement('span');
12395             placeholder.className = 'roo-tpl-' + tpl.value;
12396             node.parentNode.replaceChild(placeholder,  node);
12397         }
12398         
12399         // parent now sees '{domtplXXXX}
12400         this.iterChild(node,this.compileNode);
12401         
12402         // we should now have node body...
12403         var div = document.createElement('div');
12404         div.appendChild(node);
12405         tpl.dom = node;
12406         // this has the unfortunate side effect of converting tagged attributes
12407         // eg. href="{...}" into %7C...%7D
12408         // this has been fixed by searching for those combo's although it's a bit hacky..
12409         
12410         
12411         tpl.body = div.innerHTML;
12412         
12413         
12414          
12415         tpl.id = tpl.uid;
12416         switch(tpl.attr) {
12417             case 'for' :
12418                 switch (tpl.value) {
12419                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12420                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12421                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12422                 }
12423                 break;
12424             
12425             case 'exec':
12426                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12427                 break;
12428             
12429             case 'if':     
12430                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12431                 break;
12432             
12433             case 'name':
12434                 tpl.id  = tpl.value; // replace non characters???
12435                 break;
12436             
12437         }
12438         
12439         
12440         this.tpls.push(tpl);
12441         
12442         
12443         
12444     },
12445     
12446     
12447     
12448     
12449     /**
12450      * Compile a segment of the template into a 'sub-template'
12451      *
12452      * 
12453      * 
12454      *
12455      */
12456     compileTpl : function(tpl)
12457     {
12458         var fm = Roo.util.Format;
12459         var useF = this.disableFormats !== true;
12460         
12461         var sep = Roo.isGecko ? "+\n" : ",\n";
12462         
12463         var undef = function(str) {
12464             Roo.debug && Roo.log("Property not found :"  + str);
12465             return '';
12466         };
12467           
12468         //Roo.log(tpl.body);
12469         
12470         
12471         
12472         var fn = function(m, lbrace, name, format, args)
12473         {
12474             //Roo.log("ARGS");
12475             //Roo.log(arguments);
12476             args = args ? args.replace(/\\'/g,"'") : args;
12477             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12478             if (typeof(format) == 'undefined') {
12479                 format =  'htmlEncode'; 
12480             }
12481             if (format == 'raw' ) {
12482                 format = false;
12483             }
12484             
12485             if(name.substr(0, 6) == 'domtpl'){
12486                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12487             }
12488             
12489             // build an array of options to determine if value is undefined..
12490             
12491             // basically get 'xxxx.yyyy' then do
12492             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12493             //    (function () { Roo.log("Property not found"); return ''; })() :
12494             //    ......
12495             
12496             var udef_ar = [];
12497             var lookfor = '';
12498             Roo.each(name.split('.'), function(st) {
12499                 lookfor += (lookfor.length ? '.': '') + st;
12500                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12501             });
12502             
12503             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12504             
12505             
12506             if(format && useF){
12507                 
12508                 args = args ? ',' + args : "";
12509                  
12510                 if(format.substr(0, 5) != "this."){
12511                     format = "fm." + format + '(';
12512                 }else{
12513                     format = 'this.call("'+ format.substr(5) + '", ';
12514                     args = ", values";
12515                 }
12516                 
12517                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12518             }
12519              
12520             if (args && args.length) {
12521                 // called with xxyx.yuu:(test,test)
12522                 // change to ()
12523                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12524             }
12525             // raw.. - :raw modifier..
12526             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12527             
12528         };
12529         var body;
12530         // branched to use + in gecko and [].join() in others
12531         if(Roo.isGecko){
12532             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12533                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12534                     "';};};";
12535         }else{
12536             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12537             body.push(tpl.body.replace(/(\r\n|\n)/g,
12538                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12539             body.push("'].join('');};};");
12540             body = body.join('');
12541         }
12542         
12543         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12544        
12545         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12546         eval(body);
12547         
12548         return this;
12549     },
12550      
12551     /**
12552      * same as applyTemplate, except it's done to one of the subTemplates
12553      * when using named templates, you can do:
12554      *
12555      * var str = pl.applySubTemplate('your-name', values);
12556      *
12557      * 
12558      * @param {Number} id of the template
12559      * @param {Object} values to apply to template
12560      * @param {Object} parent (normaly the instance of this object)
12561      */
12562     applySubTemplate : function(id, values, parent)
12563     {
12564         
12565         
12566         var t = this.tpls[id];
12567         
12568         
12569         try { 
12570             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12571                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12572                 return '';
12573             }
12574         } catch(e) {
12575             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12576             Roo.log(values);
12577           
12578             return '';
12579         }
12580         try { 
12581             
12582             if(t.execCall && t.execCall.call(this, values, parent)){
12583                 return '';
12584             }
12585         } catch(e) {
12586             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12587             Roo.log(values);
12588             return '';
12589         }
12590         
12591         try {
12592             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12593             parent = t.target ? values : parent;
12594             if(t.forCall && vs instanceof Array){
12595                 var buf = [];
12596                 for(var i = 0, len = vs.length; i < len; i++){
12597                     try {
12598                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12599                     } catch (e) {
12600                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12601                         Roo.log(e.body);
12602                         //Roo.log(t.compiled);
12603                         Roo.log(vs[i]);
12604                     }   
12605                 }
12606                 return buf.join('');
12607             }
12608         } catch (e) {
12609             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12610             Roo.log(values);
12611             return '';
12612         }
12613         try {
12614             return t.compiled.call(this, vs, parent);
12615         } catch (e) {
12616             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12617             Roo.log(e.body);
12618             //Roo.log(t.compiled);
12619             Roo.log(values);
12620             return '';
12621         }
12622     },
12623
12624    
12625
12626     applyTemplate : function(values){
12627         return this.master.compiled.call(this, values, {});
12628         //var s = this.subs;
12629     },
12630
12631     apply : function(){
12632         return this.applyTemplate.apply(this, arguments);
12633     }
12634
12635  });
12636
12637 Roo.DomTemplate.from = function(el){
12638     el = Roo.getDom(el);
12639     return new Roo.Domtemplate(el.value || el.innerHTML);
12640 };/*
12641  * Based on:
12642  * Ext JS Library 1.1.1
12643  * Copyright(c) 2006-2007, Ext JS, LLC.
12644  *
12645  * Originally Released Under LGPL - original licence link has changed is not relivant.
12646  *
12647  * Fork - LGPL
12648  * <script type="text/javascript">
12649  */
12650
12651 /**
12652  * @class Roo.util.DelayedTask
12653  * Provides a convenient method of performing setTimeout where a new
12654  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12655  * You can use this class to buffer
12656  * the keypress events for a certain number of milliseconds, and perform only if they stop
12657  * for that amount of time.
12658  * @constructor The parameters to this constructor serve as defaults and are not required.
12659  * @param {Function} fn (optional) The default function to timeout
12660  * @param {Object} scope (optional) The default scope of that timeout
12661  * @param {Array} args (optional) The default Array of arguments
12662  */
12663 Roo.util.DelayedTask = function(fn, scope, args){
12664     var id = null, d, t;
12665
12666     var call = function(){
12667         var now = new Date().getTime();
12668         if(now - t >= d){
12669             clearInterval(id);
12670             id = null;
12671             fn.apply(scope, args || []);
12672         }
12673     };
12674     /**
12675      * Cancels any pending timeout and queues a new one
12676      * @param {Number} delay The milliseconds to delay
12677      * @param {Function} newFn (optional) Overrides function passed to constructor
12678      * @param {Object} newScope (optional) Overrides scope passed to constructor
12679      * @param {Array} newArgs (optional) Overrides args passed to constructor
12680      */
12681     this.delay = function(delay, newFn, newScope, newArgs){
12682         if(id && delay != d){
12683             this.cancel();
12684         }
12685         d = delay;
12686         t = new Date().getTime();
12687         fn = newFn || fn;
12688         scope = newScope || scope;
12689         args = newArgs || args;
12690         if(!id){
12691             id = setInterval(call, d);
12692         }
12693     };
12694
12695     /**
12696      * Cancel the last queued timeout
12697      */
12698     this.cancel = function(){
12699         if(id){
12700             clearInterval(id);
12701             id = null;
12702         }
12703     };
12704 };/*
12705  * Based on:
12706  * Ext JS Library 1.1.1
12707  * Copyright(c) 2006-2007, Ext JS, LLC.
12708  *
12709  * Originally Released Under LGPL - original licence link has changed is not relivant.
12710  *
12711  * Fork - LGPL
12712  * <script type="text/javascript">
12713  */
12714  
12715  
12716 Roo.util.TaskRunner = function(interval){
12717     interval = interval || 10;
12718     var tasks = [], removeQueue = [];
12719     var id = 0;
12720     var running = false;
12721
12722     var stopThread = function(){
12723         running = false;
12724         clearInterval(id);
12725         id = 0;
12726     };
12727
12728     var startThread = function(){
12729         if(!running){
12730             running = true;
12731             id = setInterval(runTasks, interval);
12732         }
12733     };
12734
12735     var removeTask = function(task){
12736         removeQueue.push(task);
12737         if(task.onStop){
12738             task.onStop();
12739         }
12740     };
12741
12742     var runTasks = function(){
12743         if(removeQueue.length > 0){
12744             for(var i = 0, len = removeQueue.length; i < len; i++){
12745                 tasks.remove(removeQueue[i]);
12746             }
12747             removeQueue = [];
12748             if(tasks.length < 1){
12749                 stopThread();
12750                 return;
12751             }
12752         }
12753         var now = new Date().getTime();
12754         for(var i = 0, len = tasks.length; i < len; ++i){
12755             var t = tasks[i];
12756             var itime = now - t.taskRunTime;
12757             if(t.interval <= itime){
12758                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12759                 t.taskRunTime = now;
12760                 if(rt === false || t.taskRunCount === t.repeat){
12761                     removeTask(t);
12762                     return;
12763                 }
12764             }
12765             if(t.duration && t.duration <= (now - t.taskStartTime)){
12766                 removeTask(t);
12767             }
12768         }
12769     };
12770
12771     /**
12772      * Queues a new task.
12773      * @param {Object} task
12774      */
12775     this.start = function(task){
12776         tasks.push(task);
12777         task.taskStartTime = new Date().getTime();
12778         task.taskRunTime = 0;
12779         task.taskRunCount = 0;
12780         startThread();
12781         return task;
12782     };
12783
12784     this.stop = function(task){
12785         removeTask(task);
12786         return task;
12787     };
12788
12789     this.stopAll = function(){
12790         stopThread();
12791         for(var i = 0, len = tasks.length; i < len; i++){
12792             if(tasks[i].onStop){
12793                 tasks[i].onStop();
12794             }
12795         }
12796         tasks = [];
12797         removeQueue = [];
12798     };
12799 };
12800
12801 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12802  * Based on:
12803  * Ext JS Library 1.1.1
12804  * Copyright(c) 2006-2007, Ext JS, LLC.
12805  *
12806  * Originally Released Under LGPL - original licence link has changed is not relivant.
12807  *
12808  * Fork - LGPL
12809  * <script type="text/javascript">
12810  */
12811
12812  
12813 /**
12814  * @class Roo.util.MixedCollection
12815  * @extends Roo.util.Observable
12816  * A Collection class that maintains both numeric indexes and keys and exposes events.
12817  * @constructor
12818  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12819  * collection (defaults to false)
12820  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12821  * and return the key value for that item.  This is used when available to look up the key on items that
12822  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12823  * equivalent to providing an implementation for the {@link #getKey} method.
12824  */
12825 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12826     this.items = [];
12827     this.map = {};
12828     this.keys = [];
12829     this.length = 0;
12830     this.addEvents({
12831         /**
12832          * @event clear
12833          * Fires when the collection is cleared.
12834          */
12835         "clear" : true,
12836         /**
12837          * @event add
12838          * Fires when an item is added to the collection.
12839          * @param {Number} index The index at which the item was added.
12840          * @param {Object} o The item added.
12841          * @param {String} key The key associated with the added item.
12842          */
12843         "add" : true,
12844         /**
12845          * @event replace
12846          * Fires when an item is replaced in the collection.
12847          * @param {String} key he key associated with the new added.
12848          * @param {Object} old The item being replaced.
12849          * @param {Object} new The new item.
12850          */
12851         "replace" : true,
12852         /**
12853          * @event remove
12854          * Fires when an item is removed from the collection.
12855          * @param {Object} o The item being removed.
12856          * @param {String} key (optional) The key associated with the removed item.
12857          */
12858         "remove" : true,
12859         "sort" : true
12860     });
12861     this.allowFunctions = allowFunctions === true;
12862     if(keyFn){
12863         this.getKey = keyFn;
12864     }
12865     Roo.util.MixedCollection.superclass.constructor.call(this);
12866 };
12867
12868 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12869     allowFunctions : false,
12870     
12871 /**
12872  * Adds an item to the collection.
12873  * @param {String} key The key to associate with the item
12874  * @param {Object} o The item to add.
12875  * @return {Object} The item added.
12876  */
12877     add : function(key, o){
12878         if(arguments.length == 1){
12879             o = arguments[0];
12880             key = this.getKey(o);
12881         }
12882         if(typeof key == "undefined" || key === null){
12883             this.length++;
12884             this.items.push(o);
12885             this.keys.push(null);
12886         }else{
12887             var old = this.map[key];
12888             if(old){
12889                 return this.replace(key, o);
12890             }
12891             this.length++;
12892             this.items.push(o);
12893             this.map[key] = o;
12894             this.keys.push(key);
12895         }
12896         this.fireEvent("add", this.length-1, o, key);
12897         return o;
12898     },
12899        
12900 /**
12901   * MixedCollection has a generic way to fetch keys if you implement getKey.
12902 <pre><code>
12903 // normal way
12904 var mc = new Roo.util.MixedCollection();
12905 mc.add(someEl.dom.id, someEl);
12906 mc.add(otherEl.dom.id, otherEl);
12907 //and so on
12908
12909 // using getKey
12910 var mc = new Roo.util.MixedCollection();
12911 mc.getKey = function(el){
12912    return el.dom.id;
12913 };
12914 mc.add(someEl);
12915 mc.add(otherEl);
12916
12917 // or via the constructor
12918 var mc = new Roo.util.MixedCollection(false, function(el){
12919    return el.dom.id;
12920 });
12921 mc.add(someEl);
12922 mc.add(otherEl);
12923 </code></pre>
12924  * @param o {Object} The item for which to find the key.
12925  * @return {Object} The key for the passed item.
12926  */
12927     getKey : function(o){
12928          return o.id; 
12929     },
12930    
12931 /**
12932  * Replaces an item in the collection.
12933  * @param {String} key The key associated with the item to replace, or the item to replace.
12934  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12935  * @return {Object}  The new item.
12936  */
12937     replace : function(key, o){
12938         if(arguments.length == 1){
12939             o = arguments[0];
12940             key = this.getKey(o);
12941         }
12942         var old = this.item(key);
12943         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12944              return this.add(key, o);
12945         }
12946         var index = this.indexOfKey(key);
12947         this.items[index] = o;
12948         this.map[key] = o;
12949         this.fireEvent("replace", key, old, o);
12950         return o;
12951     },
12952    
12953 /**
12954  * Adds all elements of an Array or an Object to the collection.
12955  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12956  * an Array of values, each of which are added to the collection.
12957  */
12958     addAll : function(objs){
12959         if(arguments.length > 1 || objs instanceof Array){
12960             var args = arguments.length > 1 ? arguments : objs;
12961             for(var i = 0, len = args.length; i < len; i++){
12962                 this.add(args[i]);
12963             }
12964         }else{
12965             for(var key in objs){
12966                 if(this.allowFunctions || typeof objs[key] != "function"){
12967                     this.add(key, objs[key]);
12968                 }
12969             }
12970         }
12971     },
12972    
12973 /**
12974  * Executes the specified function once for every item in the collection, passing each
12975  * item as the first and only parameter. returning false from the function will stop the iteration.
12976  * @param {Function} fn The function to execute for each item.
12977  * @param {Object} scope (optional) The scope in which to execute the function.
12978  */
12979     each : function(fn, scope){
12980         var items = [].concat(this.items); // each safe for removal
12981         for(var i = 0, len = items.length; i < len; i++){
12982             if(fn.call(scope || items[i], items[i], i, len) === false){
12983                 break;
12984             }
12985         }
12986     },
12987    
12988 /**
12989  * Executes the specified function once for every key in the collection, passing each
12990  * key, and its associated item as the first two parameters.
12991  * @param {Function} fn The function to execute for each item.
12992  * @param {Object} scope (optional) The scope in which to execute the function.
12993  */
12994     eachKey : function(fn, scope){
12995         for(var i = 0, len = this.keys.length; i < len; i++){
12996             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12997         }
12998     },
12999    
13000 /**
13001  * Returns the first item in the collection which elicits a true return value from the
13002  * passed selection function.
13003  * @param {Function} fn The selection function to execute for each item.
13004  * @param {Object} scope (optional) The scope in which to execute the function.
13005  * @return {Object} The first item in the collection which returned true from the selection function.
13006  */
13007     find : function(fn, scope){
13008         for(var i = 0, len = this.items.length; i < len; i++){
13009             if(fn.call(scope || window, this.items[i], this.keys[i])){
13010                 return this.items[i];
13011             }
13012         }
13013         return null;
13014     },
13015    
13016 /**
13017  * Inserts an item at the specified index in the collection.
13018  * @param {Number} index The index to insert the item at.
13019  * @param {String} key The key to associate with the new item, or the item itself.
13020  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13021  * @return {Object} The item inserted.
13022  */
13023     insert : function(index, key, o){
13024         if(arguments.length == 2){
13025             o = arguments[1];
13026             key = this.getKey(o);
13027         }
13028         if(index >= this.length){
13029             return this.add(key, o);
13030         }
13031         this.length++;
13032         this.items.splice(index, 0, o);
13033         if(typeof key != "undefined" && key != null){
13034             this.map[key] = o;
13035         }
13036         this.keys.splice(index, 0, key);
13037         this.fireEvent("add", index, o, key);
13038         return o;
13039     },
13040    
13041 /**
13042  * Removed an item from the collection.
13043  * @param {Object} o The item to remove.
13044  * @return {Object} The item removed.
13045  */
13046     remove : function(o){
13047         return this.removeAt(this.indexOf(o));
13048     },
13049    
13050 /**
13051  * Remove an item from a specified index in the collection.
13052  * @param {Number} index The index within the collection of the item to remove.
13053  */
13054     removeAt : function(index){
13055         if(index < this.length && index >= 0){
13056             this.length--;
13057             var o = this.items[index];
13058             this.items.splice(index, 1);
13059             var key = this.keys[index];
13060             if(typeof key != "undefined"){
13061                 delete this.map[key];
13062             }
13063             this.keys.splice(index, 1);
13064             this.fireEvent("remove", o, key);
13065         }
13066     },
13067    
13068 /**
13069  * Removed an item associated with the passed key fom the collection.
13070  * @param {String} key The key of the item to remove.
13071  */
13072     removeKey : function(key){
13073         return this.removeAt(this.indexOfKey(key));
13074     },
13075    
13076 /**
13077  * Returns the number of items in the collection.
13078  * @return {Number} the number of items in the collection.
13079  */
13080     getCount : function(){
13081         return this.length; 
13082     },
13083    
13084 /**
13085  * Returns index within the collection of the passed Object.
13086  * @param {Object} o The item to find the index of.
13087  * @return {Number} index of the item.
13088  */
13089     indexOf : function(o){
13090         if(!this.items.indexOf){
13091             for(var i = 0, len = this.items.length; i < len; i++){
13092                 if(this.items[i] == o) return i;
13093             }
13094             return -1;
13095         }else{
13096             return this.items.indexOf(o);
13097         }
13098     },
13099    
13100 /**
13101  * Returns index within the collection of the passed key.
13102  * @param {String} key The key to find the index of.
13103  * @return {Number} index of the key.
13104  */
13105     indexOfKey : function(key){
13106         if(!this.keys.indexOf){
13107             for(var i = 0, len = this.keys.length; i < len; i++){
13108                 if(this.keys[i] == key) return i;
13109             }
13110             return -1;
13111         }else{
13112             return this.keys.indexOf(key);
13113         }
13114     },
13115    
13116 /**
13117  * Returns the item associated with the passed key OR index. Key has priority over index.
13118  * @param {String/Number} key The key or index of the item.
13119  * @return {Object} The item associated with the passed key.
13120  */
13121     item : function(key){
13122         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13123         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13124     },
13125     
13126 /**
13127  * Returns the item at the specified index.
13128  * @param {Number} index The index of the item.
13129  * @return {Object}
13130  */
13131     itemAt : function(index){
13132         return this.items[index];
13133     },
13134     
13135 /**
13136  * Returns the item associated with the passed key.
13137  * @param {String/Number} key The key of the item.
13138  * @return {Object} The item associated with the passed key.
13139  */
13140     key : function(key){
13141         return this.map[key];
13142     },
13143    
13144 /**
13145  * Returns true if the collection contains the passed Object as an item.
13146  * @param {Object} o  The Object to look for in the collection.
13147  * @return {Boolean} True if the collection contains the Object as an item.
13148  */
13149     contains : function(o){
13150         return this.indexOf(o) != -1;
13151     },
13152    
13153 /**
13154  * Returns true if the collection contains the passed Object as a key.
13155  * @param {String} key The key to look for in the collection.
13156  * @return {Boolean} True if the collection contains the Object as a key.
13157  */
13158     containsKey : function(key){
13159         return typeof this.map[key] != "undefined";
13160     },
13161    
13162 /**
13163  * Removes all items from the collection.
13164  */
13165     clear : function(){
13166         this.length = 0;
13167         this.items = [];
13168         this.keys = [];
13169         this.map = {};
13170         this.fireEvent("clear");
13171     },
13172    
13173 /**
13174  * Returns the first item in the collection.
13175  * @return {Object} the first item in the collection..
13176  */
13177     first : function(){
13178         return this.items[0]; 
13179     },
13180    
13181 /**
13182  * Returns the last item in the collection.
13183  * @return {Object} the last item in the collection..
13184  */
13185     last : function(){
13186         return this.items[this.length-1];   
13187     },
13188     
13189     _sort : function(property, dir, fn){
13190         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13191         fn = fn || function(a, b){
13192             return a-b;
13193         };
13194         var c = [], k = this.keys, items = this.items;
13195         for(var i = 0, len = items.length; i < len; i++){
13196             c[c.length] = {key: k[i], value: items[i], index: i};
13197         }
13198         c.sort(function(a, b){
13199             var v = fn(a[property], b[property]) * dsc;
13200             if(v == 0){
13201                 v = (a.index < b.index ? -1 : 1);
13202             }
13203             return v;
13204         });
13205         for(var i = 0, len = c.length; i < len; i++){
13206             items[i] = c[i].value;
13207             k[i] = c[i].key;
13208         }
13209         this.fireEvent("sort", this);
13210     },
13211     
13212     /**
13213      * Sorts this collection with the passed comparison function
13214      * @param {String} direction (optional) "ASC" or "DESC"
13215      * @param {Function} fn (optional) comparison function
13216      */
13217     sort : function(dir, fn){
13218         this._sort("value", dir, fn);
13219     },
13220     
13221     /**
13222      * Sorts this collection by keys
13223      * @param {String} direction (optional) "ASC" or "DESC"
13224      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13225      */
13226     keySort : function(dir, fn){
13227         this._sort("key", dir, fn || function(a, b){
13228             return String(a).toUpperCase()-String(b).toUpperCase();
13229         });
13230     },
13231     
13232     /**
13233      * Returns a range of items in this collection
13234      * @param {Number} startIndex (optional) defaults to 0
13235      * @param {Number} endIndex (optional) default to the last item
13236      * @return {Array} An array of items
13237      */
13238     getRange : function(start, end){
13239         var items = this.items;
13240         if(items.length < 1){
13241             return [];
13242         }
13243         start = start || 0;
13244         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13245         var r = [];
13246         if(start <= end){
13247             for(var i = start; i <= end; i++) {
13248                     r[r.length] = items[i];
13249             }
13250         }else{
13251             for(var i = start; i >= end; i--) {
13252                     r[r.length] = items[i];
13253             }
13254         }
13255         return r;
13256     },
13257         
13258     /**
13259      * Filter the <i>objects</i> in this collection by a specific property. 
13260      * Returns a new collection that has been filtered.
13261      * @param {String} property A property on your objects
13262      * @param {String/RegExp} value Either string that the property values 
13263      * should start with or a RegExp to test against the property
13264      * @return {MixedCollection} The new filtered collection
13265      */
13266     filter : function(property, value){
13267         if(!value.exec){ // not a regex
13268             value = String(value);
13269             if(value.length == 0){
13270                 return this.clone();
13271             }
13272             value = new RegExp("^" + Roo.escapeRe(value), "i");
13273         }
13274         return this.filterBy(function(o){
13275             return o && value.test(o[property]);
13276         });
13277         },
13278     
13279     /**
13280      * Filter by a function. * Returns a new collection that has been filtered.
13281      * The passed function will be called with each 
13282      * object in the collection. If the function returns true, the value is included 
13283      * otherwise it is filtered.
13284      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13285      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13286      * @return {MixedCollection} The new filtered collection
13287      */
13288     filterBy : function(fn, scope){
13289         var r = new Roo.util.MixedCollection();
13290         r.getKey = this.getKey;
13291         var k = this.keys, it = this.items;
13292         for(var i = 0, len = it.length; i < len; i++){
13293             if(fn.call(scope||this, it[i], k[i])){
13294                                 r.add(k[i], it[i]);
13295                         }
13296         }
13297         return r;
13298     },
13299     
13300     /**
13301      * Creates a duplicate of this collection
13302      * @return {MixedCollection}
13303      */
13304     clone : function(){
13305         var r = new Roo.util.MixedCollection();
13306         var k = this.keys, it = this.items;
13307         for(var i = 0, len = it.length; i < len; i++){
13308             r.add(k[i], it[i]);
13309         }
13310         r.getKey = this.getKey;
13311         return r;
13312     }
13313 });
13314 /**
13315  * Returns the item associated with the passed key or index.
13316  * @method
13317  * @param {String/Number} key The key or index of the item.
13318  * @return {Object} The item associated with the passed key.
13319  */
13320 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13321  * Based on:
13322  * Ext JS Library 1.1.1
13323  * Copyright(c) 2006-2007, Ext JS, LLC.
13324  *
13325  * Originally Released Under LGPL - original licence link has changed is not relivant.
13326  *
13327  * Fork - LGPL
13328  * <script type="text/javascript">
13329  */
13330 /**
13331  * @class Roo.util.JSON
13332  * Modified version of Douglas Crockford"s json.js that doesn"t
13333  * mess with the Object prototype 
13334  * http://www.json.org/js.html
13335  * @singleton
13336  */
13337 Roo.util.JSON = new (function(){
13338     var useHasOwn = {}.hasOwnProperty ? true : false;
13339     
13340     // crashes Safari in some instances
13341     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13342     
13343     var pad = function(n) {
13344         return n < 10 ? "0" + n : n;
13345     };
13346     
13347     var m = {
13348         "\b": '\\b',
13349         "\t": '\\t',
13350         "\n": '\\n',
13351         "\f": '\\f',
13352         "\r": '\\r',
13353         '"' : '\\"',
13354         "\\": '\\\\'
13355     };
13356
13357     var encodeString = function(s){
13358         if (/["\\\x00-\x1f]/.test(s)) {
13359             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13360                 var c = m[b];
13361                 if(c){
13362                     return c;
13363                 }
13364                 c = b.charCodeAt();
13365                 return "\\u00" +
13366                     Math.floor(c / 16).toString(16) +
13367                     (c % 16).toString(16);
13368             }) + '"';
13369         }
13370         return '"' + s + '"';
13371     };
13372     
13373     var encodeArray = function(o){
13374         var a = ["["], b, i, l = o.length, v;
13375             for (i = 0; i < l; i += 1) {
13376                 v = o[i];
13377                 switch (typeof v) {
13378                     case "undefined":
13379                     case "function":
13380                     case "unknown":
13381                         break;
13382                     default:
13383                         if (b) {
13384                             a.push(',');
13385                         }
13386                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13387                         b = true;
13388                 }
13389             }
13390             a.push("]");
13391             return a.join("");
13392     };
13393     
13394     var encodeDate = function(o){
13395         return '"' + o.getFullYear() + "-" +
13396                 pad(o.getMonth() + 1) + "-" +
13397                 pad(o.getDate()) + "T" +
13398                 pad(o.getHours()) + ":" +
13399                 pad(o.getMinutes()) + ":" +
13400                 pad(o.getSeconds()) + '"';
13401     };
13402     
13403     /**
13404      * Encodes an Object, Array or other value
13405      * @param {Mixed} o The variable to encode
13406      * @return {String} The JSON string
13407      */
13408     this.encode = function(o)
13409     {
13410         // should this be extended to fully wrap stringify..
13411         
13412         if(typeof o == "undefined" || o === null){
13413             return "null";
13414         }else if(o instanceof Array){
13415             return encodeArray(o);
13416         }else if(o instanceof Date){
13417             return encodeDate(o);
13418         }else if(typeof o == "string"){
13419             return encodeString(o);
13420         }else if(typeof o == "number"){
13421             return isFinite(o) ? String(o) : "null";
13422         }else if(typeof o == "boolean"){
13423             return String(o);
13424         }else {
13425             var a = ["{"], b, i, v;
13426             for (i in o) {
13427                 if(!useHasOwn || o.hasOwnProperty(i)) {
13428                     v = o[i];
13429                     switch (typeof v) {
13430                     case "undefined":
13431                     case "function":
13432                     case "unknown":
13433                         break;
13434                     default:
13435                         if(b){
13436                             a.push(',');
13437                         }
13438                         a.push(this.encode(i), ":",
13439                                 v === null ? "null" : this.encode(v));
13440                         b = true;
13441                     }
13442                 }
13443             }
13444             a.push("}");
13445             return a.join("");
13446         }
13447     };
13448     
13449     /**
13450      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13451      * @param {String} json The JSON string
13452      * @return {Object} The resulting object
13453      */
13454     this.decode = function(json){
13455         
13456         return  /** eval:var:json */ eval("(" + json + ')');
13457     };
13458 })();
13459 /** 
13460  * Shorthand for {@link Roo.util.JSON#encode}
13461  * @member Roo encode 
13462  * @method */
13463 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13464 /** 
13465  * Shorthand for {@link Roo.util.JSON#decode}
13466  * @member Roo decode 
13467  * @method */
13468 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13469 /*
13470  * Based on:
13471  * Ext JS Library 1.1.1
13472  * Copyright(c) 2006-2007, Ext JS, LLC.
13473  *
13474  * Originally Released Under LGPL - original licence link has changed is not relivant.
13475  *
13476  * Fork - LGPL
13477  * <script type="text/javascript">
13478  */
13479  
13480 /**
13481  * @class Roo.util.Format
13482  * Reusable data formatting functions
13483  * @singleton
13484  */
13485 Roo.util.Format = function(){
13486     var trimRe = /^\s+|\s+$/g;
13487     return {
13488         /**
13489          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13490          * @param {String} value The string to truncate
13491          * @param {Number} length The maximum length to allow before truncating
13492          * @return {String} The converted text
13493          */
13494         ellipsis : function(value, len){
13495             if(value && value.length > len){
13496                 return value.substr(0, len-3)+"...";
13497             }
13498             return value;
13499         },
13500
13501         /**
13502          * Checks a reference and converts it to empty string if it is undefined
13503          * @param {Mixed} value Reference to check
13504          * @return {Mixed} Empty string if converted, otherwise the original value
13505          */
13506         undef : function(value){
13507             return typeof value != "undefined" ? value : "";
13508         },
13509
13510         /**
13511          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13512          * @param {String} value The string to encode
13513          * @return {String} The encoded text
13514          */
13515         htmlEncode : function(value){
13516             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13517         },
13518
13519         /**
13520          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13521          * @param {String} value The string to decode
13522          * @return {String} The decoded text
13523          */
13524         htmlDecode : function(value){
13525             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13526         },
13527
13528         /**
13529          * Trims any whitespace from either side of a string
13530          * @param {String} value The text to trim
13531          * @return {String} The trimmed text
13532          */
13533         trim : function(value){
13534             return String(value).replace(trimRe, "");
13535         },
13536
13537         /**
13538          * Returns a substring from within an original string
13539          * @param {String} value The original text
13540          * @param {Number} start The start index of the substring
13541          * @param {Number} length The length of the substring
13542          * @return {String} The substring
13543          */
13544         substr : function(value, start, length){
13545             return String(value).substr(start, length);
13546         },
13547
13548         /**
13549          * Converts a string to all lower case letters
13550          * @param {String} value The text to convert
13551          * @return {String} The converted text
13552          */
13553         lowercase : function(value){
13554             return String(value).toLowerCase();
13555         },
13556
13557         /**
13558          * Converts a string to all upper case letters
13559          * @param {String} value The text to convert
13560          * @return {String} The converted text
13561          */
13562         uppercase : function(value){
13563             return String(value).toUpperCase();
13564         },
13565
13566         /**
13567          * Converts the first character only of a string to upper case
13568          * @param {String} value The text to convert
13569          * @return {String} The converted text
13570          */
13571         capitalize : function(value){
13572             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13573         },
13574
13575         // private
13576         call : function(value, fn){
13577             if(arguments.length > 2){
13578                 var args = Array.prototype.slice.call(arguments, 2);
13579                 args.unshift(value);
13580                  
13581                 return /** eval:var:value */  eval(fn).apply(window, args);
13582             }else{
13583                 /** eval:var:value */
13584                 return /** eval:var:value */ eval(fn).call(window, value);
13585             }
13586         },
13587
13588        
13589         /**
13590          * safer version of Math.toFixed..??/
13591          * @param {Number/String} value The numeric value to format
13592          * @param {Number/String} value Decimal places 
13593          * @return {String} The formatted currency string
13594          */
13595         toFixed : function(v, n)
13596         {
13597             // why not use to fixed - precision is buggered???
13598             if (!n) {
13599                 return Math.round(v-0);
13600             }
13601             var fact = Math.pow(10,n+1);
13602             v = (Math.round((v-0)*fact))/fact;
13603             var z = (''+fact).substring(2);
13604             if (v == Math.floor(v)) {
13605                 return Math.floor(v) + '.' + z;
13606             }
13607             
13608             // now just padd decimals..
13609             var ps = String(v).split('.');
13610             var fd = (ps[1] + z);
13611             var r = fd.substring(0,n); 
13612             var rm = fd.substring(n); 
13613             if (rm < 5) {
13614                 return ps[0] + '.' + r;
13615             }
13616             r*=1; // turn it into a number;
13617             r++;
13618             if (String(r).length != n) {
13619                 ps[0]*=1;
13620                 ps[0]++;
13621                 r = String(r).substring(1); // chop the end off.
13622             }
13623             
13624             return ps[0] + '.' + r;
13625              
13626         },
13627         
13628         /**
13629          * Format a number as US currency
13630          * @param {Number/String} value The numeric value to format
13631          * @return {String} The formatted currency string
13632          */
13633         usMoney : function(v){
13634             return '$' + Roo.util.Format.number(v);
13635         },
13636         
13637         /**
13638          * Format a number
13639          * eventually this should probably emulate php's number_format
13640          * @param {Number/String} value The numeric value to format
13641          * @param {Number} decimals number of decimal places
13642          * @return {String} The formatted currency string
13643          */
13644         number : function(v,decimals)
13645         {
13646             // multiply and round.
13647             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13648             var mul = Math.pow(10, decimals);
13649             var zero = String(mul).substring(1);
13650             v = (Math.round((v-0)*mul))/mul;
13651             
13652             // if it's '0' number.. then
13653             
13654             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13655             v = String(v);
13656             var ps = v.split('.');
13657             var whole = ps[0];
13658             
13659             
13660             var r = /(\d+)(\d{3})/;
13661             // add comma's
13662             while (r.test(whole)) {
13663                 whole = whole.replace(r, '$1' + ',' + '$2');
13664             }
13665             
13666             
13667             var sub = ps[1] ?
13668                     // has decimals..
13669                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13670                     // does not have decimals
13671                     (decimals ? ('.' + zero) : '');
13672             
13673             
13674             return whole + sub ;
13675         },
13676         
13677         /**
13678          * Parse a value into a formatted date using the specified format pattern.
13679          * @param {Mixed} value The value to format
13680          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13681          * @return {String} The formatted date string
13682          */
13683         date : function(v, format){
13684             if(!v){
13685                 return "";
13686             }
13687             if(!(v instanceof Date)){
13688                 v = new Date(Date.parse(v));
13689             }
13690             return v.dateFormat(format || Roo.util.Format.defaults.date);
13691         },
13692
13693         /**
13694          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13695          * @param {String} format Any valid date format string
13696          * @return {Function} The date formatting function
13697          */
13698         dateRenderer : function(format){
13699             return function(v){
13700                 return Roo.util.Format.date(v, format);  
13701             };
13702         },
13703
13704         // private
13705         stripTagsRE : /<\/?[^>]+>/gi,
13706         
13707         /**
13708          * Strips all HTML tags
13709          * @param {Mixed} value The text from which to strip tags
13710          * @return {String} The stripped text
13711          */
13712         stripTags : function(v){
13713             return !v ? v : String(v).replace(this.stripTagsRE, "");
13714         }
13715     };
13716 }();
13717 Roo.util.Format.defaults = {
13718     date : 'd/M/Y'
13719 };/*
13720  * Based on:
13721  * Ext JS Library 1.1.1
13722  * Copyright(c) 2006-2007, Ext JS, LLC.
13723  *
13724  * Originally Released Under LGPL - original licence link has changed is not relivant.
13725  *
13726  * Fork - LGPL
13727  * <script type="text/javascript">
13728  */
13729
13730
13731  
13732
13733 /**
13734  * @class Roo.MasterTemplate
13735  * @extends Roo.Template
13736  * Provides a template that can have child templates. The syntax is:
13737 <pre><code>
13738 var t = new Roo.MasterTemplate(
13739         '&lt;select name="{name}"&gt;',
13740                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13741         '&lt;/select&gt;'
13742 );
13743 t.add('options', {value: 'foo', text: 'bar'});
13744 // or you can add multiple child elements in one shot
13745 t.addAll('options', [
13746     {value: 'foo', text: 'bar'},
13747     {value: 'foo2', text: 'bar2'},
13748     {value: 'foo3', text: 'bar3'}
13749 ]);
13750 // then append, applying the master template values
13751 t.append('my-form', {name: 'my-select'});
13752 </code></pre>
13753 * A name attribute for the child template is not required if you have only one child
13754 * template or you want to refer to them by index.
13755  */
13756 Roo.MasterTemplate = function(){
13757     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13758     this.originalHtml = this.html;
13759     var st = {};
13760     var m, re = this.subTemplateRe;
13761     re.lastIndex = 0;
13762     var subIndex = 0;
13763     while(m = re.exec(this.html)){
13764         var name = m[1], content = m[2];
13765         st[subIndex] = {
13766             name: name,
13767             index: subIndex,
13768             buffer: [],
13769             tpl : new Roo.Template(content)
13770         };
13771         if(name){
13772             st[name] = st[subIndex];
13773         }
13774         st[subIndex].tpl.compile();
13775         st[subIndex].tpl.call = this.call.createDelegate(this);
13776         subIndex++;
13777     }
13778     this.subCount = subIndex;
13779     this.subs = st;
13780 };
13781 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13782     /**
13783     * The regular expression used to match sub templates
13784     * @type RegExp
13785     * @property
13786     */
13787     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13788
13789     /**
13790      * Applies the passed values to a child template.
13791      * @param {String/Number} name (optional) The name or index of the child template
13792      * @param {Array/Object} values The values to be applied to the template
13793      * @return {MasterTemplate} this
13794      */
13795      add : function(name, values){
13796         if(arguments.length == 1){
13797             values = arguments[0];
13798             name = 0;
13799         }
13800         var s = this.subs[name];
13801         s.buffer[s.buffer.length] = s.tpl.apply(values);
13802         return this;
13803     },
13804
13805     /**
13806      * Applies all the passed values to a child template.
13807      * @param {String/Number} name (optional) The name or index of the child template
13808      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13809      * @param {Boolean} reset (optional) True to reset the template first
13810      * @return {MasterTemplate} this
13811      */
13812     fill : function(name, values, reset){
13813         var a = arguments;
13814         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13815             values = a[0];
13816             name = 0;
13817             reset = a[1];
13818         }
13819         if(reset){
13820             this.reset();
13821         }
13822         for(var i = 0, len = values.length; i < len; i++){
13823             this.add(name, values[i]);
13824         }
13825         return this;
13826     },
13827
13828     /**
13829      * Resets the template for reuse
13830      * @return {MasterTemplate} this
13831      */
13832      reset : function(){
13833         var s = this.subs;
13834         for(var i = 0; i < this.subCount; i++){
13835             s[i].buffer = [];
13836         }
13837         return this;
13838     },
13839
13840     applyTemplate : function(values){
13841         var s = this.subs;
13842         var replaceIndex = -1;
13843         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13844             return s[++replaceIndex].buffer.join("");
13845         });
13846         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13847     },
13848
13849     apply : function(){
13850         return this.applyTemplate.apply(this, arguments);
13851     },
13852
13853     compile : function(){return this;}
13854 });
13855
13856 /**
13857  * Alias for fill().
13858  * @method
13859  */
13860 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13861  /**
13862  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13863  * var tpl = Roo.MasterTemplate.from('element-id');
13864  * @param {String/HTMLElement} el
13865  * @param {Object} config
13866  * @static
13867  */
13868 Roo.MasterTemplate.from = function(el, config){
13869     el = Roo.getDom(el);
13870     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13871 };/*
13872  * Based on:
13873  * Ext JS Library 1.1.1
13874  * Copyright(c) 2006-2007, Ext JS, LLC.
13875  *
13876  * Originally Released Under LGPL - original licence link has changed is not relivant.
13877  *
13878  * Fork - LGPL
13879  * <script type="text/javascript">
13880  */
13881
13882  
13883 /**
13884  * @class Roo.util.CSS
13885  * Utility class for manipulating CSS rules
13886  * @singleton
13887  */
13888 Roo.util.CSS = function(){
13889         var rules = null;
13890         var doc = document;
13891
13892     var camelRe = /(-[a-z])/gi;
13893     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13894
13895    return {
13896    /**
13897     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13898     * tag and appended to the HEAD of the document.
13899     * @param {String|Object} cssText The text containing the css rules
13900     * @param {String} id An id to add to the stylesheet for later removal
13901     * @return {StyleSheet}
13902     */
13903     createStyleSheet : function(cssText, id){
13904         var ss;
13905         var head = doc.getElementsByTagName("head")[0];
13906         var nrules = doc.createElement("style");
13907         nrules.setAttribute("type", "text/css");
13908         if(id){
13909             nrules.setAttribute("id", id);
13910         }
13911         if (typeof(cssText) != 'string') {
13912             // support object maps..
13913             // not sure if this a good idea.. 
13914             // perhaps it should be merged with the general css handling
13915             // and handle js style props.
13916             var cssTextNew = [];
13917             for(var n in cssText) {
13918                 var citems = [];
13919                 for(var k in cssText[n]) {
13920                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13921                 }
13922                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13923                 
13924             }
13925             cssText = cssTextNew.join("\n");
13926             
13927         }
13928        
13929        
13930        if(Roo.isIE){
13931            head.appendChild(nrules);
13932            ss = nrules.styleSheet;
13933            ss.cssText = cssText;
13934        }else{
13935            try{
13936                 nrules.appendChild(doc.createTextNode(cssText));
13937            }catch(e){
13938                nrules.cssText = cssText; 
13939            }
13940            head.appendChild(nrules);
13941            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13942        }
13943        this.cacheStyleSheet(ss);
13944        return ss;
13945    },
13946
13947    /**
13948     * Removes a style or link tag by id
13949     * @param {String} id The id of the tag
13950     */
13951    removeStyleSheet : function(id){
13952        var existing = doc.getElementById(id);
13953        if(existing){
13954            existing.parentNode.removeChild(existing);
13955        }
13956    },
13957
13958    /**
13959     * Dynamically swaps an existing stylesheet reference for a new one
13960     * @param {String} id The id of an existing link tag to remove
13961     * @param {String} url The href of the new stylesheet to include
13962     */
13963    swapStyleSheet : function(id, url){
13964        this.removeStyleSheet(id);
13965        var ss = doc.createElement("link");
13966        ss.setAttribute("rel", "stylesheet");
13967        ss.setAttribute("type", "text/css");
13968        ss.setAttribute("id", id);
13969        ss.setAttribute("href", url);
13970        doc.getElementsByTagName("head")[0].appendChild(ss);
13971    },
13972    
13973    /**
13974     * Refresh the rule cache if you have dynamically added stylesheets
13975     * @return {Object} An object (hash) of rules indexed by selector
13976     */
13977    refreshCache : function(){
13978        return this.getRules(true);
13979    },
13980
13981    // private
13982    cacheStyleSheet : function(stylesheet){
13983        if(!rules){
13984            rules = {};
13985        }
13986        try{// try catch for cross domain access issue
13987            var ssRules = stylesheet.cssRules || stylesheet.rules;
13988            for(var j = ssRules.length-1; j >= 0; --j){
13989                rules[ssRules[j].selectorText] = ssRules[j];
13990            }
13991        }catch(e){}
13992    },
13993    
13994    /**
13995     * Gets all css rules for the document
13996     * @param {Boolean} refreshCache true to refresh the internal cache
13997     * @return {Object} An object (hash) of rules indexed by selector
13998     */
13999    getRules : function(refreshCache){
14000                 if(rules == null || refreshCache){
14001                         rules = {};
14002                         var ds = doc.styleSheets;
14003                         for(var i =0, len = ds.length; i < len; i++){
14004                             try{
14005                         this.cacheStyleSheet(ds[i]);
14006                     }catch(e){} 
14007                 }
14008                 }
14009                 return rules;
14010         },
14011         
14012         /**
14013     * Gets an an individual CSS rule by selector(s)
14014     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14015     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14016     * @return {CSSRule} The CSS rule or null if one is not found
14017     */
14018    getRule : function(selector, refreshCache){
14019                 var rs = this.getRules(refreshCache);
14020                 if(!(selector instanceof Array)){
14021                     return rs[selector];
14022                 }
14023                 for(var i = 0; i < selector.length; i++){
14024                         if(rs[selector[i]]){
14025                                 return rs[selector[i]];
14026                         }
14027                 }
14028                 return null;
14029         },
14030         
14031         
14032         /**
14033     * Updates a rule property
14034     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14035     * @param {String} property The css property
14036     * @param {String} value The new value for the property
14037     * @return {Boolean} true If a rule was found and updated
14038     */
14039    updateRule : function(selector, property, value){
14040                 if(!(selector instanceof Array)){
14041                         var rule = this.getRule(selector);
14042                         if(rule){
14043                                 rule.style[property.replace(camelRe, camelFn)] = value;
14044                                 return true;
14045                         }
14046                 }else{
14047                         for(var i = 0; i < selector.length; i++){
14048                                 if(this.updateRule(selector[i], property, value)){
14049                                         return true;
14050                                 }
14051                         }
14052                 }
14053                 return false;
14054         }
14055    };   
14056 }();/*
14057  * Based on:
14058  * Ext JS Library 1.1.1
14059  * Copyright(c) 2006-2007, Ext JS, LLC.
14060  *
14061  * Originally Released Under LGPL - original licence link has changed is not relivant.
14062  *
14063  * Fork - LGPL
14064  * <script type="text/javascript">
14065  */
14066
14067  
14068
14069 /**
14070  * @class Roo.util.ClickRepeater
14071  * @extends Roo.util.Observable
14072  * 
14073  * A wrapper class which can be applied to any element. Fires a "click" event while the
14074  * mouse is pressed. The interval between firings may be specified in the config but
14075  * defaults to 10 milliseconds.
14076  * 
14077  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14078  * 
14079  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14080  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14081  * Similar to an autorepeat key delay.
14082  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14083  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14084  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14085  *           "interval" and "delay" are ignored. "immediate" is honored.
14086  * @cfg {Boolean} preventDefault True to prevent the default click event
14087  * @cfg {Boolean} stopDefault True to stop the default click event
14088  * 
14089  * @history
14090  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14091  *     2007-02-02 jvs Renamed to ClickRepeater
14092  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14093  *
14094  *  @constructor
14095  * @param {String/HTMLElement/Element} el The element to listen on
14096  * @param {Object} config
14097  **/
14098 Roo.util.ClickRepeater = function(el, config)
14099 {
14100     this.el = Roo.get(el);
14101     this.el.unselectable();
14102
14103     Roo.apply(this, config);
14104
14105     this.addEvents({
14106     /**
14107      * @event mousedown
14108      * Fires when the mouse button is depressed.
14109      * @param {Roo.util.ClickRepeater} this
14110      */
14111         "mousedown" : true,
14112     /**
14113      * @event click
14114      * Fires on a specified interval during the time the element is pressed.
14115      * @param {Roo.util.ClickRepeater} this
14116      */
14117         "click" : true,
14118     /**
14119      * @event mouseup
14120      * Fires when the mouse key is released.
14121      * @param {Roo.util.ClickRepeater} this
14122      */
14123         "mouseup" : true
14124     });
14125
14126     this.el.on("mousedown", this.handleMouseDown, this);
14127     if(this.preventDefault || this.stopDefault){
14128         this.el.on("click", function(e){
14129             if(this.preventDefault){
14130                 e.preventDefault();
14131             }
14132             if(this.stopDefault){
14133                 e.stopEvent();
14134             }
14135         }, this);
14136     }
14137
14138     // allow inline handler
14139     if(this.handler){
14140         this.on("click", this.handler,  this.scope || this);
14141     }
14142
14143     Roo.util.ClickRepeater.superclass.constructor.call(this);
14144 };
14145
14146 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14147     interval : 20,
14148     delay: 250,
14149     preventDefault : true,
14150     stopDefault : false,
14151     timer : 0,
14152
14153     // private
14154     handleMouseDown : function(){
14155         clearTimeout(this.timer);
14156         this.el.blur();
14157         if(this.pressClass){
14158             this.el.addClass(this.pressClass);
14159         }
14160         this.mousedownTime = new Date();
14161
14162         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14163         this.el.on("mouseout", this.handleMouseOut, this);
14164
14165         this.fireEvent("mousedown", this);
14166         this.fireEvent("click", this);
14167         
14168         this.timer = this.click.defer(this.delay || this.interval, this);
14169     },
14170
14171     // private
14172     click : function(){
14173         this.fireEvent("click", this);
14174         this.timer = this.click.defer(this.getInterval(), this);
14175     },
14176
14177     // private
14178     getInterval: function(){
14179         if(!this.accelerate){
14180             return this.interval;
14181         }
14182         var pressTime = this.mousedownTime.getElapsed();
14183         if(pressTime < 500){
14184             return 400;
14185         }else if(pressTime < 1700){
14186             return 320;
14187         }else if(pressTime < 2600){
14188             return 250;
14189         }else if(pressTime < 3500){
14190             return 180;
14191         }else if(pressTime < 4400){
14192             return 140;
14193         }else if(pressTime < 5300){
14194             return 80;
14195         }else if(pressTime < 6200){
14196             return 50;
14197         }else{
14198             return 10;
14199         }
14200     },
14201
14202     // private
14203     handleMouseOut : function(){
14204         clearTimeout(this.timer);
14205         if(this.pressClass){
14206             this.el.removeClass(this.pressClass);
14207         }
14208         this.el.on("mouseover", this.handleMouseReturn, this);
14209     },
14210
14211     // private
14212     handleMouseReturn : function(){
14213         this.el.un("mouseover", this.handleMouseReturn);
14214         if(this.pressClass){
14215             this.el.addClass(this.pressClass);
14216         }
14217         this.click();
14218     },
14219
14220     // private
14221     handleMouseUp : function(){
14222         clearTimeout(this.timer);
14223         this.el.un("mouseover", this.handleMouseReturn);
14224         this.el.un("mouseout", this.handleMouseOut);
14225         Roo.get(document).un("mouseup", this.handleMouseUp);
14226         this.el.removeClass(this.pressClass);
14227         this.fireEvent("mouseup", this);
14228     }
14229 });/*
14230  * Based on:
14231  * Ext JS Library 1.1.1
14232  * Copyright(c) 2006-2007, Ext JS, LLC.
14233  *
14234  * Originally Released Under LGPL - original licence link has changed is not relivant.
14235  *
14236  * Fork - LGPL
14237  * <script type="text/javascript">
14238  */
14239
14240  
14241 /**
14242  * @class Roo.KeyNav
14243  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14244  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14245  * way to implement custom navigation schemes for any UI component.</p>
14246  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14247  * pageUp, pageDown, del, home, end.  Usage:</p>
14248  <pre><code>
14249 var nav = new Roo.KeyNav("my-element", {
14250     "left" : function(e){
14251         this.moveLeft(e.ctrlKey);
14252     },
14253     "right" : function(e){
14254         this.moveRight(e.ctrlKey);
14255     },
14256     "enter" : function(e){
14257         this.save();
14258     },
14259     scope : this
14260 });
14261 </code></pre>
14262  * @constructor
14263  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14264  * @param {Object} config The config
14265  */
14266 Roo.KeyNav = function(el, config){
14267     this.el = Roo.get(el);
14268     Roo.apply(this, config);
14269     if(!this.disabled){
14270         this.disabled = true;
14271         this.enable();
14272     }
14273 };
14274
14275 Roo.KeyNav.prototype = {
14276     /**
14277      * @cfg {Boolean} disabled
14278      * True to disable this KeyNav instance (defaults to false)
14279      */
14280     disabled : false,
14281     /**
14282      * @cfg {String} defaultEventAction
14283      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14284      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14285      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14286      */
14287     defaultEventAction: "stopEvent",
14288     /**
14289      * @cfg {Boolean} forceKeyDown
14290      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14291      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14292      * handle keydown instead of keypress.
14293      */
14294     forceKeyDown : false,
14295
14296     // private
14297     prepareEvent : function(e){
14298         var k = e.getKey();
14299         var h = this.keyToHandler[k];
14300         //if(h && this[h]){
14301         //    e.stopPropagation();
14302         //}
14303         if(Roo.isSafari && h && k >= 37 && k <= 40){
14304             e.stopEvent();
14305         }
14306     },
14307
14308     // private
14309     relay : function(e){
14310         var k = e.getKey();
14311         var h = this.keyToHandler[k];
14312         if(h && this[h]){
14313             if(this.doRelay(e, this[h], h) !== true){
14314                 e[this.defaultEventAction]();
14315             }
14316         }
14317     },
14318
14319     // private
14320     doRelay : function(e, h, hname){
14321         return h.call(this.scope || this, e);
14322     },
14323
14324     // possible handlers
14325     enter : false,
14326     left : false,
14327     right : false,
14328     up : false,
14329     down : false,
14330     tab : false,
14331     esc : false,
14332     pageUp : false,
14333     pageDown : false,
14334     del : false,
14335     home : false,
14336     end : false,
14337
14338     // quick lookup hash
14339     keyToHandler : {
14340         37 : "left",
14341         39 : "right",
14342         38 : "up",
14343         40 : "down",
14344         33 : "pageUp",
14345         34 : "pageDown",
14346         46 : "del",
14347         36 : "home",
14348         35 : "end",
14349         13 : "enter",
14350         27 : "esc",
14351         9  : "tab"
14352     },
14353
14354         /**
14355          * Enable this KeyNav
14356          */
14357         enable: function(){
14358                 if(this.disabled){
14359             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14360             // the EventObject will normalize Safari automatically
14361             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14362                 this.el.on("keydown", this.relay,  this);
14363             }else{
14364                 this.el.on("keydown", this.prepareEvent,  this);
14365                 this.el.on("keypress", this.relay,  this);
14366             }
14367                     this.disabled = false;
14368                 }
14369         },
14370
14371         /**
14372          * Disable this KeyNav
14373          */
14374         disable: function(){
14375                 if(!this.disabled){
14376                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14377                 this.el.un("keydown", this.relay);
14378             }else{
14379                 this.el.un("keydown", this.prepareEvent);
14380                 this.el.un("keypress", this.relay);
14381             }
14382                     this.disabled = true;
14383                 }
14384         }
14385 };/*
14386  * Based on:
14387  * Ext JS Library 1.1.1
14388  * Copyright(c) 2006-2007, Ext JS, LLC.
14389  *
14390  * Originally Released Under LGPL - original licence link has changed is not relivant.
14391  *
14392  * Fork - LGPL
14393  * <script type="text/javascript">
14394  */
14395
14396  
14397 /**
14398  * @class Roo.KeyMap
14399  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14400  * The constructor accepts the same config object as defined by {@link #addBinding}.
14401  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14402  * combination it will call the function with this signature (if the match is a multi-key
14403  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14404  * A KeyMap can also handle a string representation of keys.<br />
14405  * Usage:
14406  <pre><code>
14407 // map one key by key code
14408 var map = new Roo.KeyMap("my-element", {
14409     key: 13, // or Roo.EventObject.ENTER
14410     fn: myHandler,
14411     scope: myObject
14412 });
14413
14414 // map multiple keys to one action by string
14415 var map = new Roo.KeyMap("my-element", {
14416     key: "a\r\n\t",
14417     fn: myHandler,
14418     scope: myObject
14419 });
14420
14421 // map multiple keys to multiple actions by strings and array of codes
14422 var map = new Roo.KeyMap("my-element", [
14423     {
14424         key: [10,13],
14425         fn: function(){ alert("Return was pressed"); }
14426     }, {
14427         key: "abc",
14428         fn: function(){ alert('a, b or c was pressed'); }
14429     }, {
14430         key: "\t",
14431         ctrl:true,
14432         shift:true,
14433         fn: function(){ alert('Control + shift + tab was pressed.'); }
14434     }
14435 ]);
14436 </code></pre>
14437  * <b>Note: A KeyMap starts enabled</b>
14438  * @constructor
14439  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14440  * @param {Object} config The config (see {@link #addBinding})
14441  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14442  */
14443 Roo.KeyMap = function(el, config, eventName){
14444     this.el  = Roo.get(el);
14445     this.eventName = eventName || "keydown";
14446     this.bindings = [];
14447     if(config){
14448         this.addBinding(config);
14449     }
14450     this.enable();
14451 };
14452
14453 Roo.KeyMap.prototype = {
14454     /**
14455      * True to stop the event from bubbling and prevent the default browser action if the
14456      * key was handled by the KeyMap (defaults to false)
14457      * @type Boolean
14458      */
14459     stopEvent : false,
14460
14461     /**
14462      * Add a new binding to this KeyMap. The following config object properties are supported:
14463      * <pre>
14464 Property    Type             Description
14465 ----------  ---------------  ----------------------------------------------------------------------
14466 key         String/Array     A single keycode or an array of keycodes to handle
14467 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14468 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14469 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14470 fn          Function         The function to call when KeyMap finds the expected key combination
14471 scope       Object           The scope of the callback function
14472 </pre>
14473      *
14474      * Usage:
14475      * <pre><code>
14476 // Create a KeyMap
14477 var map = new Roo.KeyMap(document, {
14478     key: Roo.EventObject.ENTER,
14479     fn: handleKey,
14480     scope: this
14481 });
14482
14483 //Add a new binding to the existing KeyMap later
14484 map.addBinding({
14485     key: 'abc',
14486     shift: true,
14487     fn: handleKey,
14488     scope: this
14489 });
14490 </code></pre>
14491      * @param {Object/Array} config A single KeyMap config or an array of configs
14492      */
14493         addBinding : function(config){
14494         if(config instanceof Array){
14495             for(var i = 0, len = config.length; i < len; i++){
14496                 this.addBinding(config[i]);
14497             }
14498             return;
14499         }
14500         var keyCode = config.key,
14501             shift = config.shift, 
14502             ctrl = config.ctrl, 
14503             alt = config.alt,
14504             fn = config.fn,
14505             scope = config.scope;
14506         if(typeof keyCode == "string"){
14507             var ks = [];
14508             var keyString = keyCode.toUpperCase();
14509             for(var j = 0, len = keyString.length; j < len; j++){
14510                 ks.push(keyString.charCodeAt(j));
14511             }
14512             keyCode = ks;
14513         }
14514         var keyArray = keyCode instanceof Array;
14515         var handler = function(e){
14516             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14517                 var k = e.getKey();
14518                 if(keyArray){
14519                     for(var i = 0, len = keyCode.length; i < len; i++){
14520                         if(keyCode[i] == k){
14521                           if(this.stopEvent){
14522                               e.stopEvent();
14523                           }
14524                           fn.call(scope || window, k, e);
14525                           return;
14526                         }
14527                     }
14528                 }else{
14529                     if(k == keyCode){
14530                         if(this.stopEvent){
14531                            e.stopEvent();
14532                         }
14533                         fn.call(scope || window, k, e);
14534                     }
14535                 }
14536             }
14537         };
14538         this.bindings.push(handler);  
14539         },
14540
14541     /**
14542      * Shorthand for adding a single key listener
14543      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14544      * following options:
14545      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14546      * @param {Function} fn The function to call
14547      * @param {Object} scope (optional) The scope of the function
14548      */
14549     on : function(key, fn, scope){
14550         var keyCode, shift, ctrl, alt;
14551         if(typeof key == "object" && !(key instanceof Array)){
14552             keyCode = key.key;
14553             shift = key.shift;
14554             ctrl = key.ctrl;
14555             alt = key.alt;
14556         }else{
14557             keyCode = key;
14558         }
14559         this.addBinding({
14560             key: keyCode,
14561             shift: shift,
14562             ctrl: ctrl,
14563             alt: alt,
14564             fn: fn,
14565             scope: scope
14566         })
14567     },
14568
14569     // private
14570     handleKeyDown : function(e){
14571             if(this.enabled){ //just in case
14572             var b = this.bindings;
14573             for(var i = 0, len = b.length; i < len; i++){
14574                 b[i].call(this, e);
14575             }
14576             }
14577         },
14578         
14579         /**
14580          * Returns true if this KeyMap is enabled
14581          * @return {Boolean} 
14582          */
14583         isEnabled : function(){
14584             return this.enabled;  
14585         },
14586         
14587         /**
14588          * Enables this KeyMap
14589          */
14590         enable: function(){
14591                 if(!this.enabled){
14592                     this.el.on(this.eventName, this.handleKeyDown, this);
14593                     this.enabled = true;
14594                 }
14595         },
14596
14597         /**
14598          * Disable this KeyMap
14599          */
14600         disable: function(){
14601                 if(this.enabled){
14602                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14603                     this.enabled = false;
14604                 }
14605         }
14606 };/*
14607  * Based on:
14608  * Ext JS Library 1.1.1
14609  * Copyright(c) 2006-2007, Ext JS, LLC.
14610  *
14611  * Originally Released Under LGPL - original licence link has changed is not relivant.
14612  *
14613  * Fork - LGPL
14614  * <script type="text/javascript">
14615  */
14616
14617  
14618 /**
14619  * @class Roo.util.TextMetrics
14620  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14621  * wide, in pixels, a given block of text will be.
14622  * @singleton
14623  */
14624 Roo.util.TextMetrics = function(){
14625     var shared;
14626     return {
14627         /**
14628          * Measures the size of the specified text
14629          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14630          * that can affect the size of the rendered text
14631          * @param {String} text The text to measure
14632          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14633          * in order to accurately measure the text height
14634          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14635          */
14636         measure : function(el, text, fixedWidth){
14637             if(!shared){
14638                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14639             }
14640             shared.bind(el);
14641             shared.setFixedWidth(fixedWidth || 'auto');
14642             return shared.getSize(text);
14643         },
14644
14645         /**
14646          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14647          * the overhead of multiple calls to initialize the style properties on each measurement.
14648          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14649          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14650          * in order to accurately measure the text height
14651          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14652          */
14653         createInstance : function(el, fixedWidth){
14654             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14655         }
14656     };
14657 }();
14658
14659  
14660
14661 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14662     var ml = new Roo.Element(document.createElement('div'));
14663     document.body.appendChild(ml.dom);
14664     ml.position('absolute');
14665     ml.setLeftTop(-1000, -1000);
14666     ml.hide();
14667
14668     if(fixedWidth){
14669         ml.setWidth(fixedWidth);
14670     }
14671      
14672     var instance = {
14673         /**
14674          * Returns the size of the specified text based on the internal element's style and width properties
14675          * @memberOf Roo.util.TextMetrics.Instance#
14676          * @param {String} text The text to measure
14677          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14678          */
14679         getSize : function(text){
14680             ml.update(text);
14681             var s = ml.getSize();
14682             ml.update('');
14683             return s;
14684         },
14685
14686         /**
14687          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14688          * that can affect the size of the rendered text
14689          * @memberOf Roo.util.TextMetrics.Instance#
14690          * @param {String/HTMLElement} el The element, dom node or id
14691          */
14692         bind : function(el){
14693             ml.setStyle(
14694                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14695             );
14696         },
14697
14698         /**
14699          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14700          * to set a fixed width in order to accurately measure the text height.
14701          * @memberOf Roo.util.TextMetrics.Instance#
14702          * @param {Number} width The width to set on the element
14703          */
14704         setFixedWidth : function(width){
14705             ml.setWidth(width);
14706         },
14707
14708         /**
14709          * Returns the measured width of the specified text
14710          * @memberOf Roo.util.TextMetrics.Instance#
14711          * @param {String} text The text to measure
14712          * @return {Number} width The width in pixels
14713          */
14714         getWidth : function(text){
14715             ml.dom.style.width = 'auto';
14716             return this.getSize(text).width;
14717         },
14718
14719         /**
14720          * Returns the measured height of the specified text.  For multiline text, be sure to call
14721          * {@link #setFixedWidth} if necessary.
14722          * @memberOf Roo.util.TextMetrics.Instance#
14723          * @param {String} text The text to measure
14724          * @return {Number} height The height in pixels
14725          */
14726         getHeight : function(text){
14727             return this.getSize(text).height;
14728         }
14729     };
14730
14731     instance.bind(bindTo);
14732
14733     return instance;
14734 };
14735
14736 // backwards compat
14737 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14738  * Based on:
14739  * Ext JS Library 1.1.1
14740  * Copyright(c) 2006-2007, Ext JS, LLC.
14741  *
14742  * Originally Released Under LGPL - original licence link has changed is not relivant.
14743  *
14744  * Fork - LGPL
14745  * <script type="text/javascript">
14746  */
14747
14748 /**
14749  * @class Roo.state.Provider
14750  * Abstract base class for state provider implementations. This class provides methods
14751  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14752  * Provider interface.
14753  */
14754 Roo.state.Provider = function(){
14755     /**
14756      * @event statechange
14757      * Fires when a state change occurs.
14758      * @param {Provider} this This state provider
14759      * @param {String} key The state key which was changed
14760      * @param {String} value The encoded value for the state
14761      */
14762     this.addEvents({
14763         "statechange": true
14764     });
14765     this.state = {};
14766     Roo.state.Provider.superclass.constructor.call(this);
14767 };
14768 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14769     /**
14770      * Returns the current value for a key
14771      * @param {String} name The key name
14772      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14773      * @return {Mixed} The state data
14774      */
14775     get : function(name, defaultValue){
14776         return typeof this.state[name] == "undefined" ?
14777             defaultValue : this.state[name];
14778     },
14779     
14780     /**
14781      * Clears a value from the state
14782      * @param {String} name The key name
14783      */
14784     clear : function(name){
14785         delete this.state[name];
14786         this.fireEvent("statechange", this, name, null);
14787     },
14788     
14789     /**
14790      * Sets the value for a key
14791      * @param {String} name The key name
14792      * @param {Mixed} value The value to set
14793      */
14794     set : function(name, value){
14795         this.state[name] = value;
14796         this.fireEvent("statechange", this, name, value);
14797     },
14798     
14799     /**
14800      * Decodes a string previously encoded with {@link #encodeValue}.
14801      * @param {String} value The value to decode
14802      * @return {Mixed} The decoded value
14803      */
14804     decodeValue : function(cookie){
14805         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14806         var matches = re.exec(unescape(cookie));
14807         if(!matches || !matches[1]) return; // non state cookie
14808         var type = matches[1];
14809         var v = matches[2];
14810         switch(type){
14811             case "n":
14812                 return parseFloat(v);
14813             case "d":
14814                 return new Date(Date.parse(v));
14815             case "b":
14816                 return (v == "1");
14817             case "a":
14818                 var all = [];
14819                 var values = v.split("^");
14820                 for(var i = 0, len = values.length; i < len; i++){
14821                     all.push(this.decodeValue(values[i]));
14822                 }
14823                 return all;
14824            case "o":
14825                 var all = {};
14826                 var values = v.split("^");
14827                 for(var i = 0, len = values.length; i < len; i++){
14828                     var kv = values[i].split("=");
14829                     all[kv[0]] = this.decodeValue(kv[1]);
14830                 }
14831                 return all;
14832            default:
14833                 return v;
14834         }
14835     },
14836     
14837     /**
14838      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14839      * @param {Mixed} value The value to encode
14840      * @return {String} The encoded value
14841      */
14842     encodeValue : function(v){
14843         var enc;
14844         if(typeof v == "number"){
14845             enc = "n:" + v;
14846         }else if(typeof v == "boolean"){
14847             enc = "b:" + (v ? "1" : "0");
14848         }else if(v instanceof Date){
14849             enc = "d:" + v.toGMTString();
14850         }else if(v instanceof Array){
14851             var flat = "";
14852             for(var i = 0, len = v.length; i < len; i++){
14853                 flat += this.encodeValue(v[i]);
14854                 if(i != len-1) flat += "^";
14855             }
14856             enc = "a:" + flat;
14857         }else if(typeof v == "object"){
14858             var flat = "";
14859             for(var key in v){
14860                 if(typeof v[key] != "function"){
14861                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14862                 }
14863             }
14864             enc = "o:" + flat.substring(0, flat.length-1);
14865         }else{
14866             enc = "s:" + v;
14867         }
14868         return escape(enc);        
14869     }
14870 });
14871
14872 /*
14873  * Based on:
14874  * Ext JS Library 1.1.1
14875  * Copyright(c) 2006-2007, Ext JS, LLC.
14876  *
14877  * Originally Released Under LGPL - original licence link has changed is not relivant.
14878  *
14879  * Fork - LGPL
14880  * <script type="text/javascript">
14881  */
14882 /**
14883  * @class Roo.state.Manager
14884  * This is the global state manager. By default all components that are "state aware" check this class
14885  * for state information if you don't pass them a custom state provider. In order for this class
14886  * to be useful, it must be initialized with a provider when your application initializes.
14887  <pre><code>
14888 // in your initialization function
14889 init : function(){
14890    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14891    ...
14892    // supposed you have a {@link Roo.BorderLayout}
14893    var layout = new Roo.BorderLayout(...);
14894    layout.restoreState();
14895    // or a {Roo.BasicDialog}
14896    var dialog = new Roo.BasicDialog(...);
14897    dialog.restoreState();
14898  </code></pre>
14899  * @singleton
14900  */
14901 Roo.state.Manager = function(){
14902     var provider = new Roo.state.Provider();
14903     
14904     return {
14905         /**
14906          * Configures the default state provider for your application
14907          * @param {Provider} stateProvider The state provider to set
14908          */
14909         setProvider : function(stateProvider){
14910             provider = stateProvider;
14911         },
14912         
14913         /**
14914          * Returns the current value for a key
14915          * @param {String} name The key name
14916          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14917          * @return {Mixed} The state data
14918          */
14919         get : function(key, defaultValue){
14920             return provider.get(key, defaultValue);
14921         },
14922         
14923         /**
14924          * Sets the value for a key
14925          * @param {String} name The key name
14926          * @param {Mixed} value The state data
14927          */
14928          set : function(key, value){
14929             provider.set(key, value);
14930         },
14931         
14932         /**
14933          * Clears a value from the state
14934          * @param {String} name The key name
14935          */
14936         clear : function(key){
14937             provider.clear(key);
14938         },
14939         
14940         /**
14941          * Gets the currently configured state provider
14942          * @return {Provider} The state provider
14943          */
14944         getProvider : function(){
14945             return provider;
14946         }
14947     };
14948 }();
14949 /*
14950  * Based on:
14951  * Ext JS Library 1.1.1
14952  * Copyright(c) 2006-2007, Ext JS, LLC.
14953  *
14954  * Originally Released Under LGPL - original licence link has changed is not relivant.
14955  *
14956  * Fork - LGPL
14957  * <script type="text/javascript">
14958  */
14959 /**
14960  * @class Roo.state.CookieProvider
14961  * @extends Roo.state.Provider
14962  * The default Provider implementation which saves state via cookies.
14963  * <br />Usage:
14964  <pre><code>
14965    var cp = new Roo.state.CookieProvider({
14966        path: "/cgi-bin/",
14967        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14968        domain: "roojs.com"
14969    })
14970    Roo.state.Manager.setProvider(cp);
14971  </code></pre>
14972  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14973  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14974  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14975  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14976  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14977  * domain the page is running on including the 'www' like 'www.roojs.com')
14978  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14979  * @constructor
14980  * Create a new CookieProvider
14981  * @param {Object} config The configuration object
14982  */
14983 Roo.state.CookieProvider = function(config){
14984     Roo.state.CookieProvider.superclass.constructor.call(this);
14985     this.path = "/";
14986     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14987     this.domain = null;
14988     this.secure = false;
14989     Roo.apply(this, config);
14990     this.state = this.readCookies();
14991 };
14992
14993 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14994     // private
14995     set : function(name, value){
14996         if(typeof value == "undefined" || value === null){
14997             this.clear(name);
14998             return;
14999         }
15000         this.setCookie(name, value);
15001         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15002     },
15003
15004     // private
15005     clear : function(name){
15006         this.clearCookie(name);
15007         Roo.state.CookieProvider.superclass.clear.call(this, name);
15008     },
15009
15010     // private
15011     readCookies : function(){
15012         var cookies = {};
15013         var c = document.cookie + ";";
15014         var re = /\s?(.*?)=(.*?);/g;
15015         var matches;
15016         while((matches = re.exec(c)) != null){
15017             var name = matches[1];
15018             var value = matches[2];
15019             if(name && name.substring(0,3) == "ys-"){
15020                 cookies[name.substr(3)] = this.decodeValue(value);
15021             }
15022         }
15023         return cookies;
15024     },
15025
15026     // private
15027     setCookie : function(name, value){
15028         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15029            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15030            ((this.path == null) ? "" : ("; path=" + this.path)) +
15031            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15032            ((this.secure == true) ? "; secure" : "");
15033     },
15034
15035     // private
15036     clearCookie : function(name){
15037         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15038            ((this.path == null) ? "" : ("; path=" + this.path)) +
15039            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15040            ((this.secure == true) ? "; secure" : "");
15041     }
15042 });/*
15043  * Based on:
15044  * Ext JS Library 1.1.1
15045  * Copyright(c) 2006-2007, Ext JS, LLC.
15046  *
15047  * Originally Released Under LGPL - original licence link has changed is not relivant.
15048  *
15049  * Fork - LGPL
15050  * <script type="text/javascript">
15051  */
15052  
15053
15054 /**
15055  * @class Roo.ComponentMgr
15056  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15057  * @singleton
15058  */
15059 Roo.ComponentMgr = function(){
15060     var all = new Roo.util.MixedCollection();
15061
15062     return {
15063         /**
15064          * Registers a component.
15065          * @param {Roo.Component} c The component
15066          */
15067         register : function(c){
15068             all.add(c);
15069         },
15070
15071         /**
15072          * Unregisters a component.
15073          * @param {Roo.Component} c The component
15074          */
15075         unregister : function(c){
15076             all.remove(c);
15077         },
15078
15079         /**
15080          * Returns a component by id
15081          * @param {String} id The component id
15082          */
15083         get : function(id){
15084             return all.get(id);
15085         },
15086
15087         /**
15088          * Registers a function that will be called when a specified component is added to ComponentMgr
15089          * @param {String} id The component id
15090          * @param {Funtction} fn The callback function
15091          * @param {Object} scope The scope of the callback
15092          */
15093         onAvailable : function(id, fn, scope){
15094             all.on("add", function(index, o){
15095                 if(o.id == id){
15096                     fn.call(scope || o, o);
15097                     all.un("add", fn, scope);
15098                 }
15099             });
15100         }
15101     };
15102 }();/*
15103  * Based on:
15104  * Ext JS Library 1.1.1
15105  * Copyright(c) 2006-2007, Ext JS, LLC.
15106  *
15107  * Originally Released Under LGPL - original licence link has changed is not relivant.
15108  *
15109  * Fork - LGPL
15110  * <script type="text/javascript">
15111  */
15112  
15113 /**
15114  * @class Roo.Component
15115  * @extends Roo.util.Observable
15116  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15117  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15118  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15119  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15120  * All visual components (widgets) that require rendering into a layout should subclass Component.
15121  * @constructor
15122  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15123  * 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
15124  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15125  */
15126 Roo.Component = function(config){
15127     config = config || {};
15128     if(config.tagName || config.dom || typeof config == "string"){ // element object
15129         config = {el: config, id: config.id || config};
15130     }
15131     this.initialConfig = config;
15132
15133     Roo.apply(this, config);
15134     this.addEvents({
15135         /**
15136          * @event disable
15137          * Fires after the component is disabled.
15138              * @param {Roo.Component} this
15139              */
15140         disable : true,
15141         /**
15142          * @event enable
15143          * Fires after the component is enabled.
15144              * @param {Roo.Component} this
15145              */
15146         enable : true,
15147         /**
15148          * @event beforeshow
15149          * Fires before the component is shown.  Return false to stop the show.
15150              * @param {Roo.Component} this
15151              */
15152         beforeshow : true,
15153         /**
15154          * @event show
15155          * Fires after the component is shown.
15156              * @param {Roo.Component} this
15157              */
15158         show : true,
15159         /**
15160          * @event beforehide
15161          * Fires before the component is hidden. Return false to stop the hide.
15162              * @param {Roo.Component} this
15163              */
15164         beforehide : true,
15165         /**
15166          * @event hide
15167          * Fires after the component is hidden.
15168              * @param {Roo.Component} this
15169              */
15170         hide : true,
15171         /**
15172          * @event beforerender
15173          * Fires before the component is rendered. Return false to stop the render.
15174              * @param {Roo.Component} this
15175              */
15176         beforerender : true,
15177         /**
15178          * @event render
15179          * Fires after the component is rendered.
15180              * @param {Roo.Component} this
15181              */
15182         render : true,
15183         /**
15184          * @event beforedestroy
15185          * Fires before the component is destroyed. Return false to stop the destroy.
15186              * @param {Roo.Component} this
15187              */
15188         beforedestroy : true,
15189         /**
15190          * @event destroy
15191          * Fires after the component is destroyed.
15192              * @param {Roo.Component} this
15193              */
15194         destroy : true
15195     });
15196     if(!this.id){
15197         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15198     }
15199     Roo.ComponentMgr.register(this);
15200     Roo.Component.superclass.constructor.call(this);
15201     this.initComponent();
15202     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15203         this.render(this.renderTo);
15204         delete this.renderTo;
15205     }
15206 };
15207
15208 /** @private */
15209 Roo.Component.AUTO_ID = 1000;
15210
15211 Roo.extend(Roo.Component, Roo.util.Observable, {
15212     /**
15213      * @scope Roo.Component.prototype
15214      * @type {Boolean}
15215      * true if this component is hidden. Read-only.
15216      */
15217     hidden : false,
15218     /**
15219      * @type {Boolean}
15220      * true if this component is disabled. Read-only.
15221      */
15222     disabled : false,
15223     /**
15224      * @type {Boolean}
15225      * true if this component has been rendered. Read-only.
15226      */
15227     rendered : false,
15228     
15229     /** @cfg {String} disableClass
15230      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15231      */
15232     disabledClass : "x-item-disabled",
15233         /** @cfg {Boolean} allowDomMove
15234          * Whether the component can move the Dom node when rendering (defaults to true).
15235          */
15236     allowDomMove : true,
15237     /** @cfg {String} hideMode (display|visibility)
15238      * How this component should hidden. Supported values are
15239      * "visibility" (css visibility), "offsets" (negative offset position) and
15240      * "display" (css display) - defaults to "display".
15241      */
15242     hideMode: 'display',
15243
15244     /** @private */
15245     ctype : "Roo.Component",
15246
15247     /**
15248      * @cfg {String} actionMode 
15249      * which property holds the element that used for  hide() / show() / disable() / enable()
15250      * default is 'el' 
15251      */
15252     actionMode : "el",
15253
15254     /** @private */
15255     getActionEl : function(){
15256         return this[this.actionMode];
15257     },
15258
15259     initComponent : Roo.emptyFn,
15260     /**
15261      * If this is a lazy rendering component, render it to its container element.
15262      * @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.
15263      */
15264     render : function(container, position){
15265         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15266             if(!container && this.el){
15267                 this.el = Roo.get(this.el);
15268                 container = this.el.dom.parentNode;
15269                 this.allowDomMove = false;
15270             }
15271             this.container = Roo.get(container);
15272             this.rendered = true;
15273             if(position !== undefined){
15274                 if(typeof position == 'number'){
15275                     position = this.container.dom.childNodes[position];
15276                 }else{
15277                     position = Roo.getDom(position);
15278                 }
15279             }
15280             this.onRender(this.container, position || null);
15281             if(this.cls){
15282                 this.el.addClass(this.cls);
15283                 delete this.cls;
15284             }
15285             if(this.style){
15286                 this.el.applyStyles(this.style);
15287                 delete this.style;
15288             }
15289             this.fireEvent("render", this);
15290             this.afterRender(this.container);
15291             if(this.hidden){
15292                 this.hide();
15293             }
15294             if(this.disabled){
15295                 this.disable();
15296             }
15297         }
15298         return this;
15299     },
15300
15301     /** @private */
15302     // default function is not really useful
15303     onRender : function(ct, position){
15304         if(this.el){
15305             this.el = Roo.get(this.el);
15306             if(this.allowDomMove !== false){
15307                 ct.dom.insertBefore(this.el.dom, position);
15308             }
15309         }
15310     },
15311
15312     /** @private */
15313     getAutoCreate : function(){
15314         var cfg = typeof this.autoCreate == "object" ?
15315                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15316         if(this.id && !cfg.id){
15317             cfg.id = this.id;
15318         }
15319         return cfg;
15320     },
15321
15322     /** @private */
15323     afterRender : Roo.emptyFn,
15324
15325     /**
15326      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15327      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15328      */
15329     destroy : function(){
15330         if(this.fireEvent("beforedestroy", this) !== false){
15331             this.purgeListeners();
15332             this.beforeDestroy();
15333             if(this.rendered){
15334                 this.el.removeAllListeners();
15335                 this.el.remove();
15336                 if(this.actionMode == "container"){
15337                     this.container.remove();
15338                 }
15339             }
15340             this.onDestroy();
15341             Roo.ComponentMgr.unregister(this);
15342             this.fireEvent("destroy", this);
15343         }
15344     },
15345
15346         /** @private */
15347     beforeDestroy : function(){
15348
15349     },
15350
15351         /** @private */
15352         onDestroy : function(){
15353
15354     },
15355
15356     /**
15357      * Returns the underlying {@link Roo.Element}.
15358      * @return {Roo.Element} The element
15359      */
15360     getEl : function(){
15361         return this.el;
15362     },
15363
15364     /**
15365      * Returns the id of this component.
15366      * @return {String}
15367      */
15368     getId : function(){
15369         return this.id;
15370     },
15371
15372     /**
15373      * Try to focus this component.
15374      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15375      * @return {Roo.Component} this
15376      */
15377     focus : function(selectText){
15378         if(this.rendered){
15379             this.el.focus();
15380             if(selectText === true){
15381                 this.el.dom.select();
15382             }
15383         }
15384         return this;
15385     },
15386
15387     /** @private */
15388     blur : function(){
15389         if(this.rendered){
15390             this.el.blur();
15391         }
15392         return this;
15393     },
15394
15395     /**
15396      * Disable this component.
15397      * @return {Roo.Component} this
15398      */
15399     disable : function(){
15400         if(this.rendered){
15401             this.onDisable();
15402         }
15403         this.disabled = true;
15404         this.fireEvent("disable", this);
15405         return this;
15406     },
15407
15408         // private
15409     onDisable : function(){
15410         this.getActionEl().addClass(this.disabledClass);
15411         this.el.dom.disabled = true;
15412     },
15413
15414     /**
15415      * Enable this component.
15416      * @return {Roo.Component} this
15417      */
15418     enable : function(){
15419         if(this.rendered){
15420             this.onEnable();
15421         }
15422         this.disabled = false;
15423         this.fireEvent("enable", this);
15424         return this;
15425     },
15426
15427         // private
15428     onEnable : function(){
15429         this.getActionEl().removeClass(this.disabledClass);
15430         this.el.dom.disabled = false;
15431     },
15432
15433     /**
15434      * Convenience function for setting disabled/enabled by boolean.
15435      * @param {Boolean} disabled
15436      */
15437     setDisabled : function(disabled){
15438         this[disabled ? "disable" : "enable"]();
15439     },
15440
15441     /**
15442      * Show this component.
15443      * @return {Roo.Component} this
15444      */
15445     show: function(){
15446         if(this.fireEvent("beforeshow", this) !== false){
15447             this.hidden = false;
15448             if(this.rendered){
15449                 this.onShow();
15450             }
15451             this.fireEvent("show", this);
15452         }
15453         return this;
15454     },
15455
15456     // private
15457     onShow : function(){
15458         var ae = this.getActionEl();
15459         if(this.hideMode == 'visibility'){
15460             ae.dom.style.visibility = "visible";
15461         }else if(this.hideMode == 'offsets'){
15462             ae.removeClass('x-hidden');
15463         }else{
15464             ae.dom.style.display = "";
15465         }
15466     },
15467
15468     /**
15469      * Hide this component.
15470      * @return {Roo.Component} this
15471      */
15472     hide: function(){
15473         if(this.fireEvent("beforehide", this) !== false){
15474             this.hidden = true;
15475             if(this.rendered){
15476                 this.onHide();
15477             }
15478             this.fireEvent("hide", this);
15479         }
15480         return this;
15481     },
15482
15483     // private
15484     onHide : function(){
15485         var ae = this.getActionEl();
15486         if(this.hideMode == 'visibility'){
15487             ae.dom.style.visibility = "hidden";
15488         }else if(this.hideMode == 'offsets'){
15489             ae.addClass('x-hidden');
15490         }else{
15491             ae.dom.style.display = "none";
15492         }
15493     },
15494
15495     /**
15496      * Convenience function to hide or show this component by boolean.
15497      * @param {Boolean} visible True to show, false to hide
15498      * @return {Roo.Component} this
15499      */
15500     setVisible: function(visible){
15501         if(visible) {
15502             this.show();
15503         }else{
15504             this.hide();
15505         }
15506         return this;
15507     },
15508
15509     /**
15510      * Returns true if this component is visible.
15511      */
15512     isVisible : function(){
15513         return this.getActionEl().isVisible();
15514     },
15515
15516     cloneConfig : function(overrides){
15517         overrides = overrides || {};
15518         var id = overrides.id || Roo.id();
15519         var cfg = Roo.applyIf(overrides, this.initialConfig);
15520         cfg.id = id; // prevent dup id
15521         return new this.constructor(cfg);
15522     }
15523 });/*
15524  * Based on:
15525  * Ext JS Library 1.1.1
15526  * Copyright(c) 2006-2007, Ext JS, LLC.
15527  *
15528  * Originally Released Under LGPL - original licence link has changed is not relivant.
15529  *
15530  * Fork - LGPL
15531  * <script type="text/javascript">
15532  */
15533
15534 /**
15535  * @class Roo.BoxComponent
15536  * @extends Roo.Component
15537  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15538  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15539  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15540  * layout containers.
15541  * @constructor
15542  * @param {Roo.Element/String/Object} config The configuration options.
15543  */
15544 Roo.BoxComponent = function(config){
15545     Roo.Component.call(this, config);
15546     this.addEvents({
15547         /**
15548          * @event resize
15549          * Fires after the component is resized.
15550              * @param {Roo.Component} this
15551              * @param {Number} adjWidth The box-adjusted width that was set
15552              * @param {Number} adjHeight The box-adjusted height that was set
15553              * @param {Number} rawWidth The width that was originally specified
15554              * @param {Number} rawHeight The height that was originally specified
15555              */
15556         resize : true,
15557         /**
15558          * @event move
15559          * Fires after the component is moved.
15560              * @param {Roo.Component} this
15561              * @param {Number} x The new x position
15562              * @param {Number} y The new y position
15563              */
15564         move : true
15565     });
15566 };
15567
15568 Roo.extend(Roo.BoxComponent, Roo.Component, {
15569     // private, set in afterRender to signify that the component has been rendered
15570     boxReady : false,
15571     // private, used to defer height settings to subclasses
15572     deferHeight: false,
15573     /** @cfg {Number} width
15574      * width (optional) size of component
15575      */
15576      /** @cfg {Number} height
15577      * height (optional) size of component
15578      */
15579      
15580     /**
15581      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15582      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15583      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15584      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15585      * @return {Roo.BoxComponent} this
15586      */
15587     setSize : function(w, h){
15588         // support for standard size objects
15589         if(typeof w == 'object'){
15590             h = w.height;
15591             w = w.width;
15592         }
15593         // not rendered
15594         if(!this.boxReady){
15595             this.width = w;
15596             this.height = h;
15597             return this;
15598         }
15599
15600         // prevent recalcs when not needed
15601         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15602             return this;
15603         }
15604         this.lastSize = {width: w, height: h};
15605
15606         var adj = this.adjustSize(w, h);
15607         var aw = adj.width, ah = adj.height;
15608         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15609             var rz = this.getResizeEl();
15610             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15611                 rz.setSize(aw, ah);
15612             }else if(!this.deferHeight && ah !== undefined){
15613                 rz.setHeight(ah);
15614             }else if(aw !== undefined){
15615                 rz.setWidth(aw);
15616             }
15617             this.onResize(aw, ah, w, h);
15618             this.fireEvent('resize', this, aw, ah, w, h);
15619         }
15620         return this;
15621     },
15622
15623     /**
15624      * Gets the current size of the component's underlying element.
15625      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15626      */
15627     getSize : function(){
15628         return this.el.getSize();
15629     },
15630
15631     /**
15632      * Gets the current XY position of the component's underlying element.
15633      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15634      * @return {Array} The XY position of the element (e.g., [100, 200])
15635      */
15636     getPosition : function(local){
15637         if(local === true){
15638             return [this.el.getLeft(true), this.el.getTop(true)];
15639         }
15640         return this.xy || this.el.getXY();
15641     },
15642
15643     /**
15644      * Gets the current box measurements of the component's underlying element.
15645      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15646      * @returns {Object} box An object in the format {x, y, width, height}
15647      */
15648     getBox : function(local){
15649         var s = this.el.getSize();
15650         if(local){
15651             s.x = this.el.getLeft(true);
15652             s.y = this.el.getTop(true);
15653         }else{
15654             var xy = this.xy || this.el.getXY();
15655             s.x = xy[0];
15656             s.y = xy[1];
15657         }
15658         return s;
15659     },
15660
15661     /**
15662      * Sets the current box measurements of the component's underlying element.
15663      * @param {Object} box An object in the format {x, y, width, height}
15664      * @returns {Roo.BoxComponent} this
15665      */
15666     updateBox : function(box){
15667         this.setSize(box.width, box.height);
15668         this.setPagePosition(box.x, box.y);
15669         return this;
15670     },
15671
15672     // protected
15673     getResizeEl : function(){
15674         return this.resizeEl || this.el;
15675     },
15676
15677     // protected
15678     getPositionEl : function(){
15679         return this.positionEl || this.el;
15680     },
15681
15682     /**
15683      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15684      * This method fires the move event.
15685      * @param {Number} left The new left
15686      * @param {Number} top The new top
15687      * @returns {Roo.BoxComponent} this
15688      */
15689     setPosition : function(x, y){
15690         this.x = x;
15691         this.y = y;
15692         if(!this.boxReady){
15693             return this;
15694         }
15695         var adj = this.adjustPosition(x, y);
15696         var ax = adj.x, ay = adj.y;
15697
15698         var el = this.getPositionEl();
15699         if(ax !== undefined || ay !== undefined){
15700             if(ax !== undefined && ay !== undefined){
15701                 el.setLeftTop(ax, ay);
15702             }else if(ax !== undefined){
15703                 el.setLeft(ax);
15704             }else if(ay !== undefined){
15705                 el.setTop(ay);
15706             }
15707             this.onPosition(ax, ay);
15708             this.fireEvent('move', this, ax, ay);
15709         }
15710         return this;
15711     },
15712
15713     /**
15714      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15715      * This method fires the move event.
15716      * @param {Number} x The new x position
15717      * @param {Number} y The new y position
15718      * @returns {Roo.BoxComponent} this
15719      */
15720     setPagePosition : function(x, y){
15721         this.pageX = x;
15722         this.pageY = y;
15723         if(!this.boxReady){
15724             return;
15725         }
15726         if(x === undefined || y === undefined){ // cannot translate undefined points
15727             return;
15728         }
15729         var p = this.el.translatePoints(x, y);
15730         this.setPosition(p.left, p.top);
15731         return this;
15732     },
15733
15734     // private
15735     onRender : function(ct, position){
15736         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15737         if(this.resizeEl){
15738             this.resizeEl = Roo.get(this.resizeEl);
15739         }
15740         if(this.positionEl){
15741             this.positionEl = Roo.get(this.positionEl);
15742         }
15743     },
15744
15745     // private
15746     afterRender : function(){
15747         Roo.BoxComponent.superclass.afterRender.call(this);
15748         this.boxReady = true;
15749         this.setSize(this.width, this.height);
15750         if(this.x || this.y){
15751             this.setPosition(this.x, this.y);
15752         }
15753         if(this.pageX || this.pageY){
15754             this.setPagePosition(this.pageX, this.pageY);
15755         }
15756     },
15757
15758     /**
15759      * Force the component's size to recalculate based on the underlying element's current height and width.
15760      * @returns {Roo.BoxComponent} this
15761      */
15762     syncSize : function(){
15763         delete this.lastSize;
15764         this.setSize(this.el.getWidth(), this.el.getHeight());
15765         return this;
15766     },
15767
15768     /**
15769      * Called after the component is resized, this method is empty by default but can be implemented by any
15770      * subclass that needs to perform custom logic after a resize occurs.
15771      * @param {Number} adjWidth The box-adjusted width that was set
15772      * @param {Number} adjHeight The box-adjusted height that was set
15773      * @param {Number} rawWidth The width that was originally specified
15774      * @param {Number} rawHeight The height that was originally specified
15775      */
15776     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15777
15778     },
15779
15780     /**
15781      * Called after the component is moved, this method is empty by default but can be implemented by any
15782      * subclass that needs to perform custom logic after a move occurs.
15783      * @param {Number} x The new x position
15784      * @param {Number} y The new y position
15785      */
15786     onPosition : function(x, y){
15787
15788     },
15789
15790     // private
15791     adjustSize : function(w, h){
15792         if(this.autoWidth){
15793             w = 'auto';
15794         }
15795         if(this.autoHeight){
15796             h = 'auto';
15797         }
15798         return {width : w, height: h};
15799     },
15800
15801     // private
15802     adjustPosition : function(x, y){
15803         return {x : x, y: y};
15804     }
15805 });/*
15806  * Original code for Roojs - LGPL
15807  * <script type="text/javascript">
15808  */
15809  
15810 /**
15811  * @class Roo.XComponent
15812  * A delayed Element creator...
15813  * Or a way to group chunks of interface together.
15814  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15815  *  used in conjunction with XComponent.build() it will create an instance of each element,
15816  *  then call addxtype() to build the User interface.
15817  * 
15818  * Mypart.xyx = new Roo.XComponent({
15819
15820     parent : 'Mypart.xyz', // empty == document.element.!!
15821     order : '001',
15822     name : 'xxxx'
15823     region : 'xxxx'
15824     disabled : function() {} 
15825      
15826     tree : function() { // return an tree of xtype declared components
15827         var MODULE = this;
15828         return 
15829         {
15830             xtype : 'NestedLayoutPanel',
15831             // technicall
15832         }
15833      ]
15834  *})
15835  *
15836  *
15837  * It can be used to build a big heiracy, with parent etc.
15838  * or you can just use this to render a single compoent to a dom element
15839  * MYPART.render(Roo.Element | String(id) | dom_element )
15840  *
15841  *
15842  * Usage patterns.
15843  *
15844  * Classic Roo
15845  *
15846  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15847  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15848  *
15849  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15850  *
15851  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15852  * - if mulitple topModules exist, the last one is defined as the top module.
15853  *
15854  * Embeded Roo
15855  * 
15856  * When the top level or multiple modules are to embedded into a existing HTML page,
15857  * the parent element can container '#id' of the element where the module will be drawn.
15858  *
15859  * Bootstrap Roo
15860  *
15861  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15862  * it relies more on a include mechanism, where sub modules are included into an outer page.
15863  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15864  * 
15865  * Bootstrap Roo Included elements
15866  *
15867  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15868  * hence confusing the component builder as it thinks there are multiple top level elements. 
15869  *
15870  * 
15871  * 
15872  * @extends Roo.util.Observable
15873  * @constructor
15874  * @param cfg {Object} configuration of component
15875  * 
15876  */
15877 Roo.XComponent = function(cfg) {
15878     Roo.apply(this, cfg);
15879     this.addEvents({ 
15880         /**
15881              * @event built
15882              * Fires when this the componnt is built
15883              * @param {Roo.XComponent} c the component
15884              */
15885         'built' : true
15886         
15887     });
15888     this.region = this.region || 'center'; // default..
15889     Roo.XComponent.register(this);
15890     this.modules = false;
15891     this.el = false; // where the layout goes..
15892     
15893     
15894 }
15895 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15896     /**
15897      * @property el
15898      * The created element (with Roo.factory())
15899      * @type {Roo.Layout}
15900      */
15901     el  : false,
15902     
15903     /**
15904      * @property el
15905      * for BC  - use el in new code
15906      * @type {Roo.Layout}
15907      */
15908     panel : false,
15909     
15910     /**
15911      * @property layout
15912      * for BC  - use el in new code
15913      * @type {Roo.Layout}
15914      */
15915     layout : false,
15916     
15917      /**
15918      * @cfg {Function|boolean} disabled
15919      * If this module is disabled by some rule, return true from the funtion
15920      */
15921     disabled : false,
15922     
15923     /**
15924      * @cfg {String} parent 
15925      * Name of parent element which it get xtype added to..
15926      */
15927     parent: false,
15928     
15929     /**
15930      * @cfg {String} order
15931      * Used to set the order in which elements are created (usefull for multiple tabs)
15932      */
15933     
15934     order : false,
15935     /**
15936      * @cfg {String} name
15937      * String to display while loading.
15938      */
15939     name : false,
15940     /**
15941      * @cfg {String} region
15942      * Region to render component to (defaults to center)
15943      */
15944     region : 'center',
15945     
15946     /**
15947      * @cfg {Array} items
15948      * A single item array - the first element is the root of the tree..
15949      * It's done this way to stay compatible with the Xtype system...
15950      */
15951     items : false,
15952     
15953     /**
15954      * @property _tree
15955      * The method that retuns the tree of parts that make up this compoennt 
15956      * @type {function}
15957      */
15958     _tree  : false,
15959     
15960      /**
15961      * render
15962      * render element to dom or tree
15963      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15964      */
15965     
15966     render : function(el)
15967     {
15968         
15969         el = el || false;
15970         var hp = this.parent ? 1 : 0;
15971         Roo.debug &&  Roo.log(this);
15972         
15973         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15974             // if parent is a '#.....' string, then let's use that..
15975             var ename = this.parent.substr(1);
15976             this.parent = false;
15977             Roo.debug && Roo.log(ename);
15978             switch (ename) {
15979                 case 'bootstrap-body' :
15980                     if (typeof(Roo.bootstrap.Body) != 'undefined') {
15981                         this.parent = { el :  new  Roo.bootstrap.Body() };
15982                         Roo.debug && Roo.log("setting el to doc body");
15983                          
15984                     } else {
15985                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
15986                     }
15987                     break;
15988                 case 'bootstrap':
15989                     this.parent = { el : true};
15990                     // fall through
15991                 default:
15992                     el = Roo.get(ename);
15993                     break;
15994             }
15995                 
15996             
15997             if (!el && !this.parent) {
15998                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
15999                 return;
16000             }
16001         }
16002         Roo.debug && Roo.log("EL:");
16003         Roo.debug && Roo.log(el);
16004         Roo.debug && Roo.log("this.parent.el:");
16005         Roo.debug && Roo.log(this.parent.el);
16006         
16007         var tree = this._tree ? this._tree() : this.tree();
16008
16009         // altertive root elements ??? - we need a better way to indicate these.
16010         var is_alt = (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16011                         (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16012         
16013         if (!this.parent && is_alt) {
16014             //el = Roo.get(document.body);
16015             this.parent = { el : true };
16016         }
16017             
16018             
16019         
16020         if (!this.parent) {
16021             
16022             Roo.debug && Roo.log("no parent - creating one");
16023             
16024             el = el ? Roo.get(el) : false;      
16025             
16026             // it's a top level one..
16027             this.parent =  {
16028                 el : new Roo.BorderLayout(el || document.body, {
16029                 
16030                      center: {
16031                          titlebar: false,
16032                          autoScroll:false,
16033                          closeOnTab: true,
16034                          tabPosition: 'top',
16035                           //resizeTabs: true,
16036                          alwaysShowTabs: el && hp? false :  true,
16037                          hideTabs: el || !hp ? true :  false,
16038                          minTabWidth: 140
16039                      }
16040                  })
16041             }
16042         }
16043         
16044         if (!this.parent.el) {
16045                 // probably an old style ctor, which has been disabled.
16046                 return;
16047
16048         }
16049                 // The 'tree' method is  '_tree now' 
16050             
16051         tree.region = tree.region || this.region;
16052         
16053         if (this.parent.el === true) {
16054             // bootstrap... - body..
16055             this.parent.el = Roo.factory(tree);
16056         }
16057         
16058         this.el = this.parent.el.addxtype(tree);
16059         this.fireEvent('built', this);
16060         
16061         this.panel = this.el;
16062         this.layout = this.panel.layout;
16063         this.parentLayout = this.parent.layout  || false;  
16064          
16065     }
16066     
16067 });
16068
16069 Roo.apply(Roo.XComponent, {
16070     /**
16071      * @property  hideProgress
16072      * true to disable the building progress bar.. usefull on single page renders.
16073      * @type Boolean
16074      */
16075     hideProgress : false,
16076     /**
16077      * @property  buildCompleted
16078      * True when the builder has completed building the interface.
16079      * @type Boolean
16080      */
16081     buildCompleted : false,
16082      
16083     /**
16084      * @property  topModule
16085      * the upper most module - uses document.element as it's constructor.
16086      * @type Object
16087      */
16088      
16089     topModule  : false,
16090       
16091     /**
16092      * @property  modules
16093      * array of modules to be created by registration system.
16094      * @type {Array} of Roo.XComponent
16095      */
16096     
16097     modules : [],
16098     /**
16099      * @property  elmodules
16100      * array of modules to be created by which use #ID 
16101      * @type {Array} of Roo.XComponent
16102      */
16103      
16104     elmodules : [],
16105
16106      /**
16107      * @property  build_from_html
16108      * Build elements from html - used by bootstrap HTML stuff 
16109      *    - this is cleared after build is completed
16110      * @type {boolean} true  (default false)
16111      */
16112      
16113     build_from_html : false,
16114
16115     /**
16116      * Register components to be built later.
16117      *
16118      * This solves the following issues
16119      * - Building is not done on page load, but after an authentication process has occured.
16120      * - Interface elements are registered on page load
16121      * - Parent Interface elements may not be loaded before child, so this handles that..
16122      * 
16123      *
16124      * example:
16125      * 
16126      * MyApp.register({
16127           order : '000001',
16128           module : 'Pman.Tab.projectMgr',
16129           region : 'center',
16130           parent : 'Pman.layout',
16131           disabled : false,  // or use a function..
16132         })
16133      
16134      * * @param {Object} details about module
16135      */
16136     register : function(obj) {
16137                 
16138         Roo.XComponent.event.fireEvent('register', obj);
16139         switch(typeof(obj.disabled) ) {
16140                 
16141             case 'undefined':
16142                 break;
16143             
16144             case 'function':
16145                 if ( obj.disabled() ) {
16146                         return;
16147                 }
16148                 break;
16149             
16150             default:
16151                 if (obj.disabled) {
16152                         return;
16153                 }
16154                 break;
16155         }
16156                 
16157         this.modules.push(obj);
16158          
16159     },
16160     /**
16161      * convert a string to an object..
16162      * eg. 'AAA.BBB' -> finds AAA.BBB
16163
16164      */
16165     
16166     toObject : function(str)
16167     {
16168         if (!str || typeof(str) == 'object') {
16169             return str;
16170         }
16171         if (str.substring(0,1) == '#') {
16172             return str;
16173         }
16174
16175         var ar = str.split('.');
16176         var rt, o;
16177         rt = ar.shift();
16178             /** eval:var:o */
16179         try {
16180             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16181         } catch (e) {
16182             throw "Module not found : " + str;
16183         }
16184         
16185         if (o === false) {
16186             throw "Module not found : " + str;
16187         }
16188         Roo.each(ar, function(e) {
16189             if (typeof(o[e]) == 'undefined') {
16190                 throw "Module not found : " + str;
16191             }
16192             o = o[e];
16193         });
16194         
16195         return o;
16196         
16197     },
16198     
16199     
16200     /**
16201      * move modules into their correct place in the tree..
16202      * 
16203      */
16204     preBuild : function ()
16205     {
16206         var _t = this;
16207         Roo.each(this.modules , function (obj)
16208         {
16209             Roo.XComponent.event.fireEvent('beforebuild', obj);
16210             
16211             var opar = obj.parent;
16212             try { 
16213                 obj.parent = this.toObject(opar);
16214             } catch(e) {
16215                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16216                 return;
16217             }
16218             
16219             if (!obj.parent) {
16220                 Roo.debug && Roo.log("GOT top level module");
16221                 Roo.debug && Roo.log(obj);
16222                 obj.modules = new Roo.util.MixedCollection(false, 
16223                     function(o) { return o.order + '' }
16224                 );
16225                 this.topModule = obj;
16226                 return;
16227             }
16228                         // parent is a string (usually a dom element name..)
16229             if (typeof(obj.parent) == 'string') {
16230                 this.elmodules.push(obj);
16231                 return;
16232             }
16233             if (obj.parent.constructor != Roo.XComponent) {
16234                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16235             }
16236             if (!obj.parent.modules) {
16237                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16238                     function(o) { return o.order + '' }
16239                 );
16240             }
16241             if (obj.parent.disabled) {
16242                 obj.disabled = true;
16243             }
16244             obj.parent.modules.add(obj);
16245         }, this);
16246     },
16247     
16248      /**
16249      * make a list of modules to build.
16250      * @return {Array} list of modules. 
16251      */ 
16252     
16253     buildOrder : function()
16254     {
16255         var _this = this;
16256         var cmp = function(a,b) {   
16257             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16258         };
16259         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16260             throw "No top level modules to build";
16261         }
16262         
16263         // make a flat list in order of modules to build.
16264         var mods = this.topModule ? [ this.topModule ] : [];
16265                 
16266         
16267         // elmodules (is a list of DOM based modules )
16268         Roo.each(this.elmodules, function(e) {
16269             mods.push(e);
16270             if (!this.topModule &&
16271                 typeof(e.parent) == 'string' &&
16272                 e.parent.substring(0,1) == '#' &&
16273                 Roo.get(e.parent.substr(1))
16274                ) {
16275                 
16276                 _this.topModule = e;
16277             }
16278             
16279         });
16280
16281         
16282         // add modules to their parents..
16283         var addMod = function(m) {
16284             Roo.debug && Roo.log("build Order: add: " + m.name);
16285                 
16286             mods.push(m);
16287             if (m.modules && !m.disabled) {
16288                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16289                 m.modules.keySort('ASC',  cmp );
16290                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16291     
16292                 m.modules.each(addMod);
16293             } else {
16294                 Roo.debug && Roo.log("build Order: no child modules");
16295             }
16296             // not sure if this is used any more..
16297             if (m.finalize) {
16298                 m.finalize.name = m.name + " (clean up) ";
16299                 mods.push(m.finalize);
16300             }
16301             
16302         }
16303         if (this.topModule && this.topModule.modules) { 
16304             this.topModule.modules.keySort('ASC',  cmp );
16305             this.topModule.modules.each(addMod);
16306         } 
16307         return mods;
16308     },
16309     
16310      /**
16311      * Build the registered modules.
16312      * @param {Object} parent element.
16313      * @param {Function} optional method to call after module has been added.
16314      * 
16315      */ 
16316    
16317     build : function(opts) 
16318     {
16319         
16320         if (typeof(opts) != 'undefined') {
16321             Roo.apply(this,opts);
16322         }
16323         
16324         this.preBuild();
16325         var mods = this.buildOrder();
16326       
16327         //this.allmods = mods;
16328         //Roo.debug && Roo.log(mods);
16329         //return;
16330         if (!mods.length) { // should not happen
16331             throw "NO modules!!!";
16332         }
16333         
16334         
16335         var msg = "Building Interface...";
16336         // flash it up as modal - so we store the mask!?
16337         if (!this.hideProgress && Roo.MessageBox) {
16338             Roo.MessageBox.show({ title: 'loading' });
16339             Roo.MessageBox.show({
16340                title: "Please wait...",
16341                msg: msg,
16342                width:450,
16343                progress:true,
16344                closable:false,
16345                modal: false
16346               
16347             });
16348         }
16349         var total = mods.length;
16350         
16351         var _this = this;
16352         var progressRun = function() {
16353             if (!mods.length) {
16354                 Roo.debug && Roo.log('hide?');
16355                 if (!this.hideProgress && Roo.MessageBox) {
16356                     Roo.MessageBox.hide();
16357                 }
16358                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16359                 
16360                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16361                 
16362                 // THE END...
16363                 return false;   
16364             }
16365             
16366             var m = mods.shift();
16367             
16368             
16369             Roo.debug && Roo.log(m);
16370             // not sure if this is supported any more.. - modules that are are just function
16371             if (typeof(m) == 'function') { 
16372                 m.call(this);
16373                 return progressRun.defer(10, _this);
16374             } 
16375             
16376             
16377             msg = "Building Interface " + (total  - mods.length) + 
16378                     " of " + total + 
16379                     (m.name ? (' - ' + m.name) : '');
16380                         Roo.debug && Roo.log(msg);
16381             if (!this.hideProgress &&  Roo.MessageBox) { 
16382                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16383             }
16384             
16385          
16386             // is the module disabled?
16387             var disabled = (typeof(m.disabled) == 'function') ?
16388                 m.disabled.call(m.module.disabled) : m.disabled;    
16389             
16390             
16391             if (disabled) {
16392                 return progressRun(); // we do not update the display!
16393             }
16394             
16395             // now build 
16396             
16397                         
16398                         
16399             m.render();
16400             // it's 10 on top level, and 1 on others??? why...
16401             return progressRun.defer(10, _this);
16402              
16403         }
16404         progressRun.defer(1, _this);
16405      
16406         
16407         
16408     },
16409         
16410         
16411         /**
16412          * Event Object.
16413          *
16414          *
16415          */
16416         event: false, 
16417     /**
16418          * wrapper for event.on - aliased later..  
16419          * Typically use to register a event handler for register:
16420          *
16421          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16422          *
16423          */
16424     on : false
16425    
16426     
16427     
16428 });
16429
16430 Roo.XComponent.event = new Roo.util.Observable({
16431                 events : { 
16432                         /**
16433                          * @event register
16434                          * Fires when an Component is registered,
16435                          * set the disable property on the Component to stop registration.
16436                          * @param {Roo.XComponent} c the component being registerd.
16437                          * 
16438                          */
16439                         'register' : true,
16440             /**
16441                          * @event beforebuild
16442                          * Fires before each Component is built
16443                          * can be used to apply permissions.
16444                          * @param {Roo.XComponent} c the component being registerd.
16445                          * 
16446                          */
16447                         'beforebuild' : true,
16448                         /**
16449                          * @event buildcomplete
16450                          * Fires on the top level element when all elements have been built
16451                          * @param {Roo.XComponent} the top level component.
16452                          */
16453                         'buildcomplete' : true
16454                         
16455                 }
16456 });
16457
16458 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16459  /*
16460  * Based on:
16461  * Ext JS Library 1.1.1
16462  * Copyright(c) 2006-2007, Ext JS, LLC.
16463  *
16464  * Originally Released Under LGPL - original licence link has changed is not relivant.
16465  *
16466  * Fork - LGPL
16467  * <script type="text/javascript">
16468  */
16469
16470
16471
16472 /*
16473  * These classes are derivatives of the similarly named classes in the YUI Library.
16474  * The original license:
16475  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16476  * Code licensed under the BSD License:
16477  * http://developer.yahoo.net/yui/license.txt
16478  */
16479
16480 (function() {
16481
16482 var Event=Roo.EventManager;
16483 var Dom=Roo.lib.Dom;
16484
16485 /**
16486  * @class Roo.dd.DragDrop
16487  * @extends Roo.util.Observable
16488  * Defines the interface and base operation of items that that can be
16489  * dragged or can be drop targets.  It was designed to be extended, overriding
16490  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16491  * Up to three html elements can be associated with a DragDrop instance:
16492  * <ul>
16493  * <li>linked element: the element that is passed into the constructor.
16494  * This is the element which defines the boundaries for interaction with
16495  * other DragDrop objects.</li>
16496  * <li>handle element(s): The drag operation only occurs if the element that
16497  * was clicked matches a handle element.  By default this is the linked
16498  * element, but there are times that you will want only a portion of the
16499  * linked element to initiate the drag operation, and the setHandleElId()
16500  * method provides a way to define this.</li>
16501  * <li>drag element: this represents the element that would be moved along
16502  * with the cursor during a drag operation.  By default, this is the linked
16503  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16504  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16505  * </li>
16506  * </ul>
16507  * This class should not be instantiated until the onload event to ensure that
16508  * the associated elements are available.
16509  * The following would define a DragDrop obj that would interact with any
16510  * other DragDrop obj in the "group1" group:
16511  * <pre>
16512  *  dd = new Roo.dd.DragDrop("div1", "group1");
16513  * </pre>
16514  * Since none of the event handlers have been implemented, nothing would
16515  * actually happen if you were to run the code above.  Normally you would
16516  * override this class or one of the default implementations, but you can
16517  * also override the methods you want on an instance of the class...
16518  * <pre>
16519  *  dd.onDragDrop = function(e, id) {
16520  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16521  *  }
16522  * </pre>
16523  * @constructor
16524  * @param {String} id of the element that is linked to this instance
16525  * @param {String} sGroup the group of related DragDrop objects
16526  * @param {object} config an object containing configurable attributes
16527  *                Valid properties for DragDrop:
16528  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16529  */
16530 Roo.dd.DragDrop = function(id, sGroup, config) {
16531     if (id) {
16532         this.init(id, sGroup, config);
16533     }
16534     
16535 };
16536
16537 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16538
16539     /**
16540      * The id of the element associated with this object.  This is what we
16541      * refer to as the "linked element" because the size and position of
16542      * this element is used to determine when the drag and drop objects have
16543      * interacted.
16544      * @property id
16545      * @type String
16546      */
16547     id: null,
16548
16549     /**
16550      * Configuration attributes passed into the constructor
16551      * @property config
16552      * @type object
16553      */
16554     config: null,
16555
16556     /**
16557      * The id of the element that will be dragged.  By default this is same
16558      * as the linked element , but could be changed to another element. Ex:
16559      * Roo.dd.DDProxy
16560      * @property dragElId
16561      * @type String
16562      * @private
16563      */
16564     dragElId: null,
16565
16566     /**
16567      * the id of the element that initiates the drag operation.  By default
16568      * this is the linked element, but could be changed to be a child of this
16569      * element.  This lets us do things like only starting the drag when the
16570      * header element within the linked html element is clicked.
16571      * @property handleElId
16572      * @type String
16573      * @private
16574      */
16575     handleElId: null,
16576
16577     /**
16578      * An associative array of HTML tags that will be ignored if clicked.
16579      * @property invalidHandleTypes
16580      * @type {string: string}
16581      */
16582     invalidHandleTypes: null,
16583
16584     /**
16585      * An associative array of ids for elements that will be ignored if clicked
16586      * @property invalidHandleIds
16587      * @type {string: string}
16588      */
16589     invalidHandleIds: null,
16590
16591     /**
16592      * An indexted array of css class names for elements that will be ignored
16593      * if clicked.
16594      * @property invalidHandleClasses
16595      * @type string[]
16596      */
16597     invalidHandleClasses: null,
16598
16599     /**
16600      * The linked element's absolute X position at the time the drag was
16601      * started
16602      * @property startPageX
16603      * @type int
16604      * @private
16605      */
16606     startPageX: 0,
16607
16608     /**
16609      * The linked element's absolute X position at the time the drag was
16610      * started
16611      * @property startPageY
16612      * @type int
16613      * @private
16614      */
16615     startPageY: 0,
16616
16617     /**
16618      * The group defines a logical collection of DragDrop objects that are
16619      * related.  Instances only get events when interacting with other
16620      * DragDrop object in the same group.  This lets us define multiple
16621      * groups using a single DragDrop subclass if we want.
16622      * @property groups
16623      * @type {string: string}
16624      */
16625     groups: null,
16626
16627     /**
16628      * Individual drag/drop instances can be locked.  This will prevent
16629      * onmousedown start drag.
16630      * @property locked
16631      * @type boolean
16632      * @private
16633      */
16634     locked: false,
16635
16636     /**
16637      * Lock this instance
16638      * @method lock
16639      */
16640     lock: function() { this.locked = true; },
16641
16642     /**
16643      * Unlock this instace
16644      * @method unlock
16645      */
16646     unlock: function() { this.locked = false; },
16647
16648     /**
16649      * By default, all insances can be a drop target.  This can be disabled by
16650      * setting isTarget to false.
16651      * @method isTarget
16652      * @type boolean
16653      */
16654     isTarget: true,
16655
16656     /**
16657      * The padding configured for this drag and drop object for calculating
16658      * the drop zone intersection with this object.
16659      * @method padding
16660      * @type int[]
16661      */
16662     padding: null,
16663
16664     /**
16665      * Cached reference to the linked element
16666      * @property _domRef
16667      * @private
16668      */
16669     _domRef: null,
16670
16671     /**
16672      * Internal typeof flag
16673      * @property __ygDragDrop
16674      * @private
16675      */
16676     __ygDragDrop: true,
16677
16678     /**
16679      * Set to true when horizontal contraints are applied
16680      * @property constrainX
16681      * @type boolean
16682      * @private
16683      */
16684     constrainX: false,
16685
16686     /**
16687      * Set to true when vertical contraints are applied
16688      * @property constrainY
16689      * @type boolean
16690      * @private
16691      */
16692     constrainY: false,
16693
16694     /**
16695      * The left constraint
16696      * @property minX
16697      * @type int
16698      * @private
16699      */
16700     minX: 0,
16701
16702     /**
16703      * The right constraint
16704      * @property maxX
16705      * @type int
16706      * @private
16707      */
16708     maxX: 0,
16709
16710     /**
16711      * The up constraint
16712      * @property minY
16713      * @type int
16714      * @type int
16715      * @private
16716      */
16717     minY: 0,
16718
16719     /**
16720      * The down constraint
16721      * @property maxY
16722      * @type int
16723      * @private
16724      */
16725     maxY: 0,
16726
16727     /**
16728      * Maintain offsets when we resetconstraints.  Set to true when you want
16729      * the position of the element relative to its parent to stay the same
16730      * when the page changes
16731      *
16732      * @property maintainOffset
16733      * @type boolean
16734      */
16735     maintainOffset: false,
16736
16737     /**
16738      * Array of pixel locations the element will snap to if we specified a
16739      * horizontal graduation/interval.  This array is generated automatically
16740      * when you define a tick interval.
16741      * @property xTicks
16742      * @type int[]
16743      */
16744     xTicks: null,
16745
16746     /**
16747      * Array of pixel locations the element will snap to if we specified a
16748      * vertical graduation/interval.  This array is generated automatically
16749      * when you define a tick interval.
16750      * @property yTicks
16751      * @type int[]
16752      */
16753     yTicks: null,
16754
16755     /**
16756      * By default the drag and drop instance will only respond to the primary
16757      * button click (left button for a right-handed mouse).  Set to true to
16758      * allow drag and drop to start with any mouse click that is propogated
16759      * by the browser
16760      * @property primaryButtonOnly
16761      * @type boolean
16762      */
16763     primaryButtonOnly: true,
16764
16765     /**
16766      * The availabe property is false until the linked dom element is accessible.
16767      * @property available
16768      * @type boolean
16769      */
16770     available: false,
16771
16772     /**
16773      * By default, drags can only be initiated if the mousedown occurs in the
16774      * region the linked element is.  This is done in part to work around a
16775      * bug in some browsers that mis-report the mousedown if the previous
16776      * mouseup happened outside of the window.  This property is set to true
16777      * if outer handles are defined.
16778      *
16779      * @property hasOuterHandles
16780      * @type boolean
16781      * @default false
16782      */
16783     hasOuterHandles: false,
16784
16785     /**
16786      * Code that executes immediately before the startDrag event
16787      * @method b4StartDrag
16788      * @private
16789      */
16790     b4StartDrag: function(x, y) { },
16791
16792     /**
16793      * Abstract method called after a drag/drop object is clicked
16794      * and the drag or mousedown time thresholds have beeen met.
16795      * @method startDrag
16796      * @param {int} X click location
16797      * @param {int} Y click location
16798      */
16799     startDrag: function(x, y) { /* override this */ },
16800
16801     /**
16802      * Code that executes immediately before the onDrag event
16803      * @method b4Drag
16804      * @private
16805      */
16806     b4Drag: function(e) { },
16807
16808     /**
16809      * Abstract method called during the onMouseMove event while dragging an
16810      * object.
16811      * @method onDrag
16812      * @param {Event} e the mousemove event
16813      */
16814     onDrag: function(e) { /* override this */ },
16815
16816     /**
16817      * Abstract method called when this element fist begins hovering over
16818      * another DragDrop obj
16819      * @method onDragEnter
16820      * @param {Event} e the mousemove event
16821      * @param {String|DragDrop[]} id In POINT mode, the element
16822      * id this is hovering over.  In INTERSECT mode, an array of one or more
16823      * dragdrop items being hovered over.
16824      */
16825     onDragEnter: function(e, id) { /* override this */ },
16826
16827     /**
16828      * Code that executes immediately before the onDragOver event
16829      * @method b4DragOver
16830      * @private
16831      */
16832     b4DragOver: function(e) { },
16833
16834     /**
16835      * Abstract method called when this element is hovering over another
16836      * DragDrop obj
16837      * @method onDragOver
16838      * @param {Event} e the mousemove event
16839      * @param {String|DragDrop[]} id In POINT mode, the element
16840      * id this is hovering over.  In INTERSECT mode, an array of dd items
16841      * being hovered over.
16842      */
16843     onDragOver: function(e, id) { /* override this */ },
16844
16845     /**
16846      * Code that executes immediately before the onDragOut event
16847      * @method b4DragOut
16848      * @private
16849      */
16850     b4DragOut: function(e) { },
16851
16852     /**
16853      * Abstract method called when we are no longer hovering over an element
16854      * @method onDragOut
16855      * @param {Event} e the mousemove event
16856      * @param {String|DragDrop[]} id In POINT mode, the element
16857      * id this was hovering over.  In INTERSECT mode, an array of dd items
16858      * that the mouse is no longer over.
16859      */
16860     onDragOut: function(e, id) { /* override this */ },
16861
16862     /**
16863      * Code that executes immediately before the onDragDrop event
16864      * @method b4DragDrop
16865      * @private
16866      */
16867     b4DragDrop: function(e) { },
16868
16869     /**
16870      * Abstract method called when this item is dropped on another DragDrop
16871      * obj
16872      * @method onDragDrop
16873      * @param {Event} e the mouseup event
16874      * @param {String|DragDrop[]} id In POINT mode, the element
16875      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16876      * was dropped on.
16877      */
16878     onDragDrop: function(e, id) { /* override this */ },
16879
16880     /**
16881      * Abstract method called when this item is dropped on an area with no
16882      * drop target
16883      * @method onInvalidDrop
16884      * @param {Event} e the mouseup event
16885      */
16886     onInvalidDrop: function(e) { /* override this */ },
16887
16888     /**
16889      * Code that executes immediately before the endDrag event
16890      * @method b4EndDrag
16891      * @private
16892      */
16893     b4EndDrag: function(e) { },
16894
16895     /**
16896      * Fired when we are done dragging the object
16897      * @method endDrag
16898      * @param {Event} e the mouseup event
16899      */
16900     endDrag: function(e) { /* override this */ },
16901
16902     /**
16903      * Code executed immediately before the onMouseDown event
16904      * @method b4MouseDown
16905      * @param {Event} e the mousedown event
16906      * @private
16907      */
16908     b4MouseDown: function(e) {  },
16909
16910     /**
16911      * Event handler that fires when a drag/drop obj gets a mousedown
16912      * @method onMouseDown
16913      * @param {Event} e the mousedown event
16914      */
16915     onMouseDown: function(e) { /* override this */ },
16916
16917     /**
16918      * Event handler that fires when a drag/drop obj gets a mouseup
16919      * @method onMouseUp
16920      * @param {Event} e the mouseup event
16921      */
16922     onMouseUp: function(e) { /* override this */ },
16923
16924     /**
16925      * Override the onAvailable method to do what is needed after the initial
16926      * position was determined.
16927      * @method onAvailable
16928      */
16929     onAvailable: function () {
16930     },
16931
16932     /*
16933      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16934      * @type Object
16935      */
16936     defaultPadding : {left:0, right:0, top:0, bottom:0},
16937
16938     /*
16939      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16940  *
16941  * Usage:
16942  <pre><code>
16943  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16944                 { dragElId: "existingProxyDiv" });
16945  dd.startDrag = function(){
16946      this.constrainTo("parent-id");
16947  };
16948  </code></pre>
16949  * Or you can initalize it using the {@link Roo.Element} object:
16950  <pre><code>
16951  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16952      startDrag : function(){
16953          this.constrainTo("parent-id");
16954      }
16955  });
16956  </code></pre>
16957      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16958      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16959      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16960      * an object containing the sides to pad. For example: {right:10, bottom:10}
16961      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16962      */
16963     constrainTo : function(constrainTo, pad, inContent){
16964         if(typeof pad == "number"){
16965             pad = {left: pad, right:pad, top:pad, bottom:pad};
16966         }
16967         pad = pad || this.defaultPadding;
16968         var b = Roo.get(this.getEl()).getBox();
16969         var ce = Roo.get(constrainTo);
16970         var s = ce.getScroll();
16971         var c, cd = ce.dom;
16972         if(cd == document.body){
16973             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16974         }else{
16975             xy = ce.getXY();
16976             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16977         }
16978
16979
16980         var topSpace = b.y - c.y;
16981         var leftSpace = b.x - c.x;
16982
16983         this.resetConstraints();
16984         this.setXConstraint(leftSpace - (pad.left||0), // left
16985                 c.width - leftSpace - b.width - (pad.right||0) //right
16986         );
16987         this.setYConstraint(topSpace - (pad.top||0), //top
16988                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16989         );
16990     },
16991
16992     /**
16993      * Returns a reference to the linked element
16994      * @method getEl
16995      * @return {HTMLElement} the html element
16996      */
16997     getEl: function() {
16998         if (!this._domRef) {
16999             this._domRef = Roo.getDom(this.id);
17000         }
17001
17002         return this._domRef;
17003     },
17004
17005     /**
17006      * Returns a reference to the actual element to drag.  By default this is
17007      * the same as the html element, but it can be assigned to another
17008      * element. An example of this can be found in Roo.dd.DDProxy
17009      * @method getDragEl
17010      * @return {HTMLElement} the html element
17011      */
17012     getDragEl: function() {
17013         return Roo.getDom(this.dragElId);
17014     },
17015
17016     /**
17017      * Sets up the DragDrop object.  Must be called in the constructor of any
17018      * Roo.dd.DragDrop subclass
17019      * @method init
17020      * @param id the id of the linked element
17021      * @param {String} sGroup the group of related items
17022      * @param {object} config configuration attributes
17023      */
17024     init: function(id, sGroup, config) {
17025         this.initTarget(id, sGroup, config);
17026         if (!Roo.isTouch) {
17027             Event.on(this.id, "mousedown", this.handleMouseDown, this);
17028         }
17029         Event.on(this.id, "touchstart", this.handleMouseDown, this);
17030         // Event.on(this.id, "selectstart", Event.preventDefault);
17031     },
17032
17033     /**
17034      * Initializes Targeting functionality only... the object does not
17035      * get a mousedown handler.
17036      * @method initTarget
17037      * @param id the id of the linked element
17038      * @param {String} sGroup the group of related items
17039      * @param {object} config configuration attributes
17040      */
17041     initTarget: function(id, sGroup, config) {
17042
17043         // configuration attributes
17044         this.config = config || {};
17045
17046         // create a local reference to the drag and drop manager
17047         this.DDM = Roo.dd.DDM;
17048         // initialize the groups array
17049         this.groups = {};
17050
17051         // assume that we have an element reference instead of an id if the
17052         // parameter is not a string
17053         if (typeof id !== "string") {
17054             id = Roo.id(id);
17055         }
17056
17057         // set the id
17058         this.id = id;
17059
17060         // add to an interaction group
17061         this.addToGroup((sGroup) ? sGroup : "default");
17062
17063         // We don't want to register this as the handle with the manager
17064         // so we just set the id rather than calling the setter.
17065         this.handleElId = id;
17066
17067         // the linked element is the element that gets dragged by default
17068         this.setDragElId(id);
17069
17070         // by default, clicked anchors will not start drag operations.
17071         this.invalidHandleTypes = { A: "A" };
17072         this.invalidHandleIds = {};
17073         this.invalidHandleClasses = [];
17074
17075         this.applyConfig();
17076
17077         this.handleOnAvailable();
17078     },
17079
17080     /**
17081      * Applies the configuration parameters that were passed into the constructor.
17082      * This is supposed to happen at each level through the inheritance chain.  So
17083      * a DDProxy implentation will execute apply config on DDProxy, DD, and
17084      * DragDrop in order to get all of the parameters that are available in
17085      * each object.
17086      * @method applyConfig
17087      */
17088     applyConfig: function() {
17089
17090         // configurable properties:
17091         //    padding, isTarget, maintainOffset, primaryButtonOnly
17092         this.padding           = this.config.padding || [0, 0, 0, 0];
17093         this.isTarget          = (this.config.isTarget !== false);
17094         this.maintainOffset    = (this.config.maintainOffset);
17095         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
17096
17097     },
17098
17099     /**
17100      * Executed when the linked element is available
17101      * @method handleOnAvailable
17102      * @private
17103      */
17104     handleOnAvailable: function() {
17105         this.available = true;
17106         this.resetConstraints();
17107         this.onAvailable();
17108     },
17109
17110      /**
17111      * Configures the padding for the target zone in px.  Effectively expands
17112      * (or reduces) the virtual object size for targeting calculations.
17113      * Supports css-style shorthand; if only one parameter is passed, all sides
17114      * will have that padding, and if only two are passed, the top and bottom
17115      * will have the first param, the left and right the second.
17116      * @method setPadding
17117      * @param {int} iTop    Top pad
17118      * @param {int} iRight  Right pad
17119      * @param {int} iBot    Bot pad
17120      * @param {int} iLeft   Left pad
17121      */
17122     setPadding: function(iTop, iRight, iBot, iLeft) {
17123         // this.padding = [iLeft, iRight, iTop, iBot];
17124         if (!iRight && 0 !== iRight) {
17125             this.padding = [iTop, iTop, iTop, iTop];
17126         } else if (!iBot && 0 !== iBot) {
17127             this.padding = [iTop, iRight, iTop, iRight];
17128         } else {
17129             this.padding = [iTop, iRight, iBot, iLeft];
17130         }
17131     },
17132
17133     /**
17134      * Stores the initial placement of the linked element.
17135      * @method setInitialPosition
17136      * @param {int} diffX   the X offset, default 0
17137      * @param {int} diffY   the Y offset, default 0
17138      */
17139     setInitPosition: function(diffX, diffY) {
17140         var el = this.getEl();
17141
17142         if (!this.DDM.verifyEl(el)) {
17143             return;
17144         }
17145
17146         var dx = diffX || 0;
17147         var dy = diffY || 0;
17148
17149         var p = Dom.getXY( el );
17150
17151         this.initPageX = p[0] - dx;
17152         this.initPageY = p[1] - dy;
17153
17154         this.lastPageX = p[0];
17155         this.lastPageY = p[1];
17156
17157
17158         this.setStartPosition(p);
17159     },
17160
17161     /**
17162      * Sets the start position of the element.  This is set when the obj
17163      * is initialized, the reset when a drag is started.
17164      * @method setStartPosition
17165      * @param pos current position (from previous lookup)
17166      * @private
17167      */
17168     setStartPosition: function(pos) {
17169         var p = pos || Dom.getXY( this.getEl() );
17170         this.deltaSetXY = null;
17171
17172         this.startPageX = p[0];
17173         this.startPageY = p[1];
17174     },
17175
17176     /**
17177      * Add this instance to a group of related drag/drop objects.  All
17178      * instances belong to at least one group, and can belong to as many
17179      * groups as needed.
17180      * @method addToGroup
17181      * @param sGroup {string} the name of the group
17182      */
17183     addToGroup: function(sGroup) {
17184         this.groups[sGroup] = true;
17185         this.DDM.regDragDrop(this, sGroup);
17186     },
17187
17188     /**
17189      * Remove's this instance from the supplied interaction group
17190      * @method removeFromGroup
17191      * @param {string}  sGroup  The group to drop
17192      */
17193     removeFromGroup: function(sGroup) {
17194         if (this.groups[sGroup]) {
17195             delete this.groups[sGroup];
17196         }
17197
17198         this.DDM.removeDDFromGroup(this, sGroup);
17199     },
17200
17201     /**
17202      * Allows you to specify that an element other than the linked element
17203      * will be moved with the cursor during a drag
17204      * @method setDragElId
17205      * @param id {string} the id of the element that will be used to initiate the drag
17206      */
17207     setDragElId: function(id) {
17208         this.dragElId = id;
17209     },
17210
17211     /**
17212      * Allows you to specify a child of the linked element that should be
17213      * used to initiate the drag operation.  An example of this would be if
17214      * you have a content div with text and links.  Clicking anywhere in the
17215      * content area would normally start the drag operation.  Use this method
17216      * to specify that an element inside of the content div is the element
17217      * that starts the drag operation.
17218      * @method setHandleElId
17219      * @param id {string} the id of the element that will be used to
17220      * initiate the drag.
17221      */
17222     setHandleElId: function(id) {
17223         if (typeof id !== "string") {
17224             id = Roo.id(id);
17225         }
17226         this.handleElId = id;
17227         this.DDM.regHandle(this.id, id);
17228     },
17229
17230     /**
17231      * Allows you to set an element outside of the linked element as a drag
17232      * handle
17233      * @method setOuterHandleElId
17234      * @param id the id of the element that will be used to initiate the drag
17235      */
17236     setOuterHandleElId: function(id) {
17237         if (typeof id !== "string") {
17238             id = Roo.id(id);
17239         }
17240         Event.on(id, "mousedown",
17241                 this.handleMouseDown, this);
17242         this.setHandleElId(id);
17243
17244         this.hasOuterHandles = true;
17245     },
17246
17247     /**
17248      * Remove all drag and drop hooks for this element
17249      * @method unreg
17250      */
17251     unreg: function() {
17252         Event.un(this.id, "mousedown",
17253                 this.handleMouseDown);
17254         Event.un(this.id, "touchstart",
17255                 this.handleMouseDown);
17256         this._domRef = null;
17257         this.DDM._remove(this);
17258     },
17259
17260     destroy : function(){
17261         this.unreg();
17262     },
17263
17264     /**
17265      * Returns true if this instance is locked, or the drag drop mgr is locked
17266      * (meaning that all drag/drop is disabled on the page.)
17267      * @method isLocked
17268      * @return {boolean} true if this obj or all drag/drop is locked, else
17269      * false
17270      */
17271     isLocked: function() {
17272         return (this.DDM.isLocked() || this.locked);
17273     },
17274
17275     /**
17276      * Fired when this object is clicked
17277      * @method handleMouseDown
17278      * @param {Event} e
17279      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17280      * @private
17281      */
17282     handleMouseDown: function(e, oDD){
17283      
17284         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17285             //Roo.log('not touch/ button !=0');
17286             return;
17287         }
17288         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17289             return; // double touch..
17290         }
17291         
17292
17293         if (this.isLocked()) {
17294             //Roo.log('locked');
17295             return;
17296         }
17297
17298         this.DDM.refreshCache(this.groups);
17299 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17300         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17301         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17302             //Roo.log('no outer handes or not over target');
17303                 // do nothing.
17304         } else {
17305 //            Roo.log('check validator');
17306             if (this.clickValidator(e)) {
17307 //                Roo.log('validate success');
17308                 // set the initial element position
17309                 this.setStartPosition();
17310
17311
17312                 this.b4MouseDown(e);
17313                 this.onMouseDown(e);
17314
17315                 this.DDM.handleMouseDown(e, this);
17316
17317                 this.DDM.stopEvent(e);
17318             } else {
17319
17320
17321             }
17322         }
17323     },
17324
17325     clickValidator: function(e) {
17326         var target = e.getTarget();
17327         return ( this.isValidHandleChild(target) &&
17328                     (this.id == this.handleElId ||
17329                         this.DDM.handleWasClicked(target, this.id)) );
17330     },
17331
17332     /**
17333      * Allows you to specify a tag name that should not start a drag operation
17334      * when clicked.  This is designed to facilitate embedding links within a
17335      * drag handle that do something other than start the drag.
17336      * @method addInvalidHandleType
17337      * @param {string} tagName the type of element to exclude
17338      */
17339     addInvalidHandleType: function(tagName) {
17340         var type = tagName.toUpperCase();
17341         this.invalidHandleTypes[type] = type;
17342     },
17343
17344     /**
17345      * Lets you to specify an element id for a child of a drag handle
17346      * that should not initiate a drag
17347      * @method addInvalidHandleId
17348      * @param {string} id the element id of the element you wish to ignore
17349      */
17350     addInvalidHandleId: function(id) {
17351         if (typeof id !== "string") {
17352             id = Roo.id(id);
17353         }
17354         this.invalidHandleIds[id] = id;
17355     },
17356
17357     /**
17358      * Lets you specify a css class of elements that will not initiate a drag
17359      * @method addInvalidHandleClass
17360      * @param {string} cssClass the class of the elements you wish to ignore
17361      */
17362     addInvalidHandleClass: function(cssClass) {
17363         this.invalidHandleClasses.push(cssClass);
17364     },
17365
17366     /**
17367      * Unsets an excluded tag name set by addInvalidHandleType
17368      * @method removeInvalidHandleType
17369      * @param {string} tagName the type of element to unexclude
17370      */
17371     removeInvalidHandleType: function(tagName) {
17372         var type = tagName.toUpperCase();
17373         // this.invalidHandleTypes[type] = null;
17374         delete this.invalidHandleTypes[type];
17375     },
17376
17377     /**
17378      * Unsets an invalid handle id
17379      * @method removeInvalidHandleId
17380      * @param {string} id the id of the element to re-enable
17381      */
17382     removeInvalidHandleId: function(id) {
17383         if (typeof id !== "string") {
17384             id = Roo.id(id);
17385         }
17386         delete this.invalidHandleIds[id];
17387     },
17388
17389     /**
17390      * Unsets an invalid css class
17391      * @method removeInvalidHandleClass
17392      * @param {string} cssClass the class of the element(s) you wish to
17393      * re-enable
17394      */
17395     removeInvalidHandleClass: function(cssClass) {
17396         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17397             if (this.invalidHandleClasses[i] == cssClass) {
17398                 delete this.invalidHandleClasses[i];
17399             }
17400         }
17401     },
17402
17403     /**
17404      * Checks the tag exclusion list to see if this click should be ignored
17405      * @method isValidHandleChild
17406      * @param {HTMLElement} node the HTMLElement to evaluate
17407      * @return {boolean} true if this is a valid tag type, false if not
17408      */
17409     isValidHandleChild: function(node) {
17410
17411         var valid = true;
17412         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17413         var nodeName;
17414         try {
17415             nodeName = node.nodeName.toUpperCase();
17416         } catch(e) {
17417             nodeName = node.nodeName;
17418         }
17419         valid = valid && !this.invalidHandleTypes[nodeName];
17420         valid = valid && !this.invalidHandleIds[node.id];
17421
17422         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17423             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17424         }
17425
17426
17427         return valid;
17428
17429     },
17430
17431     /**
17432      * Create the array of horizontal tick marks if an interval was specified
17433      * in setXConstraint().
17434      * @method setXTicks
17435      * @private
17436      */
17437     setXTicks: function(iStartX, iTickSize) {
17438         this.xTicks = [];
17439         this.xTickSize = iTickSize;
17440
17441         var tickMap = {};
17442
17443         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17444             if (!tickMap[i]) {
17445                 this.xTicks[this.xTicks.length] = i;
17446                 tickMap[i] = true;
17447             }
17448         }
17449
17450         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17451             if (!tickMap[i]) {
17452                 this.xTicks[this.xTicks.length] = i;
17453                 tickMap[i] = true;
17454             }
17455         }
17456
17457         this.xTicks.sort(this.DDM.numericSort) ;
17458     },
17459
17460     /**
17461      * Create the array of vertical tick marks if an interval was specified in
17462      * setYConstraint().
17463      * @method setYTicks
17464      * @private
17465      */
17466     setYTicks: function(iStartY, iTickSize) {
17467         this.yTicks = [];
17468         this.yTickSize = iTickSize;
17469
17470         var tickMap = {};
17471
17472         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17473             if (!tickMap[i]) {
17474                 this.yTicks[this.yTicks.length] = i;
17475                 tickMap[i] = true;
17476             }
17477         }
17478
17479         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17480             if (!tickMap[i]) {
17481                 this.yTicks[this.yTicks.length] = i;
17482                 tickMap[i] = true;
17483             }
17484         }
17485
17486         this.yTicks.sort(this.DDM.numericSort) ;
17487     },
17488
17489     /**
17490      * By default, the element can be dragged any place on the screen.  Use
17491      * this method to limit the horizontal travel of the element.  Pass in
17492      * 0,0 for the parameters if you want to lock the drag to the y axis.
17493      * @method setXConstraint
17494      * @param {int} iLeft the number of pixels the element can move to the left
17495      * @param {int} iRight the number of pixels the element can move to the
17496      * right
17497      * @param {int} iTickSize optional parameter for specifying that the
17498      * element
17499      * should move iTickSize pixels at a time.
17500      */
17501     setXConstraint: function(iLeft, iRight, iTickSize) {
17502         this.leftConstraint = iLeft;
17503         this.rightConstraint = iRight;
17504
17505         this.minX = this.initPageX - iLeft;
17506         this.maxX = this.initPageX + iRight;
17507         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17508
17509         this.constrainX = true;
17510     },
17511
17512     /**
17513      * Clears any constraints applied to this instance.  Also clears ticks
17514      * since they can't exist independent of a constraint at this time.
17515      * @method clearConstraints
17516      */
17517     clearConstraints: function() {
17518         this.constrainX = false;
17519         this.constrainY = false;
17520         this.clearTicks();
17521     },
17522
17523     /**
17524      * Clears any tick interval defined for this instance
17525      * @method clearTicks
17526      */
17527     clearTicks: function() {
17528         this.xTicks = null;
17529         this.yTicks = null;
17530         this.xTickSize = 0;
17531         this.yTickSize = 0;
17532     },
17533
17534     /**
17535      * By default, the element can be dragged any place on the screen.  Set
17536      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17537      * parameters if you want to lock the drag to the x axis.
17538      * @method setYConstraint
17539      * @param {int} iUp the number of pixels the element can move up
17540      * @param {int} iDown the number of pixels the element can move down
17541      * @param {int} iTickSize optional parameter for specifying that the
17542      * element should move iTickSize pixels at a time.
17543      */
17544     setYConstraint: function(iUp, iDown, iTickSize) {
17545         this.topConstraint = iUp;
17546         this.bottomConstraint = iDown;
17547
17548         this.minY = this.initPageY - iUp;
17549         this.maxY = this.initPageY + iDown;
17550         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17551
17552         this.constrainY = true;
17553
17554     },
17555
17556     /**
17557      * resetConstraints must be called if you manually reposition a dd element.
17558      * @method resetConstraints
17559      * @param {boolean} maintainOffset
17560      */
17561     resetConstraints: function() {
17562
17563
17564         // Maintain offsets if necessary
17565         if (this.initPageX || this.initPageX === 0) {
17566             // figure out how much this thing has moved
17567             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17568             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17569
17570             this.setInitPosition(dx, dy);
17571
17572         // This is the first time we have detected the element's position
17573         } else {
17574             this.setInitPosition();
17575         }
17576
17577         if (this.constrainX) {
17578             this.setXConstraint( this.leftConstraint,
17579                                  this.rightConstraint,
17580                                  this.xTickSize        );
17581         }
17582
17583         if (this.constrainY) {
17584             this.setYConstraint( this.topConstraint,
17585                                  this.bottomConstraint,
17586                                  this.yTickSize         );
17587         }
17588     },
17589
17590     /**
17591      * Normally the drag element is moved pixel by pixel, but we can specify
17592      * that it move a number of pixels at a time.  This method resolves the
17593      * location when we have it set up like this.
17594      * @method getTick
17595      * @param {int} val where we want to place the object
17596      * @param {int[]} tickArray sorted array of valid points
17597      * @return {int} the closest tick
17598      * @private
17599      */
17600     getTick: function(val, tickArray) {
17601
17602         if (!tickArray) {
17603             // If tick interval is not defined, it is effectively 1 pixel,
17604             // so we return the value passed to us.
17605             return val;
17606         } else if (tickArray[0] >= val) {
17607             // The value is lower than the first tick, so we return the first
17608             // tick.
17609             return tickArray[0];
17610         } else {
17611             for (var i=0, len=tickArray.length; i<len; ++i) {
17612                 var next = i + 1;
17613                 if (tickArray[next] && tickArray[next] >= val) {
17614                     var diff1 = val - tickArray[i];
17615                     var diff2 = tickArray[next] - val;
17616                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17617                 }
17618             }
17619
17620             // The value is larger than the last tick, so we return the last
17621             // tick.
17622             return tickArray[tickArray.length - 1];
17623         }
17624     },
17625
17626     /**
17627      * toString method
17628      * @method toString
17629      * @return {string} string representation of the dd obj
17630      */
17631     toString: function() {
17632         return ("DragDrop " + this.id);
17633     }
17634
17635 });
17636
17637 })();
17638 /*
17639  * Based on:
17640  * Ext JS Library 1.1.1
17641  * Copyright(c) 2006-2007, Ext JS, LLC.
17642  *
17643  * Originally Released Under LGPL - original licence link has changed is not relivant.
17644  *
17645  * Fork - LGPL
17646  * <script type="text/javascript">
17647  */
17648
17649
17650 /**
17651  * The drag and drop utility provides a framework for building drag and drop
17652  * applications.  In addition to enabling drag and drop for specific elements,
17653  * the drag and drop elements are tracked by the manager class, and the
17654  * interactions between the various elements are tracked during the drag and
17655  * the implementing code is notified about these important moments.
17656  */
17657
17658 // Only load the library once.  Rewriting the manager class would orphan
17659 // existing drag and drop instances.
17660 if (!Roo.dd.DragDropMgr) {
17661
17662 /**
17663  * @class Roo.dd.DragDropMgr
17664  * DragDropMgr is a singleton that tracks the element interaction for
17665  * all DragDrop items in the window.  Generally, you will not call
17666  * this class directly, but it does have helper methods that could
17667  * be useful in your DragDrop implementations.
17668  * @singleton
17669  */
17670 Roo.dd.DragDropMgr = function() {
17671
17672     var Event = Roo.EventManager;
17673
17674     return {
17675
17676         /**
17677          * Two dimensional Array of registered DragDrop objects.  The first
17678          * dimension is the DragDrop item group, the second the DragDrop
17679          * object.
17680          * @property ids
17681          * @type {string: string}
17682          * @private
17683          * @static
17684          */
17685         ids: {},
17686
17687         /**
17688          * Array of element ids defined as drag handles.  Used to determine
17689          * if the element that generated the mousedown event is actually the
17690          * handle and not the html element itself.
17691          * @property handleIds
17692          * @type {string: string}
17693          * @private
17694          * @static
17695          */
17696         handleIds: {},
17697
17698         /**
17699          * the DragDrop object that is currently being dragged
17700          * @property dragCurrent
17701          * @type DragDrop
17702          * @private
17703          * @static
17704          **/
17705         dragCurrent: null,
17706
17707         /**
17708          * the DragDrop object(s) that are being hovered over
17709          * @property dragOvers
17710          * @type Array
17711          * @private
17712          * @static
17713          */
17714         dragOvers: {},
17715
17716         /**
17717          * the X distance between the cursor and the object being dragged
17718          * @property deltaX
17719          * @type int
17720          * @private
17721          * @static
17722          */
17723         deltaX: 0,
17724
17725         /**
17726          * the Y distance between the cursor and the object being dragged
17727          * @property deltaY
17728          * @type int
17729          * @private
17730          * @static
17731          */
17732         deltaY: 0,
17733
17734         /**
17735          * Flag to determine if we should prevent the default behavior of the
17736          * events we define. By default this is true, but this can be set to
17737          * false if you need the default behavior (not recommended)
17738          * @property preventDefault
17739          * @type boolean
17740          * @static
17741          */
17742         preventDefault: true,
17743
17744         /**
17745          * Flag to determine if we should stop the propagation of the events
17746          * we generate. This is true by default but you may want to set it to
17747          * false if the html element contains other features that require the
17748          * mouse click.
17749          * @property stopPropagation
17750          * @type boolean
17751          * @static
17752          */
17753         stopPropagation: true,
17754
17755         /**
17756          * Internal flag that is set to true when drag and drop has been
17757          * intialized
17758          * @property initialized
17759          * @private
17760          * @static
17761          */
17762         initalized: false,
17763
17764         /**
17765          * All drag and drop can be disabled.
17766          * @property locked
17767          * @private
17768          * @static
17769          */
17770         locked: false,
17771
17772         /**
17773          * Called the first time an element is registered.
17774          * @method init
17775          * @private
17776          * @static
17777          */
17778         init: function() {
17779             this.initialized = true;
17780         },
17781
17782         /**
17783          * In point mode, drag and drop interaction is defined by the
17784          * location of the cursor during the drag/drop
17785          * @property POINT
17786          * @type int
17787          * @static
17788          */
17789         POINT: 0,
17790
17791         /**
17792          * In intersect mode, drag and drop interactio nis defined by the
17793          * overlap of two or more drag and drop objects.
17794          * @property INTERSECT
17795          * @type int
17796          * @static
17797          */
17798         INTERSECT: 1,
17799
17800         /**
17801          * The current drag and drop mode.  Default: POINT
17802          * @property mode
17803          * @type int
17804          * @static
17805          */
17806         mode: 0,
17807
17808         /**
17809          * Runs method on all drag and drop objects
17810          * @method _execOnAll
17811          * @private
17812          * @static
17813          */
17814         _execOnAll: function(sMethod, args) {
17815             for (var i in this.ids) {
17816                 for (var j in this.ids[i]) {
17817                     var oDD = this.ids[i][j];
17818                     if (! this.isTypeOfDD(oDD)) {
17819                         continue;
17820                     }
17821                     oDD[sMethod].apply(oDD, args);
17822                 }
17823             }
17824         },
17825
17826         /**
17827          * Drag and drop initialization.  Sets up the global event handlers
17828          * @method _onLoad
17829          * @private
17830          * @static
17831          */
17832         _onLoad: function() {
17833
17834             this.init();
17835
17836             if (!Roo.isTouch) {
17837                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17838                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17839             }
17840             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17841             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17842             
17843             Event.on(window,   "unload",    this._onUnload, this, true);
17844             Event.on(window,   "resize",    this._onResize, this, true);
17845             // Event.on(window,   "mouseout",    this._test);
17846
17847         },
17848
17849         /**
17850          * Reset constraints on all drag and drop objs
17851          * @method _onResize
17852          * @private
17853          * @static
17854          */
17855         _onResize: function(e) {
17856             this._execOnAll("resetConstraints", []);
17857         },
17858
17859         /**
17860          * Lock all drag and drop functionality
17861          * @method lock
17862          * @static
17863          */
17864         lock: function() { this.locked = true; },
17865
17866         /**
17867          * Unlock all drag and drop functionality
17868          * @method unlock
17869          * @static
17870          */
17871         unlock: function() { this.locked = false; },
17872
17873         /**
17874          * Is drag and drop locked?
17875          * @method isLocked
17876          * @return {boolean} True if drag and drop is locked, false otherwise.
17877          * @static
17878          */
17879         isLocked: function() { return this.locked; },
17880
17881         /**
17882          * Location cache that is set for all drag drop objects when a drag is
17883          * initiated, cleared when the drag is finished.
17884          * @property locationCache
17885          * @private
17886          * @static
17887          */
17888         locationCache: {},
17889
17890         /**
17891          * Set useCache to false if you want to force object the lookup of each
17892          * drag and drop linked element constantly during a drag.
17893          * @property useCache
17894          * @type boolean
17895          * @static
17896          */
17897         useCache: true,
17898
17899         /**
17900          * The number of pixels that the mouse needs to move after the
17901          * mousedown before the drag is initiated.  Default=3;
17902          * @property clickPixelThresh
17903          * @type int
17904          * @static
17905          */
17906         clickPixelThresh: 3,
17907
17908         /**
17909          * The number of milliseconds after the mousedown event to initiate the
17910          * drag if we don't get a mouseup event. Default=1000
17911          * @property clickTimeThresh
17912          * @type int
17913          * @static
17914          */
17915         clickTimeThresh: 350,
17916
17917         /**
17918          * Flag that indicates that either the drag pixel threshold or the
17919          * mousdown time threshold has been met
17920          * @property dragThreshMet
17921          * @type boolean
17922          * @private
17923          * @static
17924          */
17925         dragThreshMet: false,
17926
17927         /**
17928          * Timeout used for the click time threshold
17929          * @property clickTimeout
17930          * @type Object
17931          * @private
17932          * @static
17933          */
17934         clickTimeout: null,
17935
17936         /**
17937          * The X position of the mousedown event stored for later use when a
17938          * drag threshold is met.
17939          * @property startX
17940          * @type int
17941          * @private
17942          * @static
17943          */
17944         startX: 0,
17945
17946         /**
17947          * The Y position of the mousedown event stored for later use when a
17948          * drag threshold is met.
17949          * @property startY
17950          * @type int
17951          * @private
17952          * @static
17953          */
17954         startY: 0,
17955
17956         /**
17957          * Each DragDrop instance must be registered with the DragDropMgr.
17958          * This is executed in DragDrop.init()
17959          * @method regDragDrop
17960          * @param {DragDrop} oDD the DragDrop object to register
17961          * @param {String} sGroup the name of the group this element belongs to
17962          * @static
17963          */
17964         regDragDrop: function(oDD, sGroup) {
17965             if (!this.initialized) { this.init(); }
17966
17967             if (!this.ids[sGroup]) {
17968                 this.ids[sGroup] = {};
17969             }
17970             this.ids[sGroup][oDD.id] = oDD;
17971         },
17972
17973         /**
17974          * Removes the supplied dd instance from the supplied group. Executed
17975          * by DragDrop.removeFromGroup, so don't call this function directly.
17976          * @method removeDDFromGroup
17977          * @private
17978          * @static
17979          */
17980         removeDDFromGroup: function(oDD, sGroup) {
17981             if (!this.ids[sGroup]) {
17982                 this.ids[sGroup] = {};
17983             }
17984
17985             var obj = this.ids[sGroup];
17986             if (obj && obj[oDD.id]) {
17987                 delete obj[oDD.id];
17988             }
17989         },
17990
17991         /**
17992          * Unregisters a drag and drop item.  This is executed in
17993          * DragDrop.unreg, use that method instead of calling this directly.
17994          * @method _remove
17995          * @private
17996          * @static
17997          */
17998         _remove: function(oDD) {
17999             for (var g in oDD.groups) {
18000                 if (g && this.ids[g][oDD.id]) {
18001                     delete this.ids[g][oDD.id];
18002                 }
18003             }
18004             delete this.handleIds[oDD.id];
18005         },
18006
18007         /**
18008          * Each DragDrop handle element must be registered.  This is done
18009          * automatically when executing DragDrop.setHandleElId()
18010          * @method regHandle
18011          * @param {String} sDDId the DragDrop id this element is a handle for
18012          * @param {String} sHandleId the id of the element that is the drag
18013          * handle
18014          * @static
18015          */
18016         regHandle: function(sDDId, sHandleId) {
18017             if (!this.handleIds[sDDId]) {
18018                 this.handleIds[sDDId] = {};
18019             }
18020             this.handleIds[sDDId][sHandleId] = sHandleId;
18021         },
18022
18023         /**
18024          * Utility function to determine if a given element has been
18025          * registered as a drag drop item.
18026          * @method isDragDrop
18027          * @param {String} id the element id to check
18028          * @return {boolean} true if this element is a DragDrop item,
18029          * false otherwise
18030          * @static
18031          */
18032         isDragDrop: function(id) {
18033             return ( this.getDDById(id) ) ? true : false;
18034         },
18035
18036         /**
18037          * Returns the drag and drop instances that are in all groups the
18038          * passed in instance belongs to.
18039          * @method getRelated
18040          * @param {DragDrop} p_oDD the obj to get related data for
18041          * @param {boolean} bTargetsOnly if true, only return targetable objs
18042          * @return {DragDrop[]} the related instances
18043          * @static
18044          */
18045         getRelated: function(p_oDD, bTargetsOnly) {
18046             var oDDs = [];
18047             for (var i in p_oDD.groups) {
18048                 for (j in this.ids[i]) {
18049                     var dd = this.ids[i][j];
18050                     if (! this.isTypeOfDD(dd)) {
18051                         continue;
18052                     }
18053                     if (!bTargetsOnly || dd.isTarget) {
18054                         oDDs[oDDs.length] = dd;
18055                     }
18056                 }
18057             }
18058
18059             return oDDs;
18060         },
18061
18062         /**
18063          * Returns true if the specified dd target is a legal target for
18064          * the specifice drag obj
18065          * @method isLegalTarget
18066          * @param {DragDrop} the drag obj
18067          * @param {DragDrop} the target
18068          * @return {boolean} true if the target is a legal target for the
18069          * dd obj
18070          * @static
18071          */
18072         isLegalTarget: function (oDD, oTargetDD) {
18073             var targets = this.getRelated(oDD, true);
18074             for (var i=0, len=targets.length;i<len;++i) {
18075                 if (targets[i].id == oTargetDD.id) {
18076                     return true;
18077                 }
18078             }
18079
18080             return false;
18081         },
18082
18083         /**
18084          * My goal is to be able to transparently determine if an object is
18085          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
18086          * returns "object", oDD.constructor.toString() always returns
18087          * "DragDrop" and not the name of the subclass.  So for now it just
18088          * evaluates a well-known variable in DragDrop.
18089          * @method isTypeOfDD
18090          * @param {Object} the object to evaluate
18091          * @return {boolean} true if typeof oDD = DragDrop
18092          * @static
18093          */
18094         isTypeOfDD: function (oDD) {
18095             return (oDD && oDD.__ygDragDrop);
18096         },
18097
18098         /**
18099          * Utility function to determine if a given element has been
18100          * registered as a drag drop handle for the given Drag Drop object.
18101          * @method isHandle
18102          * @param {String} id the element id to check
18103          * @return {boolean} true if this element is a DragDrop handle, false
18104          * otherwise
18105          * @static
18106          */
18107         isHandle: function(sDDId, sHandleId) {
18108             return ( this.handleIds[sDDId] &&
18109                             this.handleIds[sDDId][sHandleId] );
18110         },
18111
18112         /**
18113          * Returns the DragDrop instance for a given id
18114          * @method getDDById
18115          * @param {String} id the id of the DragDrop object
18116          * @return {DragDrop} the drag drop object, null if it is not found
18117          * @static
18118          */
18119         getDDById: function(id) {
18120             for (var i in this.ids) {
18121                 if (this.ids[i][id]) {
18122                     return this.ids[i][id];
18123                 }
18124             }
18125             return null;
18126         },
18127
18128         /**
18129          * Fired after a registered DragDrop object gets the mousedown event.
18130          * Sets up the events required to track the object being dragged
18131          * @method handleMouseDown
18132          * @param {Event} e the event
18133          * @param oDD the DragDrop object being dragged
18134          * @private
18135          * @static
18136          */
18137         handleMouseDown: function(e, oDD) {
18138             if(Roo.QuickTips){
18139                 Roo.QuickTips.disable();
18140             }
18141             this.currentTarget = e.getTarget();
18142
18143             this.dragCurrent = oDD;
18144
18145             var el = oDD.getEl();
18146
18147             // track start position
18148             this.startX = e.getPageX();
18149             this.startY = e.getPageY();
18150
18151             this.deltaX = this.startX - el.offsetLeft;
18152             this.deltaY = this.startY - el.offsetTop;
18153
18154             this.dragThreshMet = false;
18155
18156             this.clickTimeout = setTimeout(
18157                     function() {
18158                         var DDM = Roo.dd.DDM;
18159                         DDM.startDrag(DDM.startX, DDM.startY);
18160                     },
18161                     this.clickTimeThresh );
18162         },
18163
18164         /**
18165          * Fired when either the drag pixel threshol or the mousedown hold
18166          * time threshold has been met.
18167          * @method startDrag
18168          * @param x {int} the X position of the original mousedown
18169          * @param y {int} the Y position of the original mousedown
18170          * @static
18171          */
18172         startDrag: function(x, y) {
18173             clearTimeout(this.clickTimeout);
18174             if (this.dragCurrent) {
18175                 this.dragCurrent.b4StartDrag(x, y);
18176                 this.dragCurrent.startDrag(x, y);
18177             }
18178             this.dragThreshMet = true;
18179         },
18180
18181         /**
18182          * Internal function to handle the mouseup event.  Will be invoked
18183          * from the context of the document.
18184          * @method handleMouseUp
18185          * @param {Event} e the event
18186          * @private
18187          * @static
18188          */
18189         handleMouseUp: function(e) {
18190
18191             if(Roo.QuickTips){
18192                 Roo.QuickTips.enable();
18193             }
18194             if (! this.dragCurrent) {
18195                 return;
18196             }
18197
18198             clearTimeout(this.clickTimeout);
18199
18200             if (this.dragThreshMet) {
18201                 this.fireEvents(e, true);
18202             } else {
18203             }
18204
18205             this.stopDrag(e);
18206
18207             this.stopEvent(e);
18208         },
18209
18210         /**
18211          * Utility to stop event propagation and event default, if these
18212          * features are turned on.
18213          * @method stopEvent
18214          * @param {Event} e the event as returned by this.getEvent()
18215          * @static
18216          */
18217         stopEvent: function(e){
18218             if(this.stopPropagation) {
18219                 e.stopPropagation();
18220             }
18221
18222             if (this.preventDefault) {
18223                 e.preventDefault();
18224             }
18225         },
18226
18227         /**
18228          * Internal function to clean up event handlers after the drag
18229          * operation is complete
18230          * @method stopDrag
18231          * @param {Event} e the event
18232          * @private
18233          * @static
18234          */
18235         stopDrag: function(e) {
18236             // Fire the drag end event for the item that was dragged
18237             if (this.dragCurrent) {
18238                 if (this.dragThreshMet) {
18239                     this.dragCurrent.b4EndDrag(e);
18240                     this.dragCurrent.endDrag(e);
18241                 }
18242
18243                 this.dragCurrent.onMouseUp(e);
18244             }
18245
18246             this.dragCurrent = null;
18247             this.dragOvers = {};
18248         },
18249
18250         /**
18251          * Internal function to handle the mousemove event.  Will be invoked
18252          * from the context of the html element.
18253          *
18254          * @TODO figure out what we can do about mouse events lost when the
18255          * user drags objects beyond the window boundary.  Currently we can
18256          * detect this in internet explorer by verifying that the mouse is
18257          * down during the mousemove event.  Firefox doesn't give us the
18258          * button state on the mousemove event.
18259          * @method handleMouseMove
18260          * @param {Event} e the event
18261          * @private
18262          * @static
18263          */
18264         handleMouseMove: function(e) {
18265             if (! this.dragCurrent) {
18266                 return true;
18267             }
18268
18269             // var button = e.which || e.button;
18270
18271             // check for IE mouseup outside of page boundary
18272             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18273                 this.stopEvent(e);
18274                 return this.handleMouseUp(e);
18275             }
18276
18277             if (!this.dragThreshMet) {
18278                 var diffX = Math.abs(this.startX - e.getPageX());
18279                 var diffY = Math.abs(this.startY - e.getPageY());
18280                 if (diffX > this.clickPixelThresh ||
18281                             diffY > this.clickPixelThresh) {
18282                     this.startDrag(this.startX, this.startY);
18283                 }
18284             }
18285
18286             if (this.dragThreshMet) {
18287                 this.dragCurrent.b4Drag(e);
18288                 this.dragCurrent.onDrag(e);
18289                 if(!this.dragCurrent.moveOnly){
18290                     this.fireEvents(e, false);
18291                 }
18292             }
18293
18294             this.stopEvent(e);
18295
18296             return true;
18297         },
18298
18299         /**
18300          * Iterates over all of the DragDrop elements to find ones we are
18301          * hovering over or dropping on
18302          * @method fireEvents
18303          * @param {Event} e the event
18304          * @param {boolean} isDrop is this a drop op or a mouseover op?
18305          * @private
18306          * @static
18307          */
18308         fireEvents: function(e, isDrop) {
18309             var dc = this.dragCurrent;
18310
18311             // If the user did the mouse up outside of the window, we could
18312             // get here even though we have ended the drag.
18313             if (!dc || dc.isLocked()) {
18314                 return;
18315             }
18316
18317             var pt = e.getPoint();
18318
18319             // cache the previous dragOver array
18320             var oldOvers = [];
18321
18322             var outEvts   = [];
18323             var overEvts  = [];
18324             var dropEvts  = [];
18325             var enterEvts = [];
18326
18327             // Check to see if the object(s) we were hovering over is no longer
18328             // being hovered over so we can fire the onDragOut event
18329             for (var i in this.dragOvers) {
18330
18331                 var ddo = this.dragOvers[i];
18332
18333                 if (! this.isTypeOfDD(ddo)) {
18334                     continue;
18335                 }
18336
18337                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18338                     outEvts.push( ddo );
18339                 }
18340
18341                 oldOvers[i] = true;
18342                 delete this.dragOvers[i];
18343             }
18344
18345             for (var sGroup in dc.groups) {
18346
18347                 if ("string" != typeof sGroup) {
18348                     continue;
18349                 }
18350
18351                 for (i in this.ids[sGroup]) {
18352                     var oDD = this.ids[sGroup][i];
18353                     if (! this.isTypeOfDD(oDD)) {
18354                         continue;
18355                     }
18356
18357                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18358                         if (this.isOverTarget(pt, oDD, this.mode)) {
18359                             // look for drop interactions
18360                             if (isDrop) {
18361                                 dropEvts.push( oDD );
18362                             // look for drag enter and drag over interactions
18363                             } else {
18364
18365                                 // initial drag over: dragEnter fires
18366                                 if (!oldOvers[oDD.id]) {
18367                                     enterEvts.push( oDD );
18368                                 // subsequent drag overs: dragOver fires
18369                                 } else {
18370                                     overEvts.push( oDD );
18371                                 }
18372
18373                                 this.dragOvers[oDD.id] = oDD;
18374                             }
18375                         }
18376                     }
18377                 }
18378             }
18379
18380             if (this.mode) {
18381                 if (outEvts.length) {
18382                     dc.b4DragOut(e, outEvts);
18383                     dc.onDragOut(e, outEvts);
18384                 }
18385
18386                 if (enterEvts.length) {
18387                     dc.onDragEnter(e, enterEvts);
18388                 }
18389
18390                 if (overEvts.length) {
18391                     dc.b4DragOver(e, overEvts);
18392                     dc.onDragOver(e, overEvts);
18393                 }
18394
18395                 if (dropEvts.length) {
18396                     dc.b4DragDrop(e, dropEvts);
18397                     dc.onDragDrop(e, dropEvts);
18398                 }
18399
18400             } else {
18401                 // fire dragout events
18402                 var len = 0;
18403                 for (i=0, len=outEvts.length; i<len; ++i) {
18404                     dc.b4DragOut(e, outEvts[i].id);
18405                     dc.onDragOut(e, outEvts[i].id);
18406                 }
18407
18408                 // fire enter events
18409                 for (i=0,len=enterEvts.length; i<len; ++i) {
18410                     // dc.b4DragEnter(e, oDD.id);
18411                     dc.onDragEnter(e, enterEvts[i].id);
18412                 }
18413
18414                 // fire over events
18415                 for (i=0,len=overEvts.length; i<len; ++i) {
18416                     dc.b4DragOver(e, overEvts[i].id);
18417                     dc.onDragOver(e, overEvts[i].id);
18418                 }
18419
18420                 // fire drop events
18421                 for (i=0, len=dropEvts.length; i<len; ++i) {
18422                     dc.b4DragDrop(e, dropEvts[i].id);
18423                     dc.onDragDrop(e, dropEvts[i].id);
18424                 }
18425
18426             }
18427
18428             // notify about a drop that did not find a target
18429             if (isDrop && !dropEvts.length) {
18430                 dc.onInvalidDrop(e);
18431             }
18432
18433         },
18434
18435         /**
18436          * Helper function for getting the best match from the list of drag
18437          * and drop objects returned by the drag and drop events when we are
18438          * in INTERSECT mode.  It returns either the first object that the
18439          * cursor is over, or the object that has the greatest overlap with
18440          * the dragged element.
18441          * @method getBestMatch
18442          * @param  {DragDrop[]} dds The array of drag and drop objects
18443          * targeted
18444          * @return {DragDrop}       The best single match
18445          * @static
18446          */
18447         getBestMatch: function(dds) {
18448             var winner = null;
18449             // Return null if the input is not what we expect
18450             //if (!dds || !dds.length || dds.length == 0) {
18451                // winner = null;
18452             // If there is only one item, it wins
18453             //} else if (dds.length == 1) {
18454
18455             var len = dds.length;
18456
18457             if (len == 1) {
18458                 winner = dds[0];
18459             } else {
18460                 // Loop through the targeted items
18461                 for (var i=0; i<len; ++i) {
18462                     var dd = dds[i];
18463                     // If the cursor is over the object, it wins.  If the
18464                     // cursor is over multiple matches, the first one we come
18465                     // to wins.
18466                     if (dd.cursorIsOver) {
18467                         winner = dd;
18468                         break;
18469                     // Otherwise the object with the most overlap wins
18470                     } else {
18471                         if (!winner ||
18472                             winner.overlap.getArea() < dd.overlap.getArea()) {
18473                             winner = dd;
18474                         }
18475                     }
18476                 }
18477             }
18478
18479             return winner;
18480         },
18481
18482         /**
18483          * Refreshes the cache of the top-left and bottom-right points of the
18484          * drag and drop objects in the specified group(s).  This is in the
18485          * format that is stored in the drag and drop instance, so typical
18486          * usage is:
18487          * <code>
18488          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18489          * </code>
18490          * Alternatively:
18491          * <code>
18492          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18493          * </code>
18494          * @TODO this really should be an indexed array.  Alternatively this
18495          * method could accept both.
18496          * @method refreshCache
18497          * @param {Object} groups an associative array of groups to refresh
18498          * @static
18499          */
18500         refreshCache: function(groups) {
18501             for (var sGroup in groups) {
18502                 if ("string" != typeof sGroup) {
18503                     continue;
18504                 }
18505                 for (var i in this.ids[sGroup]) {
18506                     var oDD = this.ids[sGroup][i];
18507
18508                     if (this.isTypeOfDD(oDD)) {
18509                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18510                         var loc = this.getLocation(oDD);
18511                         if (loc) {
18512                             this.locationCache[oDD.id] = loc;
18513                         } else {
18514                             delete this.locationCache[oDD.id];
18515                             // this will unregister the drag and drop object if
18516                             // the element is not in a usable state
18517                             // oDD.unreg();
18518                         }
18519                     }
18520                 }
18521             }
18522         },
18523
18524         /**
18525          * This checks to make sure an element exists and is in the DOM.  The
18526          * main purpose is to handle cases where innerHTML is used to remove
18527          * drag and drop objects from the DOM.  IE provides an 'unspecified
18528          * error' when trying to access the offsetParent of such an element
18529          * @method verifyEl
18530          * @param {HTMLElement} el the element to check
18531          * @return {boolean} true if the element looks usable
18532          * @static
18533          */
18534         verifyEl: function(el) {
18535             if (el) {
18536                 var parent;
18537                 if(Roo.isIE){
18538                     try{
18539                         parent = el.offsetParent;
18540                     }catch(e){}
18541                 }else{
18542                     parent = el.offsetParent;
18543                 }
18544                 if (parent) {
18545                     return true;
18546                 }
18547             }
18548
18549             return false;
18550         },
18551
18552         /**
18553          * Returns a Region object containing the drag and drop element's position
18554          * and size, including the padding configured for it
18555          * @method getLocation
18556          * @param {DragDrop} oDD the drag and drop object to get the
18557          *                       location for
18558          * @return {Roo.lib.Region} a Region object representing the total area
18559          *                             the element occupies, including any padding
18560          *                             the instance is configured for.
18561          * @static
18562          */
18563         getLocation: function(oDD) {
18564             if (! this.isTypeOfDD(oDD)) {
18565                 return null;
18566             }
18567
18568             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18569
18570             try {
18571                 pos= Roo.lib.Dom.getXY(el);
18572             } catch (e) { }
18573
18574             if (!pos) {
18575                 return null;
18576             }
18577
18578             x1 = pos[0];
18579             x2 = x1 + el.offsetWidth;
18580             y1 = pos[1];
18581             y2 = y1 + el.offsetHeight;
18582
18583             t = y1 - oDD.padding[0];
18584             r = x2 + oDD.padding[1];
18585             b = y2 + oDD.padding[2];
18586             l = x1 - oDD.padding[3];
18587
18588             return new Roo.lib.Region( t, r, b, l );
18589         },
18590
18591         /**
18592          * Checks the cursor location to see if it over the target
18593          * @method isOverTarget
18594          * @param {Roo.lib.Point} pt The point to evaluate
18595          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18596          * @return {boolean} true if the mouse is over the target
18597          * @private
18598          * @static
18599          */
18600         isOverTarget: function(pt, oTarget, intersect) {
18601             // use cache if available
18602             var loc = this.locationCache[oTarget.id];
18603             if (!loc || !this.useCache) {
18604                 loc = this.getLocation(oTarget);
18605                 this.locationCache[oTarget.id] = loc;
18606
18607             }
18608
18609             if (!loc) {
18610                 return false;
18611             }
18612
18613             oTarget.cursorIsOver = loc.contains( pt );
18614
18615             // DragDrop is using this as a sanity check for the initial mousedown
18616             // in this case we are done.  In POINT mode, if the drag obj has no
18617             // contraints, we are also done. Otherwise we need to evaluate the
18618             // location of the target as related to the actual location of the
18619             // dragged element.
18620             var dc = this.dragCurrent;
18621             if (!dc || !dc.getTargetCoord ||
18622                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18623                 return oTarget.cursorIsOver;
18624             }
18625
18626             oTarget.overlap = null;
18627
18628             // Get the current location of the drag element, this is the
18629             // location of the mouse event less the delta that represents
18630             // where the original mousedown happened on the element.  We
18631             // need to consider constraints and ticks as well.
18632             var pos = dc.getTargetCoord(pt.x, pt.y);
18633
18634             var el = dc.getDragEl();
18635             var curRegion = new Roo.lib.Region( pos.y,
18636                                                    pos.x + el.offsetWidth,
18637                                                    pos.y + el.offsetHeight,
18638                                                    pos.x );
18639
18640             var overlap = curRegion.intersect(loc);
18641
18642             if (overlap) {
18643                 oTarget.overlap = overlap;
18644                 return (intersect) ? true : oTarget.cursorIsOver;
18645             } else {
18646                 return false;
18647             }
18648         },
18649
18650         /**
18651          * unload event handler
18652          * @method _onUnload
18653          * @private
18654          * @static
18655          */
18656         _onUnload: function(e, me) {
18657             Roo.dd.DragDropMgr.unregAll();
18658         },
18659
18660         /**
18661          * Cleans up the drag and drop events and objects.
18662          * @method unregAll
18663          * @private
18664          * @static
18665          */
18666         unregAll: function() {
18667
18668             if (this.dragCurrent) {
18669                 this.stopDrag();
18670                 this.dragCurrent = null;
18671             }
18672
18673             this._execOnAll("unreg", []);
18674
18675             for (i in this.elementCache) {
18676                 delete this.elementCache[i];
18677             }
18678
18679             this.elementCache = {};
18680             this.ids = {};
18681         },
18682
18683         /**
18684          * A cache of DOM elements
18685          * @property elementCache
18686          * @private
18687          * @static
18688          */
18689         elementCache: {},
18690
18691         /**
18692          * Get the wrapper for the DOM element specified
18693          * @method getElWrapper
18694          * @param {String} id the id of the element to get
18695          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18696          * @private
18697          * @deprecated This wrapper isn't that useful
18698          * @static
18699          */
18700         getElWrapper: function(id) {
18701             var oWrapper = this.elementCache[id];
18702             if (!oWrapper || !oWrapper.el) {
18703                 oWrapper = this.elementCache[id] =
18704                     new this.ElementWrapper(Roo.getDom(id));
18705             }
18706             return oWrapper;
18707         },
18708
18709         /**
18710          * Returns the actual DOM element
18711          * @method getElement
18712          * @param {String} id the id of the elment to get
18713          * @return {Object} The element
18714          * @deprecated use Roo.getDom instead
18715          * @static
18716          */
18717         getElement: function(id) {
18718             return Roo.getDom(id);
18719         },
18720
18721         /**
18722          * Returns the style property for the DOM element (i.e.,
18723          * document.getElById(id).style)
18724          * @method getCss
18725          * @param {String} id the id of the elment to get
18726          * @return {Object} The style property of the element
18727          * @deprecated use Roo.getDom instead
18728          * @static
18729          */
18730         getCss: function(id) {
18731             var el = Roo.getDom(id);
18732             return (el) ? el.style : null;
18733         },
18734
18735         /**
18736          * Inner class for cached elements
18737          * @class DragDropMgr.ElementWrapper
18738          * @for DragDropMgr
18739          * @private
18740          * @deprecated
18741          */
18742         ElementWrapper: function(el) {
18743                 /**
18744                  * The element
18745                  * @property el
18746                  */
18747                 this.el = el || null;
18748                 /**
18749                  * The element id
18750                  * @property id
18751                  */
18752                 this.id = this.el && el.id;
18753                 /**
18754                  * A reference to the style property
18755                  * @property css
18756                  */
18757                 this.css = this.el && el.style;
18758             },
18759
18760         /**
18761          * Returns the X position of an html element
18762          * @method getPosX
18763          * @param el the element for which to get the position
18764          * @return {int} the X coordinate
18765          * @for DragDropMgr
18766          * @deprecated use Roo.lib.Dom.getX instead
18767          * @static
18768          */
18769         getPosX: function(el) {
18770             return Roo.lib.Dom.getX(el);
18771         },
18772
18773         /**
18774          * Returns the Y position of an html element
18775          * @method getPosY
18776          * @param el the element for which to get the position
18777          * @return {int} the Y coordinate
18778          * @deprecated use Roo.lib.Dom.getY instead
18779          * @static
18780          */
18781         getPosY: function(el) {
18782             return Roo.lib.Dom.getY(el);
18783         },
18784
18785         /**
18786          * Swap two nodes.  In IE, we use the native method, for others we
18787          * emulate the IE behavior
18788          * @method swapNode
18789          * @param n1 the first node to swap
18790          * @param n2 the other node to swap
18791          * @static
18792          */
18793         swapNode: function(n1, n2) {
18794             if (n1.swapNode) {
18795                 n1.swapNode(n2);
18796             } else {
18797                 var p = n2.parentNode;
18798                 var s = n2.nextSibling;
18799
18800                 if (s == n1) {
18801                     p.insertBefore(n1, n2);
18802                 } else if (n2 == n1.nextSibling) {
18803                     p.insertBefore(n2, n1);
18804                 } else {
18805                     n1.parentNode.replaceChild(n2, n1);
18806                     p.insertBefore(n1, s);
18807                 }
18808             }
18809         },
18810
18811         /**
18812          * Returns the current scroll position
18813          * @method getScroll
18814          * @private
18815          * @static
18816          */
18817         getScroll: function () {
18818             var t, l, dde=document.documentElement, db=document.body;
18819             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18820                 t = dde.scrollTop;
18821                 l = dde.scrollLeft;
18822             } else if (db) {
18823                 t = db.scrollTop;
18824                 l = db.scrollLeft;
18825             } else {
18826
18827             }
18828             return { top: t, left: l };
18829         },
18830
18831         /**
18832          * Returns the specified element style property
18833          * @method getStyle
18834          * @param {HTMLElement} el          the element
18835          * @param {string}      styleProp   the style property
18836          * @return {string} The value of the style property
18837          * @deprecated use Roo.lib.Dom.getStyle
18838          * @static
18839          */
18840         getStyle: function(el, styleProp) {
18841             return Roo.fly(el).getStyle(styleProp);
18842         },
18843
18844         /**
18845          * Gets the scrollTop
18846          * @method getScrollTop
18847          * @return {int} the document's scrollTop
18848          * @static
18849          */
18850         getScrollTop: function () { return this.getScroll().top; },
18851
18852         /**
18853          * Gets the scrollLeft
18854          * @method getScrollLeft
18855          * @return {int} the document's scrollTop
18856          * @static
18857          */
18858         getScrollLeft: function () { return this.getScroll().left; },
18859
18860         /**
18861          * Sets the x/y position of an element to the location of the
18862          * target element.
18863          * @method moveToEl
18864          * @param {HTMLElement} moveEl      The element to move
18865          * @param {HTMLElement} targetEl    The position reference element
18866          * @static
18867          */
18868         moveToEl: function (moveEl, targetEl) {
18869             var aCoord = Roo.lib.Dom.getXY(targetEl);
18870             Roo.lib.Dom.setXY(moveEl, aCoord);
18871         },
18872
18873         /**
18874          * Numeric array sort function
18875          * @method numericSort
18876          * @static
18877          */
18878         numericSort: function(a, b) { return (a - b); },
18879
18880         /**
18881          * Internal counter
18882          * @property _timeoutCount
18883          * @private
18884          * @static
18885          */
18886         _timeoutCount: 0,
18887
18888         /**
18889          * Trying to make the load order less important.  Without this we get
18890          * an error if this file is loaded before the Event Utility.
18891          * @method _addListeners
18892          * @private
18893          * @static
18894          */
18895         _addListeners: function() {
18896             var DDM = Roo.dd.DDM;
18897             if ( Roo.lib.Event && document ) {
18898                 DDM._onLoad();
18899             } else {
18900                 if (DDM._timeoutCount > 2000) {
18901                 } else {
18902                     setTimeout(DDM._addListeners, 10);
18903                     if (document && document.body) {
18904                         DDM._timeoutCount += 1;
18905                     }
18906                 }
18907             }
18908         },
18909
18910         /**
18911          * Recursively searches the immediate parent and all child nodes for
18912          * the handle element in order to determine wheter or not it was
18913          * clicked.
18914          * @method handleWasClicked
18915          * @param node the html element to inspect
18916          * @static
18917          */
18918         handleWasClicked: function(node, id) {
18919             if (this.isHandle(id, node.id)) {
18920                 return true;
18921             } else {
18922                 // check to see if this is a text node child of the one we want
18923                 var p = node.parentNode;
18924
18925                 while (p) {
18926                     if (this.isHandle(id, p.id)) {
18927                         return true;
18928                     } else {
18929                         p = p.parentNode;
18930                     }
18931                 }
18932             }
18933
18934             return false;
18935         }
18936
18937     };
18938
18939 }();
18940
18941 // shorter alias, save a few bytes
18942 Roo.dd.DDM = Roo.dd.DragDropMgr;
18943 Roo.dd.DDM._addListeners();
18944
18945 }/*
18946  * Based on:
18947  * Ext JS Library 1.1.1
18948  * Copyright(c) 2006-2007, Ext JS, LLC.
18949  *
18950  * Originally Released Under LGPL - original licence link has changed is not relivant.
18951  *
18952  * Fork - LGPL
18953  * <script type="text/javascript">
18954  */
18955
18956 /**
18957  * @class Roo.dd.DD
18958  * A DragDrop implementation where the linked element follows the
18959  * mouse cursor during a drag.
18960  * @extends Roo.dd.DragDrop
18961  * @constructor
18962  * @param {String} id the id of the linked element
18963  * @param {String} sGroup the group of related DragDrop items
18964  * @param {object} config an object containing configurable attributes
18965  *                Valid properties for DD:
18966  *                    scroll
18967  */
18968 Roo.dd.DD = function(id, sGroup, config) {
18969     if (id) {
18970         this.init(id, sGroup, config);
18971     }
18972 };
18973
18974 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18975
18976     /**
18977      * When set to true, the utility automatically tries to scroll the browser
18978      * window wehn a drag and drop element is dragged near the viewport boundary.
18979      * Defaults to true.
18980      * @property scroll
18981      * @type boolean
18982      */
18983     scroll: true,
18984
18985     /**
18986      * Sets the pointer offset to the distance between the linked element's top
18987      * left corner and the location the element was clicked
18988      * @method autoOffset
18989      * @param {int} iPageX the X coordinate of the click
18990      * @param {int} iPageY the Y coordinate of the click
18991      */
18992     autoOffset: function(iPageX, iPageY) {
18993         var x = iPageX - this.startPageX;
18994         var y = iPageY - this.startPageY;
18995         this.setDelta(x, y);
18996     },
18997
18998     /**
18999      * Sets the pointer offset.  You can call this directly to force the
19000      * offset to be in a particular location (e.g., pass in 0,0 to set it
19001      * to the center of the object)
19002      * @method setDelta
19003      * @param {int} iDeltaX the distance from the left
19004      * @param {int} iDeltaY the distance from the top
19005      */
19006     setDelta: function(iDeltaX, iDeltaY) {
19007         this.deltaX = iDeltaX;
19008         this.deltaY = iDeltaY;
19009     },
19010
19011     /**
19012      * Sets the drag element to the location of the mousedown or click event,
19013      * maintaining the cursor location relative to the location on the element
19014      * that was clicked.  Override this if you want to place the element in a
19015      * location other than where the cursor is.
19016      * @method setDragElPos
19017      * @param {int} iPageX the X coordinate of the mousedown or drag event
19018      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19019      */
19020     setDragElPos: function(iPageX, iPageY) {
19021         // the first time we do this, we are going to check to make sure
19022         // the element has css positioning
19023
19024         var el = this.getDragEl();
19025         this.alignElWithMouse(el, iPageX, iPageY);
19026     },
19027
19028     /**
19029      * Sets the element to the location of the mousedown or click event,
19030      * maintaining the cursor location relative to the location on the element
19031      * that was clicked.  Override this if you want to place the element in a
19032      * location other than where the cursor is.
19033      * @method alignElWithMouse
19034      * @param {HTMLElement} el the element to move
19035      * @param {int} iPageX the X coordinate of the mousedown or drag event
19036      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19037      */
19038     alignElWithMouse: function(el, iPageX, iPageY) {
19039         var oCoord = this.getTargetCoord(iPageX, iPageY);
19040         var fly = el.dom ? el : Roo.fly(el);
19041         if (!this.deltaSetXY) {
19042             var aCoord = [oCoord.x, oCoord.y];
19043             fly.setXY(aCoord);
19044             var newLeft = fly.getLeft(true);
19045             var newTop  = fly.getTop(true);
19046             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
19047         } else {
19048             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
19049         }
19050
19051         this.cachePosition(oCoord.x, oCoord.y);
19052         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
19053         return oCoord;
19054     },
19055
19056     /**
19057      * Saves the most recent position so that we can reset the constraints and
19058      * tick marks on-demand.  We need to know this so that we can calculate the
19059      * number of pixels the element is offset from its original position.
19060      * @method cachePosition
19061      * @param iPageX the current x position (optional, this just makes it so we
19062      * don't have to look it up again)
19063      * @param iPageY the current y position (optional, this just makes it so we
19064      * don't have to look it up again)
19065      */
19066     cachePosition: function(iPageX, iPageY) {
19067         if (iPageX) {
19068             this.lastPageX = iPageX;
19069             this.lastPageY = iPageY;
19070         } else {
19071             var aCoord = Roo.lib.Dom.getXY(this.getEl());
19072             this.lastPageX = aCoord[0];
19073             this.lastPageY = aCoord[1];
19074         }
19075     },
19076
19077     /**
19078      * Auto-scroll the window if the dragged object has been moved beyond the
19079      * visible window boundary.
19080      * @method autoScroll
19081      * @param {int} x the drag element's x position
19082      * @param {int} y the drag element's y position
19083      * @param {int} h the height of the drag element
19084      * @param {int} w the width of the drag element
19085      * @private
19086      */
19087     autoScroll: function(x, y, h, w) {
19088
19089         if (this.scroll) {
19090             // The client height
19091             var clientH = Roo.lib.Dom.getViewWidth();
19092
19093             // The client width
19094             var clientW = Roo.lib.Dom.getViewHeight();
19095
19096             // The amt scrolled down
19097             var st = this.DDM.getScrollTop();
19098
19099             // The amt scrolled right
19100             var sl = this.DDM.getScrollLeft();
19101
19102             // Location of the bottom of the element
19103             var bot = h + y;
19104
19105             // Location of the right of the element
19106             var right = w + x;
19107
19108             // The distance from the cursor to the bottom of the visible area,
19109             // adjusted so that we don't scroll if the cursor is beyond the
19110             // element drag constraints
19111             var toBot = (clientH + st - y - this.deltaY);
19112
19113             // The distance from the cursor to the right of the visible area
19114             var toRight = (clientW + sl - x - this.deltaX);
19115
19116
19117             // How close to the edge the cursor must be before we scroll
19118             // var thresh = (document.all) ? 100 : 40;
19119             var thresh = 40;
19120
19121             // How many pixels to scroll per autoscroll op.  This helps to reduce
19122             // clunky scrolling. IE is more sensitive about this ... it needs this
19123             // value to be higher.
19124             var scrAmt = (document.all) ? 80 : 30;
19125
19126             // Scroll down if we are near the bottom of the visible page and the
19127             // obj extends below the crease
19128             if ( bot > clientH && toBot < thresh ) {
19129                 window.scrollTo(sl, st + scrAmt);
19130             }
19131
19132             // Scroll up if the window is scrolled down and the top of the object
19133             // goes above the top border
19134             if ( y < st && st > 0 && y - st < thresh ) {
19135                 window.scrollTo(sl, st - scrAmt);
19136             }
19137
19138             // Scroll right if the obj is beyond the right border and the cursor is
19139             // near the border.
19140             if ( right > clientW && toRight < thresh ) {
19141                 window.scrollTo(sl + scrAmt, st);
19142             }
19143
19144             // Scroll left if the window has been scrolled to the right and the obj
19145             // extends past the left border
19146             if ( x < sl && sl > 0 && x - sl < thresh ) {
19147                 window.scrollTo(sl - scrAmt, st);
19148             }
19149         }
19150     },
19151
19152     /**
19153      * Finds the location the element should be placed if we want to move
19154      * it to where the mouse location less the click offset would place us.
19155      * @method getTargetCoord
19156      * @param {int} iPageX the X coordinate of the click
19157      * @param {int} iPageY the Y coordinate of the click
19158      * @return an object that contains the coordinates (Object.x and Object.y)
19159      * @private
19160      */
19161     getTargetCoord: function(iPageX, iPageY) {
19162
19163
19164         var x = iPageX - this.deltaX;
19165         var y = iPageY - this.deltaY;
19166
19167         if (this.constrainX) {
19168             if (x < this.minX) { x = this.minX; }
19169             if (x > this.maxX) { x = this.maxX; }
19170         }
19171
19172         if (this.constrainY) {
19173             if (y < this.minY) { y = this.minY; }
19174             if (y > this.maxY) { y = this.maxY; }
19175         }
19176
19177         x = this.getTick(x, this.xTicks);
19178         y = this.getTick(y, this.yTicks);
19179
19180
19181         return {x:x, y:y};
19182     },
19183
19184     /*
19185      * Sets up config options specific to this class. Overrides
19186      * Roo.dd.DragDrop, but all versions of this method through the
19187      * inheritance chain are called
19188      */
19189     applyConfig: function() {
19190         Roo.dd.DD.superclass.applyConfig.call(this);
19191         this.scroll = (this.config.scroll !== false);
19192     },
19193
19194     /*
19195      * Event that fires prior to the onMouseDown event.  Overrides
19196      * Roo.dd.DragDrop.
19197      */
19198     b4MouseDown: function(e) {
19199         // this.resetConstraints();
19200         this.autoOffset(e.getPageX(),
19201                             e.getPageY());
19202     },
19203
19204     /*
19205      * Event that fires prior to the onDrag event.  Overrides
19206      * Roo.dd.DragDrop.
19207      */
19208     b4Drag: function(e) {
19209         this.setDragElPos(e.getPageX(),
19210                             e.getPageY());
19211     },
19212
19213     toString: function() {
19214         return ("DD " + this.id);
19215     }
19216
19217     //////////////////////////////////////////////////////////////////////////
19218     // Debugging ygDragDrop events that can be overridden
19219     //////////////////////////////////////////////////////////////////////////
19220     /*
19221     startDrag: function(x, y) {
19222     },
19223
19224     onDrag: function(e) {
19225     },
19226
19227     onDragEnter: function(e, id) {
19228     },
19229
19230     onDragOver: function(e, id) {
19231     },
19232
19233     onDragOut: function(e, id) {
19234     },
19235
19236     onDragDrop: function(e, id) {
19237     },
19238
19239     endDrag: function(e) {
19240     }
19241
19242     */
19243
19244 });/*
19245  * Based on:
19246  * Ext JS Library 1.1.1
19247  * Copyright(c) 2006-2007, Ext JS, LLC.
19248  *
19249  * Originally Released Under LGPL - original licence link has changed is not relivant.
19250  *
19251  * Fork - LGPL
19252  * <script type="text/javascript">
19253  */
19254
19255 /**
19256  * @class Roo.dd.DDProxy
19257  * A DragDrop implementation that inserts an empty, bordered div into
19258  * the document that follows the cursor during drag operations.  At the time of
19259  * the click, the frame div is resized to the dimensions of the linked html
19260  * element, and moved to the exact location of the linked element.
19261  *
19262  * References to the "frame" element refer to the single proxy element that
19263  * was created to be dragged in place of all DDProxy elements on the
19264  * page.
19265  *
19266  * @extends Roo.dd.DD
19267  * @constructor
19268  * @param {String} id the id of the linked html element
19269  * @param {String} sGroup the group of related DragDrop objects
19270  * @param {object} config an object containing configurable attributes
19271  *                Valid properties for DDProxy in addition to those in DragDrop:
19272  *                   resizeFrame, centerFrame, dragElId
19273  */
19274 Roo.dd.DDProxy = function(id, sGroup, config) {
19275     if (id) {
19276         this.init(id, sGroup, config);
19277         this.initFrame();
19278     }
19279 };
19280
19281 /**
19282  * The default drag frame div id
19283  * @property Roo.dd.DDProxy.dragElId
19284  * @type String
19285  * @static
19286  */
19287 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19288
19289 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19290
19291     /**
19292      * By default we resize the drag frame to be the same size as the element
19293      * we want to drag (this is to get the frame effect).  We can turn it off
19294      * if we want a different behavior.
19295      * @property resizeFrame
19296      * @type boolean
19297      */
19298     resizeFrame: true,
19299
19300     /**
19301      * By default the frame is positioned exactly where the drag element is, so
19302      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19303      * you do not have constraints on the obj is to have the drag frame centered
19304      * around the cursor.  Set centerFrame to true for this effect.
19305      * @property centerFrame
19306      * @type boolean
19307      */
19308     centerFrame: false,
19309
19310     /**
19311      * Creates the proxy element if it does not yet exist
19312      * @method createFrame
19313      */
19314     createFrame: function() {
19315         var self = this;
19316         var body = document.body;
19317
19318         if (!body || !body.firstChild) {
19319             setTimeout( function() { self.createFrame(); }, 50 );
19320             return;
19321         }
19322
19323         var div = this.getDragEl();
19324
19325         if (!div) {
19326             div    = document.createElement("div");
19327             div.id = this.dragElId;
19328             var s  = div.style;
19329
19330             s.position   = "absolute";
19331             s.visibility = "hidden";
19332             s.cursor     = "move";
19333             s.border     = "2px solid #aaa";
19334             s.zIndex     = 999;
19335
19336             // appendChild can blow up IE if invoked prior to the window load event
19337             // while rendering a table.  It is possible there are other scenarios
19338             // that would cause this to happen as well.
19339             body.insertBefore(div, body.firstChild);
19340         }
19341     },
19342
19343     /**
19344      * Initialization for the drag frame element.  Must be called in the
19345      * constructor of all subclasses
19346      * @method initFrame
19347      */
19348     initFrame: function() {
19349         this.createFrame();
19350     },
19351
19352     applyConfig: function() {
19353         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19354
19355         this.resizeFrame = (this.config.resizeFrame !== false);
19356         this.centerFrame = (this.config.centerFrame);
19357         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19358     },
19359
19360     /**
19361      * Resizes the drag frame to the dimensions of the clicked object, positions
19362      * it over the object, and finally displays it
19363      * @method showFrame
19364      * @param {int} iPageX X click position
19365      * @param {int} iPageY Y click position
19366      * @private
19367      */
19368     showFrame: function(iPageX, iPageY) {
19369         var el = this.getEl();
19370         var dragEl = this.getDragEl();
19371         var s = dragEl.style;
19372
19373         this._resizeProxy();
19374
19375         if (this.centerFrame) {
19376             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19377                            Math.round(parseInt(s.height, 10)/2) );
19378         }
19379
19380         this.setDragElPos(iPageX, iPageY);
19381
19382         Roo.fly(dragEl).show();
19383     },
19384
19385     /**
19386      * The proxy is automatically resized to the dimensions of the linked
19387      * element when a drag is initiated, unless resizeFrame is set to false
19388      * @method _resizeProxy
19389      * @private
19390      */
19391     _resizeProxy: function() {
19392         if (this.resizeFrame) {
19393             var el = this.getEl();
19394             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19395         }
19396     },
19397
19398     // overrides Roo.dd.DragDrop
19399     b4MouseDown: function(e) {
19400         var x = e.getPageX();
19401         var y = e.getPageY();
19402         this.autoOffset(x, y);
19403         this.setDragElPos(x, y);
19404     },
19405
19406     // overrides Roo.dd.DragDrop
19407     b4StartDrag: function(x, y) {
19408         // show the drag frame
19409         this.showFrame(x, y);
19410     },
19411
19412     // overrides Roo.dd.DragDrop
19413     b4EndDrag: function(e) {
19414         Roo.fly(this.getDragEl()).hide();
19415     },
19416
19417     // overrides Roo.dd.DragDrop
19418     // By default we try to move the element to the last location of the frame.
19419     // This is so that the default behavior mirrors that of Roo.dd.DD.
19420     endDrag: function(e) {
19421
19422         var lel = this.getEl();
19423         var del = this.getDragEl();
19424
19425         // Show the drag frame briefly so we can get its position
19426         del.style.visibility = "";
19427
19428         this.beforeMove();
19429         // Hide the linked element before the move to get around a Safari
19430         // rendering bug.
19431         lel.style.visibility = "hidden";
19432         Roo.dd.DDM.moveToEl(lel, del);
19433         del.style.visibility = "hidden";
19434         lel.style.visibility = "";
19435
19436         this.afterDrag();
19437     },
19438
19439     beforeMove : function(){
19440
19441     },
19442
19443     afterDrag : function(){
19444
19445     },
19446
19447     toString: function() {
19448         return ("DDProxy " + this.id);
19449     }
19450
19451 });
19452 /*
19453  * Based on:
19454  * Ext JS Library 1.1.1
19455  * Copyright(c) 2006-2007, Ext JS, LLC.
19456  *
19457  * Originally Released Under LGPL - original licence link has changed is not relivant.
19458  *
19459  * Fork - LGPL
19460  * <script type="text/javascript">
19461  */
19462
19463  /**
19464  * @class Roo.dd.DDTarget
19465  * A DragDrop implementation that does not move, but can be a drop
19466  * target.  You would get the same result by simply omitting implementation
19467  * for the event callbacks, but this way we reduce the processing cost of the
19468  * event listener and the callbacks.
19469  * @extends Roo.dd.DragDrop
19470  * @constructor
19471  * @param {String} id the id of the element that is a drop target
19472  * @param {String} sGroup the group of related DragDrop objects
19473  * @param {object} config an object containing configurable attributes
19474  *                 Valid properties for DDTarget in addition to those in
19475  *                 DragDrop:
19476  *                    none
19477  */
19478 Roo.dd.DDTarget = function(id, sGroup, config) {
19479     if (id) {
19480         this.initTarget(id, sGroup, config);
19481     }
19482     if (config.listeners || config.events) { 
19483        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19484             listeners : config.listeners || {}, 
19485             events : config.events || {} 
19486         });    
19487     }
19488 };
19489
19490 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19491 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19492     toString: function() {
19493         return ("DDTarget " + this.id);
19494     }
19495 });
19496 /*
19497  * Based on:
19498  * Ext JS Library 1.1.1
19499  * Copyright(c) 2006-2007, Ext JS, LLC.
19500  *
19501  * Originally Released Under LGPL - original licence link has changed is not relivant.
19502  *
19503  * Fork - LGPL
19504  * <script type="text/javascript">
19505  */
19506  
19507
19508 /**
19509  * @class Roo.dd.ScrollManager
19510  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19511  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19512  * @singleton
19513  */
19514 Roo.dd.ScrollManager = function(){
19515     var ddm = Roo.dd.DragDropMgr;
19516     var els = {};
19517     var dragEl = null;
19518     var proc = {};
19519     
19520     
19521     
19522     var onStop = function(e){
19523         dragEl = null;
19524         clearProc();
19525     };
19526     
19527     var triggerRefresh = function(){
19528         if(ddm.dragCurrent){
19529              ddm.refreshCache(ddm.dragCurrent.groups);
19530         }
19531     };
19532     
19533     var doScroll = function(){
19534         if(ddm.dragCurrent){
19535             var dds = Roo.dd.ScrollManager;
19536             if(!dds.animate){
19537                 if(proc.el.scroll(proc.dir, dds.increment)){
19538                     triggerRefresh();
19539                 }
19540             }else{
19541                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19542             }
19543         }
19544     };
19545     
19546     var clearProc = function(){
19547         if(proc.id){
19548             clearInterval(proc.id);
19549         }
19550         proc.id = 0;
19551         proc.el = null;
19552         proc.dir = "";
19553     };
19554     
19555     var startProc = function(el, dir){
19556          Roo.log('scroll startproc');
19557         clearProc();
19558         proc.el = el;
19559         proc.dir = dir;
19560         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19561     };
19562     
19563     var onFire = function(e, isDrop){
19564        
19565         if(isDrop || !ddm.dragCurrent){ return; }
19566         var dds = Roo.dd.ScrollManager;
19567         if(!dragEl || dragEl != ddm.dragCurrent){
19568             dragEl = ddm.dragCurrent;
19569             // refresh regions on drag start
19570             dds.refreshCache();
19571         }
19572         
19573         var xy = Roo.lib.Event.getXY(e);
19574         var pt = new Roo.lib.Point(xy[0], xy[1]);
19575         for(var id in els){
19576             var el = els[id], r = el._region;
19577             if(r && r.contains(pt) && el.isScrollable()){
19578                 if(r.bottom - pt.y <= dds.thresh){
19579                     if(proc.el != el){
19580                         startProc(el, "down");
19581                     }
19582                     return;
19583                 }else if(r.right - pt.x <= dds.thresh){
19584                     if(proc.el != el){
19585                         startProc(el, "left");
19586                     }
19587                     return;
19588                 }else if(pt.y - r.top <= dds.thresh){
19589                     if(proc.el != el){
19590                         startProc(el, "up");
19591                     }
19592                     return;
19593                 }else if(pt.x - r.left <= dds.thresh){
19594                     if(proc.el != el){
19595                         startProc(el, "right");
19596                     }
19597                     return;
19598                 }
19599             }
19600         }
19601         clearProc();
19602     };
19603     
19604     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19605     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19606     
19607     return {
19608         /**
19609          * Registers new overflow element(s) to auto scroll
19610          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19611          */
19612         register : function(el){
19613             if(el instanceof Array){
19614                 for(var i = 0, len = el.length; i < len; i++) {
19615                         this.register(el[i]);
19616                 }
19617             }else{
19618                 el = Roo.get(el);
19619                 els[el.id] = el;
19620             }
19621             Roo.dd.ScrollManager.els = els;
19622         },
19623         
19624         /**
19625          * Unregisters overflow element(s) so they are no longer scrolled
19626          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19627          */
19628         unregister : function(el){
19629             if(el instanceof Array){
19630                 for(var i = 0, len = el.length; i < len; i++) {
19631                         this.unregister(el[i]);
19632                 }
19633             }else{
19634                 el = Roo.get(el);
19635                 delete els[el.id];
19636             }
19637         },
19638         
19639         /**
19640          * The number of pixels from the edge of a container the pointer needs to be to 
19641          * trigger scrolling (defaults to 25)
19642          * @type Number
19643          */
19644         thresh : 25,
19645         
19646         /**
19647          * The number of pixels to scroll in each scroll increment (defaults to 50)
19648          * @type Number
19649          */
19650         increment : 100,
19651         
19652         /**
19653          * The frequency of scrolls in milliseconds (defaults to 500)
19654          * @type Number
19655          */
19656         frequency : 500,
19657         
19658         /**
19659          * True to animate the scroll (defaults to true)
19660          * @type Boolean
19661          */
19662         animate: true,
19663         
19664         /**
19665          * The animation duration in seconds - 
19666          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19667          * @type Number
19668          */
19669         animDuration: .4,
19670         
19671         /**
19672          * Manually trigger a cache refresh.
19673          */
19674         refreshCache : function(){
19675             for(var id in els){
19676                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19677                     els[id]._region = els[id].getRegion();
19678                 }
19679             }
19680         }
19681     };
19682 }();/*
19683  * Based on:
19684  * Ext JS Library 1.1.1
19685  * Copyright(c) 2006-2007, Ext JS, LLC.
19686  *
19687  * Originally Released Under LGPL - original licence link has changed is not relivant.
19688  *
19689  * Fork - LGPL
19690  * <script type="text/javascript">
19691  */
19692  
19693
19694 /**
19695  * @class Roo.dd.Registry
19696  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19697  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19698  * @singleton
19699  */
19700 Roo.dd.Registry = function(){
19701     var elements = {}; 
19702     var handles = {}; 
19703     var autoIdSeed = 0;
19704
19705     var getId = function(el, autogen){
19706         if(typeof el == "string"){
19707             return el;
19708         }
19709         var id = el.id;
19710         if(!id && autogen !== false){
19711             id = "roodd-" + (++autoIdSeed);
19712             el.id = id;
19713         }
19714         return id;
19715     };
19716     
19717     return {
19718     /**
19719      * Register a drag drop element
19720      * @param {String|HTMLElement} element The id or DOM node to register
19721      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19722      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19723      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19724      * populated in the data object (if applicable):
19725      * <pre>
19726 Value      Description<br />
19727 ---------  ------------------------------------------<br />
19728 handles    Array of DOM nodes that trigger dragging<br />
19729            for the element being registered<br />
19730 isHandle   True if the element passed in triggers<br />
19731            dragging itself, else false
19732 </pre>
19733      */
19734         register : function(el, data){
19735             data = data || {};
19736             if(typeof el == "string"){
19737                 el = document.getElementById(el);
19738             }
19739             data.ddel = el;
19740             elements[getId(el)] = data;
19741             if(data.isHandle !== false){
19742                 handles[data.ddel.id] = data;
19743             }
19744             if(data.handles){
19745                 var hs = data.handles;
19746                 for(var i = 0, len = hs.length; i < len; i++){
19747                         handles[getId(hs[i])] = data;
19748                 }
19749             }
19750         },
19751
19752     /**
19753      * Unregister a drag drop element
19754      * @param {String|HTMLElement}  element The id or DOM node to unregister
19755      */
19756         unregister : function(el){
19757             var id = getId(el, false);
19758             var data = elements[id];
19759             if(data){
19760                 delete elements[id];
19761                 if(data.handles){
19762                     var hs = data.handles;
19763                     for(var i = 0, len = hs.length; i < len; i++){
19764                         delete handles[getId(hs[i], false)];
19765                     }
19766                 }
19767             }
19768         },
19769
19770     /**
19771      * Returns the handle registered for a DOM Node by id
19772      * @param {String|HTMLElement} id The DOM node or id to look up
19773      * @return {Object} handle The custom handle data
19774      */
19775         getHandle : function(id){
19776             if(typeof id != "string"){ // must be element?
19777                 id = id.id;
19778             }
19779             return handles[id];
19780         },
19781
19782     /**
19783      * Returns the handle that is registered for the DOM node that is the target of the event
19784      * @param {Event} e The event
19785      * @return {Object} handle The custom handle data
19786      */
19787         getHandleFromEvent : function(e){
19788             var t = Roo.lib.Event.getTarget(e);
19789             return t ? handles[t.id] : null;
19790         },
19791
19792     /**
19793      * Returns a custom data object that is registered for a DOM node by id
19794      * @param {String|HTMLElement} id The DOM node or id to look up
19795      * @return {Object} data The custom data
19796      */
19797         getTarget : function(id){
19798             if(typeof id != "string"){ // must be element?
19799                 id = id.id;
19800             }
19801             return elements[id];
19802         },
19803
19804     /**
19805      * Returns a custom data object that is registered for the DOM node that is the target of the event
19806      * @param {Event} e The event
19807      * @return {Object} data The custom data
19808      */
19809         getTargetFromEvent : function(e){
19810             var t = Roo.lib.Event.getTarget(e);
19811             return t ? elements[t.id] || handles[t.id] : null;
19812         }
19813     };
19814 }();/*
19815  * Based on:
19816  * Ext JS Library 1.1.1
19817  * Copyright(c) 2006-2007, Ext JS, LLC.
19818  *
19819  * Originally Released Under LGPL - original licence link has changed is not relivant.
19820  *
19821  * Fork - LGPL
19822  * <script type="text/javascript">
19823  */
19824  
19825
19826 /**
19827  * @class Roo.dd.StatusProxy
19828  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19829  * default drag proxy used by all Roo.dd components.
19830  * @constructor
19831  * @param {Object} config
19832  */
19833 Roo.dd.StatusProxy = function(config){
19834     Roo.apply(this, config);
19835     this.id = this.id || Roo.id();
19836     this.el = new Roo.Layer({
19837         dh: {
19838             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19839                 {tag: "div", cls: "x-dd-drop-icon"},
19840                 {tag: "div", cls: "x-dd-drag-ghost"}
19841             ]
19842         }, 
19843         shadow: !config || config.shadow !== false
19844     });
19845     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19846     this.dropStatus = this.dropNotAllowed;
19847 };
19848
19849 Roo.dd.StatusProxy.prototype = {
19850     /**
19851      * @cfg {String} dropAllowed
19852      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19853      */
19854     dropAllowed : "x-dd-drop-ok",
19855     /**
19856      * @cfg {String} dropNotAllowed
19857      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19858      */
19859     dropNotAllowed : "x-dd-drop-nodrop",
19860
19861     /**
19862      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19863      * over the current target element.
19864      * @param {String} cssClass The css class for the new drop status indicator image
19865      */
19866     setStatus : function(cssClass){
19867         cssClass = cssClass || this.dropNotAllowed;
19868         if(this.dropStatus != cssClass){
19869             this.el.replaceClass(this.dropStatus, cssClass);
19870             this.dropStatus = cssClass;
19871         }
19872     },
19873
19874     /**
19875      * Resets the status indicator to the default dropNotAllowed value
19876      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19877      */
19878     reset : function(clearGhost){
19879         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19880         this.dropStatus = this.dropNotAllowed;
19881         if(clearGhost){
19882             this.ghost.update("");
19883         }
19884     },
19885
19886     /**
19887      * Updates the contents of the ghost element
19888      * @param {String} html The html that will replace the current innerHTML of the ghost element
19889      */
19890     update : function(html){
19891         if(typeof html == "string"){
19892             this.ghost.update(html);
19893         }else{
19894             this.ghost.update("");
19895             html.style.margin = "0";
19896             this.ghost.dom.appendChild(html);
19897         }
19898         // ensure float = none set?? cant remember why though.
19899         var el = this.ghost.dom.firstChild;
19900                 if(el){
19901                         Roo.fly(el).setStyle('float', 'none');
19902                 }
19903     },
19904     
19905     /**
19906      * Returns the underlying proxy {@link Roo.Layer}
19907      * @return {Roo.Layer} el
19908     */
19909     getEl : function(){
19910         return this.el;
19911     },
19912
19913     /**
19914      * Returns the ghost element
19915      * @return {Roo.Element} el
19916      */
19917     getGhost : function(){
19918         return this.ghost;
19919     },
19920
19921     /**
19922      * Hides the proxy
19923      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19924      */
19925     hide : function(clear){
19926         this.el.hide();
19927         if(clear){
19928             this.reset(true);
19929         }
19930     },
19931
19932     /**
19933      * Stops the repair animation if it's currently running
19934      */
19935     stop : function(){
19936         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19937             this.anim.stop();
19938         }
19939     },
19940
19941     /**
19942      * Displays this proxy
19943      */
19944     show : function(){
19945         this.el.show();
19946     },
19947
19948     /**
19949      * Force the Layer to sync its shadow and shim positions to the element
19950      */
19951     sync : function(){
19952         this.el.sync();
19953     },
19954
19955     /**
19956      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19957      * invalid drop operation by the item being dragged.
19958      * @param {Array} xy The XY position of the element ([x, y])
19959      * @param {Function} callback The function to call after the repair is complete
19960      * @param {Object} scope The scope in which to execute the callback
19961      */
19962     repair : function(xy, callback, scope){
19963         this.callback = callback;
19964         this.scope = scope;
19965         if(xy && this.animRepair !== false){
19966             this.el.addClass("x-dd-drag-repair");
19967             this.el.hideUnders(true);
19968             this.anim = this.el.shift({
19969                 duration: this.repairDuration || .5,
19970                 easing: 'easeOut',
19971                 xy: xy,
19972                 stopFx: true,
19973                 callback: this.afterRepair,
19974                 scope: this
19975             });
19976         }else{
19977             this.afterRepair();
19978         }
19979     },
19980
19981     // private
19982     afterRepair : function(){
19983         this.hide(true);
19984         if(typeof this.callback == "function"){
19985             this.callback.call(this.scope || this);
19986         }
19987         this.callback = null;
19988         this.scope = null;
19989     }
19990 };/*
19991  * Based on:
19992  * Ext JS Library 1.1.1
19993  * Copyright(c) 2006-2007, Ext JS, LLC.
19994  *
19995  * Originally Released Under LGPL - original licence link has changed is not relivant.
19996  *
19997  * Fork - LGPL
19998  * <script type="text/javascript">
19999  */
20000
20001 /**
20002  * @class Roo.dd.DragSource
20003  * @extends Roo.dd.DDProxy
20004  * A simple class that provides the basic implementation needed to make any element draggable.
20005  * @constructor
20006  * @param {String/HTMLElement/Element} el The container element
20007  * @param {Object} config
20008  */
20009 Roo.dd.DragSource = function(el, config){
20010     this.el = Roo.get(el);
20011     this.dragData = {};
20012     
20013     Roo.apply(this, config);
20014     
20015     if(!this.proxy){
20016         this.proxy = new Roo.dd.StatusProxy();
20017     }
20018
20019     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
20020           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
20021     
20022     this.dragging = false;
20023 };
20024
20025 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
20026     /**
20027      * @cfg {String} dropAllowed
20028      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20029      */
20030     dropAllowed : "x-dd-drop-ok",
20031     /**
20032      * @cfg {String} dropNotAllowed
20033      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20034      */
20035     dropNotAllowed : "x-dd-drop-nodrop",
20036
20037     /**
20038      * Returns the data object associated with this drag source
20039      * @return {Object} data An object containing arbitrary data
20040      */
20041     getDragData : function(e){
20042         return this.dragData;
20043     },
20044
20045     // private
20046     onDragEnter : function(e, id){
20047         var target = Roo.dd.DragDropMgr.getDDById(id);
20048         this.cachedTarget = target;
20049         if(this.beforeDragEnter(target, e, id) !== false){
20050             if(target.isNotifyTarget){
20051                 var status = target.notifyEnter(this, e, this.dragData);
20052                 this.proxy.setStatus(status);
20053             }else{
20054                 this.proxy.setStatus(this.dropAllowed);
20055             }
20056             
20057             if(this.afterDragEnter){
20058                 /**
20059                  * An empty function by default, but provided so that you can perform a custom action
20060                  * when the dragged item enters the drop target by providing an implementation.
20061                  * @param {Roo.dd.DragDrop} target The drop target
20062                  * @param {Event} e The event object
20063                  * @param {String} id The id of the dragged element
20064                  * @method afterDragEnter
20065                  */
20066                 this.afterDragEnter(target, e, id);
20067             }
20068         }
20069     },
20070
20071     /**
20072      * An empty function by default, but provided so that you can perform a custom action
20073      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
20074      * @param {Roo.dd.DragDrop} target The drop target
20075      * @param {Event} e The event object
20076      * @param {String} id The id of the dragged element
20077      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20078      */
20079     beforeDragEnter : function(target, e, id){
20080         return true;
20081     },
20082
20083     // private
20084     alignElWithMouse: function() {
20085         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
20086         this.proxy.sync();
20087     },
20088
20089     // private
20090     onDragOver : function(e, id){
20091         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20092         if(this.beforeDragOver(target, e, id) !== false){
20093             if(target.isNotifyTarget){
20094                 var status = target.notifyOver(this, e, this.dragData);
20095                 this.proxy.setStatus(status);
20096             }
20097
20098             if(this.afterDragOver){
20099                 /**
20100                  * An empty function by default, but provided so that you can perform a custom action
20101                  * while the dragged item is over the drop target by providing an implementation.
20102                  * @param {Roo.dd.DragDrop} target The drop target
20103                  * @param {Event} e The event object
20104                  * @param {String} id The id of the dragged element
20105                  * @method afterDragOver
20106                  */
20107                 this.afterDragOver(target, e, id);
20108             }
20109         }
20110     },
20111
20112     /**
20113      * An empty function by default, but provided so that you can perform a custom action
20114      * while the dragged item is over the drop target and optionally cancel the onDragOver.
20115      * @param {Roo.dd.DragDrop} target The drop target
20116      * @param {Event} e The event object
20117      * @param {String} id The id of the dragged element
20118      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20119      */
20120     beforeDragOver : function(target, e, id){
20121         return true;
20122     },
20123
20124     // private
20125     onDragOut : function(e, id){
20126         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20127         if(this.beforeDragOut(target, e, id) !== false){
20128             if(target.isNotifyTarget){
20129                 target.notifyOut(this, e, this.dragData);
20130             }
20131             this.proxy.reset();
20132             if(this.afterDragOut){
20133                 /**
20134                  * An empty function by default, but provided so that you can perform a custom action
20135                  * after the dragged item is dragged out of the target without dropping.
20136                  * @param {Roo.dd.DragDrop} target The drop target
20137                  * @param {Event} e The event object
20138                  * @param {String} id The id of the dragged element
20139                  * @method afterDragOut
20140                  */
20141                 this.afterDragOut(target, e, id);
20142             }
20143         }
20144         this.cachedTarget = null;
20145     },
20146
20147     /**
20148      * An empty function by default, but provided so that you can perform a custom action before the dragged
20149      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20150      * @param {Roo.dd.DragDrop} target The drop target
20151      * @param {Event} e The event object
20152      * @param {String} id The id of the dragged element
20153      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20154      */
20155     beforeDragOut : function(target, e, id){
20156         return true;
20157     },
20158     
20159     // private
20160     onDragDrop : function(e, id){
20161         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20162         if(this.beforeDragDrop(target, e, id) !== false){
20163             if(target.isNotifyTarget){
20164                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20165                     this.onValidDrop(target, e, id);
20166                 }else{
20167                     this.onInvalidDrop(target, e, id);
20168                 }
20169             }else{
20170                 this.onValidDrop(target, e, id);
20171             }
20172             
20173             if(this.afterDragDrop){
20174                 /**
20175                  * An empty function by default, but provided so that you can perform a custom action
20176                  * after a valid drag drop has occurred by providing an implementation.
20177                  * @param {Roo.dd.DragDrop} target The drop target
20178                  * @param {Event} e The event object
20179                  * @param {String} id The id of the dropped element
20180                  * @method afterDragDrop
20181                  */
20182                 this.afterDragDrop(target, e, id);
20183             }
20184         }
20185         delete this.cachedTarget;
20186     },
20187
20188     /**
20189      * An empty function by default, but provided so that you can perform a custom action before the dragged
20190      * item is dropped onto the target and optionally cancel the onDragDrop.
20191      * @param {Roo.dd.DragDrop} target The drop target
20192      * @param {Event} e The event object
20193      * @param {String} id The id of the dragged element
20194      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20195      */
20196     beforeDragDrop : function(target, e, id){
20197         return true;
20198     },
20199
20200     // private
20201     onValidDrop : function(target, e, id){
20202         this.hideProxy();
20203         if(this.afterValidDrop){
20204             /**
20205              * An empty function by default, but provided so that you can perform a custom action
20206              * after a valid drop has occurred by providing an implementation.
20207              * @param {Object} target The target DD 
20208              * @param {Event} e The event object
20209              * @param {String} id The id of the dropped element
20210              * @method afterInvalidDrop
20211              */
20212             this.afterValidDrop(target, e, id);
20213         }
20214     },
20215
20216     // private
20217     getRepairXY : function(e, data){
20218         return this.el.getXY();  
20219     },
20220
20221     // private
20222     onInvalidDrop : function(target, e, id){
20223         this.beforeInvalidDrop(target, e, id);
20224         if(this.cachedTarget){
20225             if(this.cachedTarget.isNotifyTarget){
20226                 this.cachedTarget.notifyOut(this, e, this.dragData);
20227             }
20228             this.cacheTarget = null;
20229         }
20230         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20231
20232         if(this.afterInvalidDrop){
20233             /**
20234              * An empty function by default, but provided so that you can perform a custom action
20235              * after an invalid drop has occurred by providing an implementation.
20236              * @param {Event} e The event object
20237              * @param {String} id The id of the dropped element
20238              * @method afterInvalidDrop
20239              */
20240             this.afterInvalidDrop(e, id);
20241         }
20242     },
20243
20244     // private
20245     afterRepair : function(){
20246         if(Roo.enableFx){
20247             this.el.highlight(this.hlColor || "c3daf9");
20248         }
20249         this.dragging = false;
20250     },
20251
20252     /**
20253      * An empty function by default, but provided so that you can perform a custom action after an invalid
20254      * drop has occurred.
20255      * @param {Roo.dd.DragDrop} target The drop target
20256      * @param {Event} e The event object
20257      * @param {String} id The id of the dragged element
20258      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20259      */
20260     beforeInvalidDrop : function(target, e, id){
20261         return true;
20262     },
20263
20264     // private
20265     handleMouseDown : function(e){
20266         if(this.dragging) {
20267             return;
20268         }
20269         var data = this.getDragData(e);
20270         if(data && this.onBeforeDrag(data, e) !== false){
20271             this.dragData = data;
20272             this.proxy.stop();
20273             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20274         } 
20275     },
20276
20277     /**
20278      * An empty function by default, but provided so that you can perform a custom action before the initial
20279      * drag event begins and optionally cancel it.
20280      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20281      * @param {Event} e The event object
20282      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20283      */
20284     onBeforeDrag : function(data, e){
20285         return true;
20286     },
20287
20288     /**
20289      * An empty function by default, but provided so that you can perform a custom action once the initial
20290      * drag event has begun.  The drag cannot be canceled from this function.
20291      * @param {Number} x The x position of the click on the dragged object
20292      * @param {Number} y The y position of the click on the dragged object
20293      */
20294     onStartDrag : Roo.emptyFn,
20295
20296     // private - YUI override
20297     startDrag : function(x, y){
20298         this.proxy.reset();
20299         this.dragging = true;
20300         this.proxy.update("");
20301         this.onInitDrag(x, y);
20302         this.proxy.show();
20303     },
20304
20305     // private
20306     onInitDrag : function(x, y){
20307         var clone = this.el.dom.cloneNode(true);
20308         clone.id = Roo.id(); // prevent duplicate ids
20309         this.proxy.update(clone);
20310         this.onStartDrag(x, y);
20311         return true;
20312     },
20313
20314     /**
20315      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20316      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20317      */
20318     getProxy : function(){
20319         return this.proxy;  
20320     },
20321
20322     /**
20323      * Hides the drag source's {@link Roo.dd.StatusProxy}
20324      */
20325     hideProxy : function(){
20326         this.proxy.hide();  
20327         this.proxy.reset(true);
20328         this.dragging = false;
20329     },
20330
20331     // private
20332     triggerCacheRefresh : function(){
20333         Roo.dd.DDM.refreshCache(this.groups);
20334     },
20335
20336     // private - override to prevent hiding
20337     b4EndDrag: function(e) {
20338     },
20339
20340     // private - override to prevent moving
20341     endDrag : function(e){
20342         this.onEndDrag(this.dragData, e);
20343     },
20344
20345     // private
20346     onEndDrag : function(data, e){
20347     },
20348     
20349     // private - pin to cursor
20350     autoOffset : function(x, y) {
20351         this.setDelta(-12, -20);
20352     }    
20353 });/*
20354  * Based on:
20355  * Ext JS Library 1.1.1
20356  * Copyright(c) 2006-2007, Ext JS, LLC.
20357  *
20358  * Originally Released Under LGPL - original licence link has changed is not relivant.
20359  *
20360  * Fork - LGPL
20361  * <script type="text/javascript">
20362  */
20363
20364
20365 /**
20366  * @class Roo.dd.DropTarget
20367  * @extends Roo.dd.DDTarget
20368  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20369  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20370  * @constructor
20371  * @param {String/HTMLElement/Element} el The container element
20372  * @param {Object} config
20373  */
20374 Roo.dd.DropTarget = function(el, config){
20375     this.el = Roo.get(el);
20376     
20377     var listeners = false; ;
20378     if (config && config.listeners) {
20379         listeners= config.listeners;
20380         delete config.listeners;
20381     }
20382     Roo.apply(this, config);
20383     
20384     if(this.containerScroll){
20385         Roo.dd.ScrollManager.register(this.el);
20386     }
20387     this.addEvents( {
20388          /**
20389          * @scope Roo.dd.DropTarget
20390          */
20391          
20392          /**
20393          * @event enter
20394          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20395          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20396          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20397          * 
20398          * IMPORTANT : it should set this.overClass and this.dropAllowed
20399          * 
20400          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20401          * @param {Event} e The event
20402          * @param {Object} data An object containing arbitrary data supplied by the drag source
20403          */
20404         "enter" : true,
20405         
20406          /**
20407          * @event over
20408          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20409          * This method will be called on every mouse movement while the drag source is over the drop target.
20410          * This default implementation simply returns the dropAllowed config value.
20411          * 
20412          * IMPORTANT : it should set this.dropAllowed
20413          * 
20414          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20415          * @param {Event} e The event
20416          * @param {Object} data An object containing arbitrary data supplied by the drag source
20417          
20418          */
20419         "over" : true,
20420         /**
20421          * @event out
20422          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20423          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20424          * overClass (if any) from the drop element.
20425          * 
20426          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20427          * @param {Event} e The event
20428          * @param {Object} data An object containing arbitrary data supplied by the drag source
20429          */
20430          "out" : true,
20431          
20432         /**
20433          * @event drop
20434          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20435          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20436          * implementation that does something to process the drop event and returns true so that the drag source's
20437          * repair action does not run.
20438          * 
20439          * IMPORTANT : it should set this.success
20440          * 
20441          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20442          * @param {Event} e The event
20443          * @param {Object} data An object containing arbitrary data supplied by the drag source
20444         */
20445          "drop" : true
20446     });
20447             
20448      
20449     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20450         this.el.dom, 
20451         this.ddGroup || this.group,
20452         {
20453             isTarget: true,
20454             listeners : listeners || {} 
20455            
20456         
20457         }
20458     );
20459
20460 };
20461
20462 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20463     /**
20464      * @cfg {String} overClass
20465      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20466      */
20467      /**
20468      * @cfg {String} ddGroup
20469      * The drag drop group to handle drop events for
20470      */
20471      
20472     /**
20473      * @cfg {String} dropAllowed
20474      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20475      */
20476     dropAllowed : "x-dd-drop-ok",
20477     /**
20478      * @cfg {String} dropNotAllowed
20479      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20480      */
20481     dropNotAllowed : "x-dd-drop-nodrop",
20482     /**
20483      * @cfg {boolean} success
20484      * set this after drop listener.. 
20485      */
20486     success : false,
20487     /**
20488      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20489      * if the drop point is valid for over/enter..
20490      */
20491     valid : false,
20492     // private
20493     isTarget : true,
20494
20495     // private
20496     isNotifyTarget : true,
20497     
20498     /**
20499      * @hide
20500      */
20501     notifyEnter : function(dd, e, data)
20502     {
20503         this.valid = true;
20504         this.fireEvent('enter', dd, e, data);
20505         if(this.overClass){
20506             this.el.addClass(this.overClass);
20507         }
20508         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20509             this.valid ? this.dropAllowed : this.dropNotAllowed
20510         );
20511     },
20512
20513     /**
20514      * @hide
20515      */
20516     notifyOver : function(dd, e, data)
20517     {
20518         this.valid = true;
20519         this.fireEvent('over', dd, e, data);
20520         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20521             this.valid ? this.dropAllowed : this.dropNotAllowed
20522         );
20523     },
20524
20525     /**
20526      * @hide
20527      */
20528     notifyOut : function(dd, e, data)
20529     {
20530         this.fireEvent('out', dd, e, data);
20531         if(this.overClass){
20532             this.el.removeClass(this.overClass);
20533         }
20534     },
20535
20536     /**
20537      * @hide
20538      */
20539     notifyDrop : function(dd, e, data)
20540     {
20541         this.success = false;
20542         this.fireEvent('drop', dd, e, data);
20543         return this.success;
20544     }
20545 });/*
20546  * Based on:
20547  * Ext JS Library 1.1.1
20548  * Copyright(c) 2006-2007, Ext JS, LLC.
20549  *
20550  * Originally Released Under LGPL - original licence link has changed is not relivant.
20551  *
20552  * Fork - LGPL
20553  * <script type="text/javascript">
20554  */
20555
20556
20557 /**
20558  * @class Roo.dd.DragZone
20559  * @extends Roo.dd.DragSource
20560  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20561  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20562  * @constructor
20563  * @param {String/HTMLElement/Element} el The container element
20564  * @param {Object} config
20565  */
20566 Roo.dd.DragZone = function(el, config){
20567     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20568     if(this.containerScroll){
20569         Roo.dd.ScrollManager.register(this.el);
20570     }
20571 };
20572
20573 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20574     /**
20575      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20576      * for auto scrolling during drag operations.
20577      */
20578     /**
20579      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20580      * method after a failed drop (defaults to "c3daf9" - light blue)
20581      */
20582
20583     /**
20584      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20585      * for a valid target to drag based on the mouse down. Override this method
20586      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20587      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20588      * @param {EventObject} e The mouse down event
20589      * @return {Object} The dragData
20590      */
20591     getDragData : function(e){
20592         return Roo.dd.Registry.getHandleFromEvent(e);
20593     },
20594     
20595     /**
20596      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20597      * this.dragData.ddel
20598      * @param {Number} x The x position of the click on the dragged object
20599      * @param {Number} y The y position of the click on the dragged object
20600      * @return {Boolean} true to continue the drag, false to cancel
20601      */
20602     onInitDrag : function(x, y){
20603         this.proxy.update(this.dragData.ddel.cloneNode(true));
20604         this.onStartDrag(x, y);
20605         return true;
20606     },
20607     
20608     /**
20609      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20610      */
20611     afterRepair : function(){
20612         if(Roo.enableFx){
20613             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20614         }
20615         this.dragging = false;
20616     },
20617
20618     /**
20619      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20620      * the XY of this.dragData.ddel
20621      * @param {EventObject} e The mouse up event
20622      * @return {Array} The xy location (e.g. [100, 200])
20623      */
20624     getRepairXY : function(e){
20625         return Roo.Element.fly(this.dragData.ddel).getXY();  
20626     }
20627 });/*
20628  * Based on:
20629  * Ext JS Library 1.1.1
20630  * Copyright(c) 2006-2007, Ext JS, LLC.
20631  *
20632  * Originally Released Under LGPL - original licence link has changed is not relivant.
20633  *
20634  * Fork - LGPL
20635  * <script type="text/javascript">
20636  */
20637 /**
20638  * @class Roo.dd.DropZone
20639  * @extends Roo.dd.DropTarget
20640  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20641  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20642  * @constructor
20643  * @param {String/HTMLElement/Element} el The container element
20644  * @param {Object} config
20645  */
20646 Roo.dd.DropZone = function(el, config){
20647     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20648 };
20649
20650 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20651     /**
20652      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20653      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20654      * provide your own custom lookup.
20655      * @param {Event} e The event
20656      * @return {Object} data The custom data
20657      */
20658     getTargetFromEvent : function(e){
20659         return Roo.dd.Registry.getTargetFromEvent(e);
20660     },
20661
20662     /**
20663      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20664      * that it has registered.  This method has no default implementation and should be overridden to provide
20665      * node-specific processing if necessary.
20666      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20667      * {@link #getTargetFromEvent} for this node)
20668      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20669      * @param {Event} e The event
20670      * @param {Object} data An object containing arbitrary data supplied by the drag source
20671      */
20672     onNodeEnter : function(n, dd, e, data){
20673         
20674     },
20675
20676     /**
20677      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20678      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20679      * overridden to provide the proper feedback.
20680      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20681      * {@link #getTargetFromEvent} for this node)
20682      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20683      * @param {Event} e The event
20684      * @param {Object} data An object containing arbitrary data supplied by the drag source
20685      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20686      * underlying {@link Roo.dd.StatusProxy} can be updated
20687      */
20688     onNodeOver : function(n, dd, e, data){
20689         return this.dropAllowed;
20690     },
20691
20692     /**
20693      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20694      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20695      * node-specific processing if necessary.
20696      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20697      * {@link #getTargetFromEvent} for this node)
20698      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20699      * @param {Event} e The event
20700      * @param {Object} data An object containing arbitrary data supplied by the drag source
20701      */
20702     onNodeOut : function(n, dd, e, data){
20703         
20704     },
20705
20706     /**
20707      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20708      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20709      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20710      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20711      * {@link #getTargetFromEvent} for this node)
20712      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20713      * @param {Event} e The event
20714      * @param {Object} data An object containing arbitrary data supplied by the drag source
20715      * @return {Boolean} True if the drop was valid, else false
20716      */
20717     onNodeDrop : function(n, dd, e, data){
20718         return false;
20719     },
20720
20721     /**
20722      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20723      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20724      * it should be overridden to provide the proper feedback if necessary.
20725      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20726      * @param {Event} e The event
20727      * @param {Object} data An object containing arbitrary data supplied by the drag source
20728      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20729      * underlying {@link Roo.dd.StatusProxy} can be updated
20730      */
20731     onContainerOver : function(dd, e, data){
20732         return this.dropNotAllowed;
20733     },
20734
20735     /**
20736      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20737      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20738      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20739      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20740      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20741      * @param {Event} e The event
20742      * @param {Object} data An object containing arbitrary data supplied by the drag source
20743      * @return {Boolean} True if the drop was valid, else false
20744      */
20745     onContainerDrop : function(dd, e, data){
20746         return false;
20747     },
20748
20749     /**
20750      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20751      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20752      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20753      * you should override this method and provide a custom implementation.
20754      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20755      * @param {Event} e The event
20756      * @param {Object} data An object containing arbitrary data supplied by the drag source
20757      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20758      * underlying {@link Roo.dd.StatusProxy} can be updated
20759      */
20760     notifyEnter : function(dd, e, data){
20761         return this.dropNotAllowed;
20762     },
20763
20764     /**
20765      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20766      * This method will be called on every mouse movement while the drag source is over the drop zone.
20767      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20768      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20769      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20770      * registered node, it will call {@link #onContainerOver}.
20771      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20772      * @param {Event} e The event
20773      * @param {Object} data An object containing arbitrary data supplied by the drag source
20774      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20775      * underlying {@link Roo.dd.StatusProxy} can be updated
20776      */
20777     notifyOver : function(dd, e, data){
20778         var n = this.getTargetFromEvent(e);
20779         if(!n){ // not over valid drop target
20780             if(this.lastOverNode){
20781                 this.onNodeOut(this.lastOverNode, dd, e, data);
20782                 this.lastOverNode = null;
20783             }
20784             return this.onContainerOver(dd, e, data);
20785         }
20786         if(this.lastOverNode != n){
20787             if(this.lastOverNode){
20788                 this.onNodeOut(this.lastOverNode, dd, e, data);
20789             }
20790             this.onNodeEnter(n, dd, e, data);
20791             this.lastOverNode = n;
20792         }
20793         return this.onNodeOver(n, dd, e, data);
20794     },
20795
20796     /**
20797      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20798      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20799      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20800      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20801      * @param {Event} e The event
20802      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20803      */
20804     notifyOut : function(dd, e, data){
20805         if(this.lastOverNode){
20806             this.onNodeOut(this.lastOverNode, dd, e, data);
20807             this.lastOverNode = null;
20808         }
20809     },
20810
20811     /**
20812      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20813      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20814      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20815      * otherwise it will call {@link #onContainerDrop}.
20816      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20817      * @param {Event} e The event
20818      * @param {Object} data An object containing arbitrary data supplied by the drag source
20819      * @return {Boolean} True if the drop was valid, else false
20820      */
20821     notifyDrop : function(dd, e, data){
20822         if(this.lastOverNode){
20823             this.onNodeOut(this.lastOverNode, dd, e, data);
20824             this.lastOverNode = null;
20825         }
20826         var n = this.getTargetFromEvent(e);
20827         return n ?
20828             this.onNodeDrop(n, dd, e, data) :
20829             this.onContainerDrop(dd, e, data);
20830     },
20831
20832     // private
20833     triggerCacheRefresh : function(){
20834         Roo.dd.DDM.refreshCache(this.groups);
20835     }  
20836 });/*
20837  * Based on:
20838  * Ext JS Library 1.1.1
20839  * Copyright(c) 2006-2007, Ext JS, LLC.
20840  *
20841  * Originally Released Under LGPL - original licence link has changed is not relivant.
20842  *
20843  * Fork - LGPL
20844  * <script type="text/javascript">
20845  */
20846
20847
20848 /**
20849  * @class Roo.data.SortTypes
20850  * @singleton
20851  * Defines the default sorting (casting?) comparison functions used when sorting data.
20852  */
20853 Roo.data.SortTypes = {
20854     /**
20855      * Default sort that does nothing
20856      * @param {Mixed} s The value being converted
20857      * @return {Mixed} The comparison value
20858      */
20859     none : function(s){
20860         return s;
20861     },
20862     
20863     /**
20864      * The regular expression used to strip tags
20865      * @type {RegExp}
20866      * @property
20867      */
20868     stripTagsRE : /<\/?[^>]+>/gi,
20869     
20870     /**
20871      * Strips all HTML tags to sort on text only
20872      * @param {Mixed} s The value being converted
20873      * @return {String} The comparison value
20874      */
20875     asText : function(s){
20876         return String(s).replace(this.stripTagsRE, "");
20877     },
20878     
20879     /**
20880      * Strips all HTML tags to sort on text only - Case insensitive
20881      * @param {Mixed} s The value being converted
20882      * @return {String} The comparison value
20883      */
20884     asUCText : function(s){
20885         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20886     },
20887     
20888     /**
20889      * Case insensitive string
20890      * @param {Mixed} s The value being converted
20891      * @return {String} The comparison value
20892      */
20893     asUCString : function(s) {
20894         return String(s).toUpperCase();
20895     },
20896     
20897     /**
20898      * Date sorting
20899      * @param {Mixed} s The value being converted
20900      * @return {Number} The comparison value
20901      */
20902     asDate : function(s) {
20903         if(!s){
20904             return 0;
20905         }
20906         if(s instanceof Date){
20907             return s.getTime();
20908         }
20909         return Date.parse(String(s));
20910     },
20911     
20912     /**
20913      * Float sorting
20914      * @param {Mixed} s The value being converted
20915      * @return {Float} The comparison value
20916      */
20917     asFloat : function(s) {
20918         var val = parseFloat(String(s).replace(/,/g, ""));
20919         if(isNaN(val)) {
20920             val = 0;
20921         }
20922         return val;
20923     },
20924     
20925     /**
20926      * Integer sorting
20927      * @param {Mixed} s The value being converted
20928      * @return {Number} The comparison value
20929      */
20930     asInt : function(s) {
20931         var val = parseInt(String(s).replace(/,/g, ""));
20932         if(isNaN(val)) {
20933             val = 0;
20934         }
20935         return val;
20936     }
20937 };/*
20938  * Based on:
20939  * Ext JS Library 1.1.1
20940  * Copyright(c) 2006-2007, Ext JS, LLC.
20941  *
20942  * Originally Released Under LGPL - original licence link has changed is not relivant.
20943  *
20944  * Fork - LGPL
20945  * <script type="text/javascript">
20946  */
20947
20948 /**
20949 * @class Roo.data.Record
20950  * Instances of this class encapsulate both record <em>definition</em> information, and record
20951  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20952  * to access Records cached in an {@link Roo.data.Store} object.<br>
20953  * <p>
20954  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20955  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20956  * objects.<br>
20957  * <p>
20958  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20959  * @constructor
20960  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20961  * {@link #create}. The parameters are the same.
20962  * @param {Array} data An associative Array of data values keyed by the field name.
20963  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20964  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20965  * not specified an integer id is generated.
20966  */
20967 Roo.data.Record = function(data, id){
20968     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20969     this.data = data;
20970 };
20971
20972 /**
20973  * Generate a constructor for a specific record layout.
20974  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20975  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20976  * Each field definition object may contain the following properties: <ul>
20977  * <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,
20978  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20979  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20980  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20981  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20982  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20983  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20984  * this may be omitted.</p></li>
20985  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20986  * <ul><li>auto (Default, implies no conversion)</li>
20987  * <li>string</li>
20988  * <li>int</li>
20989  * <li>float</li>
20990  * <li>boolean</li>
20991  * <li>date</li></ul></p></li>
20992  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20993  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20994  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20995  * by the Reader into an object that will be stored in the Record. It is passed the
20996  * following parameters:<ul>
20997  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20998  * </ul></p></li>
20999  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
21000  * </ul>
21001  * <br>usage:<br><pre><code>
21002 var TopicRecord = Roo.data.Record.create(
21003     {name: 'title', mapping: 'topic_title'},
21004     {name: 'author', mapping: 'username'},
21005     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
21006     {name: 'lastPost', mapping: 'post_time', type: 'date'},
21007     {name: 'lastPoster', mapping: 'user2'},
21008     {name: 'excerpt', mapping: 'post_text'}
21009 );
21010
21011 var myNewRecord = new TopicRecord({
21012     title: 'Do my job please',
21013     author: 'noobie',
21014     totalPosts: 1,
21015     lastPost: new Date(),
21016     lastPoster: 'Animal',
21017     excerpt: 'No way dude!'
21018 });
21019 myStore.add(myNewRecord);
21020 </code></pre>
21021  * @method create
21022  * @static
21023  */
21024 Roo.data.Record.create = function(o){
21025     var f = function(){
21026         f.superclass.constructor.apply(this, arguments);
21027     };
21028     Roo.extend(f, Roo.data.Record);
21029     var p = f.prototype;
21030     p.fields = new Roo.util.MixedCollection(false, function(field){
21031         return field.name;
21032     });
21033     for(var i = 0, len = o.length; i < len; i++){
21034         p.fields.add(new Roo.data.Field(o[i]));
21035     }
21036     f.getField = function(name){
21037         return p.fields.get(name);  
21038     };
21039     return f;
21040 };
21041
21042 Roo.data.Record.AUTO_ID = 1000;
21043 Roo.data.Record.EDIT = 'edit';
21044 Roo.data.Record.REJECT = 'reject';
21045 Roo.data.Record.COMMIT = 'commit';
21046
21047 Roo.data.Record.prototype = {
21048     /**
21049      * Readonly flag - true if this record has been modified.
21050      * @type Boolean
21051      */
21052     dirty : false,
21053     editing : false,
21054     error: null,
21055     modified: null,
21056
21057     // private
21058     join : function(store){
21059         this.store = store;
21060     },
21061
21062     /**
21063      * Set the named field to the specified value.
21064      * @param {String} name The name of the field to set.
21065      * @param {Object} value The value to set the field to.
21066      */
21067     set : function(name, value){
21068         if(this.data[name] == value){
21069             return;
21070         }
21071         this.dirty = true;
21072         if(!this.modified){
21073             this.modified = {};
21074         }
21075         if(typeof this.modified[name] == 'undefined'){
21076             this.modified[name] = this.data[name];
21077         }
21078         this.data[name] = value;
21079         if(!this.editing && this.store){
21080             this.store.afterEdit(this);
21081         }       
21082     },
21083
21084     /**
21085      * Get the value of the named field.
21086      * @param {String} name The name of the field to get the value of.
21087      * @return {Object} The value of the field.
21088      */
21089     get : function(name){
21090         return this.data[name]; 
21091     },
21092
21093     // private
21094     beginEdit : function(){
21095         this.editing = true;
21096         this.modified = {}; 
21097     },
21098
21099     // private
21100     cancelEdit : function(){
21101         this.editing = false;
21102         delete this.modified;
21103     },
21104
21105     // private
21106     endEdit : function(){
21107         this.editing = false;
21108         if(this.dirty && this.store){
21109             this.store.afterEdit(this);
21110         }
21111     },
21112
21113     /**
21114      * Usually called by the {@link Roo.data.Store} which owns the Record.
21115      * Rejects all changes made to the Record since either creation, or the last commit operation.
21116      * Modified fields are reverted to their original values.
21117      * <p>
21118      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21119      * of reject operations.
21120      */
21121     reject : function(){
21122         var m = this.modified;
21123         for(var n in m){
21124             if(typeof m[n] != "function"){
21125                 this.data[n] = m[n];
21126             }
21127         }
21128         this.dirty = false;
21129         delete this.modified;
21130         this.editing = false;
21131         if(this.store){
21132             this.store.afterReject(this);
21133         }
21134     },
21135
21136     /**
21137      * Usually called by the {@link Roo.data.Store} which owns the Record.
21138      * Commits all changes made to the Record since either creation, or the last commit operation.
21139      * <p>
21140      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21141      * of commit operations.
21142      */
21143     commit : function(){
21144         this.dirty = false;
21145         delete this.modified;
21146         this.editing = false;
21147         if(this.store){
21148             this.store.afterCommit(this);
21149         }
21150     },
21151
21152     // private
21153     hasError : function(){
21154         return this.error != null;
21155     },
21156
21157     // private
21158     clearError : function(){
21159         this.error = null;
21160     },
21161
21162     /**
21163      * Creates a copy of this record.
21164      * @param {String} id (optional) A new record id if you don't want to use this record's id
21165      * @return {Record}
21166      */
21167     copy : function(newId) {
21168         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21169     }
21170 };/*
21171  * Based on:
21172  * Ext JS Library 1.1.1
21173  * Copyright(c) 2006-2007, Ext JS, LLC.
21174  *
21175  * Originally Released Under LGPL - original licence link has changed is not relivant.
21176  *
21177  * Fork - LGPL
21178  * <script type="text/javascript">
21179  */
21180
21181
21182
21183 /**
21184  * @class Roo.data.Store
21185  * @extends Roo.util.Observable
21186  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21187  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21188  * <p>
21189  * 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
21190  * has no knowledge of the format of the data returned by the Proxy.<br>
21191  * <p>
21192  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21193  * instances from the data object. These records are cached and made available through accessor functions.
21194  * @constructor
21195  * Creates a new Store.
21196  * @param {Object} config A config object containing the objects needed for the Store to access data,
21197  * and read the data into Records.
21198  */
21199 Roo.data.Store = function(config){
21200     this.data = new Roo.util.MixedCollection(false);
21201     this.data.getKey = function(o){
21202         return o.id;
21203     };
21204     this.baseParams = {};
21205     // private
21206     this.paramNames = {
21207         "start" : "start",
21208         "limit" : "limit",
21209         "sort" : "sort",
21210         "dir" : "dir",
21211         "multisort" : "_multisort"
21212     };
21213
21214     if(config && config.data){
21215         this.inlineData = config.data;
21216         delete config.data;
21217     }
21218
21219     Roo.apply(this, config);
21220     
21221     if(this.reader){ // reader passed
21222         this.reader = Roo.factory(this.reader, Roo.data);
21223         this.reader.xmodule = this.xmodule || false;
21224         if(!this.recordType){
21225             this.recordType = this.reader.recordType;
21226         }
21227         if(this.reader.onMetaChange){
21228             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21229         }
21230     }
21231
21232     if(this.recordType){
21233         this.fields = this.recordType.prototype.fields;
21234     }
21235     this.modified = [];
21236
21237     this.addEvents({
21238         /**
21239          * @event datachanged
21240          * Fires when the data cache has changed, and a widget which is using this Store
21241          * as a Record cache should refresh its view.
21242          * @param {Store} this
21243          */
21244         datachanged : true,
21245         /**
21246          * @event metachange
21247          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21248          * @param {Store} this
21249          * @param {Object} meta The JSON metadata
21250          */
21251         metachange : true,
21252         /**
21253          * @event add
21254          * Fires when Records have been added to the Store
21255          * @param {Store} this
21256          * @param {Roo.data.Record[]} records The array of Records added
21257          * @param {Number} index The index at which the record(s) were added
21258          */
21259         add : true,
21260         /**
21261          * @event remove
21262          * Fires when a Record has been removed from the Store
21263          * @param {Store} this
21264          * @param {Roo.data.Record} record The Record that was removed
21265          * @param {Number} index The index at which the record was removed
21266          */
21267         remove : true,
21268         /**
21269          * @event update
21270          * Fires when a Record has been updated
21271          * @param {Store} this
21272          * @param {Roo.data.Record} record The Record that was updated
21273          * @param {String} operation The update operation being performed.  Value may be one of:
21274          * <pre><code>
21275  Roo.data.Record.EDIT
21276  Roo.data.Record.REJECT
21277  Roo.data.Record.COMMIT
21278          * </code></pre>
21279          */
21280         update : true,
21281         /**
21282          * @event clear
21283          * Fires when the data cache has been cleared.
21284          * @param {Store} this
21285          */
21286         clear : true,
21287         /**
21288          * @event beforeload
21289          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21290          * the load action will be canceled.
21291          * @param {Store} this
21292          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21293          */
21294         beforeload : true,
21295         /**
21296          * @event beforeloadadd
21297          * Fires after a new set of Records has been loaded.
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          */
21302         beforeloadadd : true,
21303         /**
21304          * @event load
21305          * Fires after a new set of Records has been loaded, before they are added to the store.
21306          * @param {Store} this
21307          * @param {Roo.data.Record[]} records The Records that were loaded
21308          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21309          * @params {Object} return from reader
21310          */
21311         load : true,
21312         /**
21313          * @event loadexception
21314          * Fires if an exception occurs in the Proxy during loading.
21315          * Called with the signature of the Proxy's "loadexception" event.
21316          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21317          * 
21318          * @param {Proxy} 
21319          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21320          * @param {Object} load options 
21321          * @param {Object} jsonData from your request (normally this contains the Exception)
21322          */
21323         loadexception : true
21324     });
21325     
21326     if(this.proxy){
21327         this.proxy = Roo.factory(this.proxy, Roo.data);
21328         this.proxy.xmodule = this.xmodule || false;
21329         this.relayEvents(this.proxy,  ["loadexception"]);
21330     }
21331     this.sortToggle = {};
21332     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21333
21334     Roo.data.Store.superclass.constructor.call(this);
21335
21336     if(this.inlineData){
21337         this.loadData(this.inlineData);
21338         delete this.inlineData;
21339     }
21340 };
21341
21342 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21343      /**
21344     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21345     * without a remote query - used by combo/forms at present.
21346     */
21347     
21348     /**
21349     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21350     */
21351     /**
21352     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21353     */
21354     /**
21355     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21356     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21357     */
21358     /**
21359     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21360     * on any HTTP request
21361     */
21362     /**
21363     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21364     */
21365     /**
21366     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21367     */
21368     multiSort: false,
21369     /**
21370     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21371     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21372     */
21373     remoteSort : false,
21374
21375     /**
21376     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21377      * loaded or when a record is removed. (defaults to false).
21378     */
21379     pruneModifiedRecords : false,
21380
21381     // private
21382     lastOptions : null,
21383
21384     /**
21385      * Add Records to the Store and fires the add event.
21386      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21387      */
21388     add : function(records){
21389         records = [].concat(records);
21390         for(var i = 0, len = records.length; i < len; i++){
21391             records[i].join(this);
21392         }
21393         var index = this.data.length;
21394         this.data.addAll(records);
21395         this.fireEvent("add", this, records, index);
21396     },
21397
21398     /**
21399      * Remove a Record from the Store and fires the remove event.
21400      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21401      */
21402     remove : function(record){
21403         var index = this.data.indexOf(record);
21404         this.data.removeAt(index);
21405         if(this.pruneModifiedRecords){
21406             this.modified.remove(record);
21407         }
21408         this.fireEvent("remove", this, record, index);
21409     },
21410
21411     /**
21412      * Remove all Records from the Store and fires the clear event.
21413      */
21414     removeAll : function(){
21415         this.data.clear();
21416         if(this.pruneModifiedRecords){
21417             this.modified = [];
21418         }
21419         this.fireEvent("clear", this);
21420     },
21421
21422     /**
21423      * Inserts Records to the Store at the given index and fires the add event.
21424      * @param {Number} index The start index at which to insert the passed Records.
21425      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21426      */
21427     insert : function(index, records){
21428         records = [].concat(records);
21429         for(var i = 0, len = records.length; i < len; i++){
21430             this.data.insert(index, records[i]);
21431             records[i].join(this);
21432         }
21433         this.fireEvent("add", this, records, index);
21434     },
21435
21436     /**
21437      * Get the index within the cache of the passed Record.
21438      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21439      * @return {Number} The index of the passed Record. Returns -1 if not found.
21440      */
21441     indexOf : function(record){
21442         return this.data.indexOf(record);
21443     },
21444
21445     /**
21446      * Get the index within the cache of the Record with the passed id.
21447      * @param {String} id The id of the Record to find.
21448      * @return {Number} The index of the Record. Returns -1 if not found.
21449      */
21450     indexOfId : function(id){
21451         return this.data.indexOfKey(id);
21452     },
21453
21454     /**
21455      * Get the Record with the specified id.
21456      * @param {String} id The id of the Record to find.
21457      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21458      */
21459     getById : function(id){
21460         return this.data.key(id);
21461     },
21462
21463     /**
21464      * Get the Record at the specified index.
21465      * @param {Number} index The index of the Record to find.
21466      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21467      */
21468     getAt : function(index){
21469         return this.data.itemAt(index);
21470     },
21471
21472     /**
21473      * Returns a range of Records between specified indices.
21474      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21475      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21476      * @return {Roo.data.Record[]} An array of Records
21477      */
21478     getRange : function(start, end){
21479         return this.data.getRange(start, end);
21480     },
21481
21482     // private
21483     storeOptions : function(o){
21484         o = Roo.apply({}, o);
21485         delete o.callback;
21486         delete o.scope;
21487         this.lastOptions = o;
21488     },
21489
21490     /**
21491      * Loads the Record cache from the configured Proxy using the configured Reader.
21492      * <p>
21493      * If using remote paging, then the first load call must specify the <em>start</em>
21494      * and <em>limit</em> properties in the options.params property to establish the initial
21495      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21496      * <p>
21497      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21498      * and this call will return before the new data has been loaded. Perform any post-processing
21499      * in a callback function, or in a "load" event handler.</strong>
21500      * <p>
21501      * @param {Object} options An object containing properties which control loading options:<ul>
21502      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21503      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21504      * passed the following arguments:<ul>
21505      * <li>r : Roo.data.Record[]</li>
21506      * <li>options: Options object from the load call</li>
21507      * <li>success: Boolean success indicator</li></ul></li>
21508      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21509      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21510      * </ul>
21511      */
21512     load : function(options){
21513         options = options || {};
21514         if(this.fireEvent("beforeload", this, options) !== false){
21515             this.storeOptions(options);
21516             var p = Roo.apply(options.params || {}, this.baseParams);
21517             // if meta was not loaded from remote source.. try requesting it.
21518             if (!this.reader.metaFromRemote) {
21519                 p._requestMeta = 1;
21520             }
21521             if(this.sortInfo && this.remoteSort){
21522                 var pn = this.paramNames;
21523                 p[pn["sort"]] = this.sortInfo.field;
21524                 p[pn["dir"]] = this.sortInfo.direction;
21525             }
21526             if (this.multiSort) {
21527                 var pn = this.paramNames;
21528                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21529             }
21530             
21531             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21532         }
21533     },
21534
21535     /**
21536      * Reloads the Record cache from the configured Proxy using the configured Reader and
21537      * the options from the last load operation performed.
21538      * @param {Object} options (optional) An object containing properties which may override the options
21539      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21540      * the most recently used options are reused).
21541      */
21542     reload : function(options){
21543         this.load(Roo.applyIf(options||{}, this.lastOptions));
21544     },
21545
21546     // private
21547     // Called as a callback by the Reader during a load operation.
21548     loadRecords : function(o, options, success){
21549         if(!o || success === false){
21550             if(success !== false){
21551                 this.fireEvent("load", this, [], options, o);
21552             }
21553             if(options.callback){
21554                 options.callback.call(options.scope || this, [], options, false);
21555             }
21556             return;
21557         }
21558         // if data returned failure - throw an exception.
21559         if (o.success === false) {
21560             // show a message if no listener is registered.
21561             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21562                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21563             }
21564             // loadmask wil be hooked into this..
21565             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21566             return;
21567         }
21568         var r = o.records, t = o.totalRecords || r.length;
21569         
21570         this.fireEvent("beforeloadadd", this, r, options, o);
21571         
21572         if(!options || options.add !== true){
21573             if(this.pruneModifiedRecords){
21574                 this.modified = [];
21575             }
21576             for(var i = 0, len = r.length; i < len; i++){
21577                 r[i].join(this);
21578             }
21579             if(this.snapshot){
21580                 this.data = this.snapshot;
21581                 delete this.snapshot;
21582             }
21583             this.data.clear();
21584             this.data.addAll(r);
21585             this.totalLength = t;
21586             this.applySort();
21587             this.fireEvent("datachanged", this);
21588         }else{
21589             this.totalLength = Math.max(t, this.data.length+r.length);
21590             this.add(r);
21591         }
21592         this.fireEvent("load", this, r, options, o);
21593         if(options.callback){
21594             options.callback.call(options.scope || this, r, options, true);
21595         }
21596     },
21597
21598
21599     /**
21600      * Loads data from a passed data block. A Reader which understands the format of the data
21601      * must have been configured in the constructor.
21602      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21603      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21604      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21605      */
21606     loadData : function(o, append){
21607         var r = this.reader.readRecords(o);
21608         this.loadRecords(r, {add: append}, true);
21609     },
21610
21611     /**
21612      * Gets the number of cached records.
21613      * <p>
21614      * <em>If using paging, this may not be the total size of the dataset. If the data object
21615      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21616      * the data set size</em>
21617      */
21618     getCount : function(){
21619         return this.data.length || 0;
21620     },
21621
21622     /**
21623      * Gets the total number of records in the dataset as returned by the server.
21624      * <p>
21625      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21626      * the dataset size</em>
21627      */
21628     getTotalCount : function(){
21629         return this.totalLength || 0;
21630     },
21631
21632     /**
21633      * Returns the sort state of the Store as an object with two properties:
21634      * <pre><code>
21635  field {String} The name of the field by which the Records are sorted
21636  direction {String} The sort order, "ASC" or "DESC"
21637      * </code></pre>
21638      */
21639     getSortState : function(){
21640         return this.sortInfo;
21641     },
21642
21643     // private
21644     applySort : function(){
21645         if(this.sortInfo && !this.remoteSort){
21646             var s = this.sortInfo, f = s.field;
21647             var st = this.fields.get(f).sortType;
21648             var fn = function(r1, r2){
21649                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21650                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21651             };
21652             this.data.sort(s.direction, fn);
21653             if(this.snapshot && this.snapshot != this.data){
21654                 this.snapshot.sort(s.direction, fn);
21655             }
21656         }
21657     },
21658
21659     /**
21660      * Sets the default sort column and order to be used by the next load operation.
21661      * @param {String} fieldName The name of the field to sort by.
21662      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21663      */
21664     setDefaultSort : function(field, dir){
21665         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21666     },
21667
21668     /**
21669      * Sort the Records.
21670      * If remote sorting is used, the sort is performed on the server, and the cache is
21671      * reloaded. If local sorting is used, the cache is sorted internally.
21672      * @param {String} fieldName The name of the field to sort by.
21673      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21674      */
21675     sort : function(fieldName, dir){
21676         var f = this.fields.get(fieldName);
21677         if(!dir){
21678             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21679             
21680             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21681                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21682             }else{
21683                 dir = f.sortDir;
21684             }
21685         }
21686         this.sortToggle[f.name] = dir;
21687         this.sortInfo = {field: f.name, direction: dir};
21688         if(!this.remoteSort){
21689             this.applySort();
21690             this.fireEvent("datachanged", this);
21691         }else{
21692             this.load(this.lastOptions);
21693         }
21694     },
21695
21696     /**
21697      * Calls the specified function for each of the Records in the cache.
21698      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21699      * Returning <em>false</em> aborts and exits the iteration.
21700      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21701      */
21702     each : function(fn, scope){
21703         this.data.each(fn, scope);
21704     },
21705
21706     /**
21707      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21708      * (e.g., during paging).
21709      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21710      */
21711     getModifiedRecords : function(){
21712         return this.modified;
21713     },
21714
21715     // private
21716     createFilterFn : function(property, value, anyMatch){
21717         if(!value.exec){ // not a regex
21718             value = String(value);
21719             if(value.length == 0){
21720                 return false;
21721             }
21722             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21723         }
21724         return function(r){
21725             return value.test(r.data[property]);
21726         };
21727     },
21728
21729     /**
21730      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21731      * @param {String} property A field on your records
21732      * @param {Number} start The record index to start at (defaults to 0)
21733      * @param {Number} end The last record index to include (defaults to length - 1)
21734      * @return {Number} The sum
21735      */
21736     sum : function(property, start, end){
21737         var rs = this.data.items, v = 0;
21738         start = start || 0;
21739         end = (end || end === 0) ? end : rs.length-1;
21740
21741         for(var i = start; i <= end; i++){
21742             v += (rs[i].data[property] || 0);
21743         }
21744         return v;
21745     },
21746
21747     /**
21748      * Filter the records by a specified property.
21749      * @param {String} field A field on your records
21750      * @param {String/RegExp} value Either a string that the field
21751      * should start with or a RegExp to test against the field
21752      * @param {Boolean} anyMatch True to match any part not just the beginning
21753      */
21754     filter : function(property, value, anyMatch){
21755         var fn = this.createFilterFn(property, value, anyMatch);
21756         return fn ? this.filterBy(fn) : this.clearFilter();
21757     },
21758
21759     /**
21760      * Filter by a function. The specified function will be called with each
21761      * record in this data source. If the function returns true the record is included,
21762      * otherwise it is filtered.
21763      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21764      * @param {Object} scope (optional) The scope of the function (defaults to this)
21765      */
21766     filterBy : function(fn, scope){
21767         this.snapshot = this.snapshot || this.data;
21768         this.data = this.queryBy(fn, scope||this);
21769         this.fireEvent("datachanged", this);
21770     },
21771
21772     /**
21773      * Query the records by a specified property.
21774      * @param {String} field A field on your records
21775      * @param {String/RegExp} value Either a string that the field
21776      * should start with or a RegExp to test against the field
21777      * @param {Boolean} anyMatch True to match any part not just the beginning
21778      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21779      */
21780     query : function(property, value, anyMatch){
21781         var fn = this.createFilterFn(property, value, anyMatch);
21782         return fn ? this.queryBy(fn) : this.data.clone();
21783     },
21784
21785     /**
21786      * Query by a function. The specified function will be called with each
21787      * record in this data source. If the function returns true the record is included
21788      * in the results.
21789      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21790      * @param {Object} scope (optional) The scope of the function (defaults to this)
21791       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21792      **/
21793     queryBy : function(fn, scope){
21794         var data = this.snapshot || this.data;
21795         return data.filterBy(fn, scope||this);
21796     },
21797
21798     /**
21799      * Collects unique values for a particular dataIndex from this store.
21800      * @param {String} dataIndex The property to collect
21801      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21802      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21803      * @return {Array} An array of the unique values
21804      **/
21805     collect : function(dataIndex, allowNull, bypassFilter){
21806         var d = (bypassFilter === true && this.snapshot) ?
21807                 this.snapshot.items : this.data.items;
21808         var v, sv, r = [], l = {};
21809         for(var i = 0, len = d.length; i < len; i++){
21810             v = d[i].data[dataIndex];
21811             sv = String(v);
21812             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21813                 l[sv] = true;
21814                 r[r.length] = v;
21815             }
21816         }
21817         return r;
21818     },
21819
21820     /**
21821      * Revert to a view of the Record cache with no filtering applied.
21822      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21823      */
21824     clearFilter : function(suppressEvent){
21825         if(this.snapshot && this.snapshot != this.data){
21826             this.data = this.snapshot;
21827             delete this.snapshot;
21828             if(suppressEvent !== true){
21829                 this.fireEvent("datachanged", this);
21830             }
21831         }
21832     },
21833
21834     // private
21835     afterEdit : function(record){
21836         if(this.modified.indexOf(record) == -1){
21837             this.modified.push(record);
21838         }
21839         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21840     },
21841     
21842     // private
21843     afterReject : function(record){
21844         this.modified.remove(record);
21845         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21846     },
21847
21848     // private
21849     afterCommit : function(record){
21850         this.modified.remove(record);
21851         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21852     },
21853
21854     /**
21855      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21856      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21857      */
21858     commitChanges : function(){
21859         var m = this.modified.slice(0);
21860         this.modified = [];
21861         for(var i = 0, len = m.length; i < len; i++){
21862             m[i].commit();
21863         }
21864     },
21865
21866     /**
21867      * Cancel outstanding changes on all changed records.
21868      */
21869     rejectChanges : function(){
21870         var m = this.modified.slice(0);
21871         this.modified = [];
21872         for(var i = 0, len = m.length; i < len; i++){
21873             m[i].reject();
21874         }
21875     },
21876
21877     onMetaChange : function(meta, rtype, o){
21878         this.recordType = rtype;
21879         this.fields = rtype.prototype.fields;
21880         delete this.snapshot;
21881         this.sortInfo = meta.sortInfo || this.sortInfo;
21882         this.modified = [];
21883         this.fireEvent('metachange', this, this.reader.meta);
21884     },
21885     
21886     moveIndex : function(data, type)
21887     {
21888         var index = this.indexOf(data);
21889         
21890         var newIndex = index + type;
21891         
21892         this.remove(data);
21893         
21894         this.insert(newIndex, data);
21895         
21896     }
21897 });/*
21898  * Based on:
21899  * Ext JS Library 1.1.1
21900  * Copyright(c) 2006-2007, Ext JS, LLC.
21901  *
21902  * Originally Released Under LGPL - original licence link has changed is not relivant.
21903  *
21904  * Fork - LGPL
21905  * <script type="text/javascript">
21906  */
21907
21908 /**
21909  * @class Roo.data.SimpleStore
21910  * @extends Roo.data.Store
21911  * Small helper class to make creating Stores from Array data easier.
21912  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21913  * @cfg {Array} fields An array of field definition objects, or field name strings.
21914  * @cfg {Array} data The multi-dimensional array of data
21915  * @constructor
21916  * @param {Object} config
21917  */
21918 Roo.data.SimpleStore = function(config){
21919     Roo.data.SimpleStore.superclass.constructor.call(this, {
21920         isLocal : true,
21921         reader: new Roo.data.ArrayReader({
21922                 id: config.id
21923             },
21924             Roo.data.Record.create(config.fields)
21925         ),
21926         proxy : new Roo.data.MemoryProxy(config.data)
21927     });
21928     this.load();
21929 };
21930 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21931  * Based on:
21932  * Ext JS Library 1.1.1
21933  * Copyright(c) 2006-2007, Ext JS, LLC.
21934  *
21935  * Originally Released Under LGPL - original licence link has changed is not relivant.
21936  *
21937  * Fork - LGPL
21938  * <script type="text/javascript">
21939  */
21940
21941 /**
21942 /**
21943  * @extends Roo.data.Store
21944  * @class Roo.data.JsonStore
21945  * Small helper class to make creating Stores for JSON data easier. <br/>
21946 <pre><code>
21947 var store = new Roo.data.JsonStore({
21948     url: 'get-images.php',
21949     root: 'images',
21950     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21951 });
21952 </code></pre>
21953  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21954  * JsonReader and HttpProxy (unless inline data is provided).</b>
21955  * @cfg {Array} fields An array of field definition objects, or field name strings.
21956  * @constructor
21957  * @param {Object} config
21958  */
21959 Roo.data.JsonStore = function(c){
21960     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21961         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21962         reader: new Roo.data.JsonReader(c, c.fields)
21963     }));
21964 };
21965 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21966  * Based on:
21967  * Ext JS Library 1.1.1
21968  * Copyright(c) 2006-2007, Ext JS, LLC.
21969  *
21970  * Originally Released Under LGPL - original licence link has changed is not relivant.
21971  *
21972  * Fork - LGPL
21973  * <script type="text/javascript">
21974  */
21975
21976  
21977 Roo.data.Field = function(config){
21978     if(typeof config == "string"){
21979         config = {name: config};
21980     }
21981     Roo.apply(this, config);
21982     
21983     if(!this.type){
21984         this.type = "auto";
21985     }
21986     
21987     var st = Roo.data.SortTypes;
21988     // named sortTypes are supported, here we look them up
21989     if(typeof this.sortType == "string"){
21990         this.sortType = st[this.sortType];
21991     }
21992     
21993     // set default sortType for strings and dates
21994     if(!this.sortType){
21995         switch(this.type){
21996             case "string":
21997                 this.sortType = st.asUCString;
21998                 break;
21999             case "date":
22000                 this.sortType = st.asDate;
22001                 break;
22002             default:
22003                 this.sortType = st.none;
22004         }
22005     }
22006
22007     // define once
22008     var stripRe = /[\$,%]/g;
22009
22010     // prebuilt conversion function for this field, instead of
22011     // switching every time we're reading a value
22012     if(!this.convert){
22013         var cv, dateFormat = this.dateFormat;
22014         switch(this.type){
22015             case "":
22016             case "auto":
22017             case undefined:
22018                 cv = function(v){ return v; };
22019                 break;
22020             case "string":
22021                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
22022                 break;
22023             case "int":
22024                 cv = function(v){
22025                     return v !== undefined && v !== null && v !== '' ?
22026                            parseInt(String(v).replace(stripRe, ""), 10) : '';
22027                     };
22028                 break;
22029             case "float":
22030                 cv = function(v){
22031                     return v !== undefined && v !== null && v !== '' ?
22032                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
22033                     };
22034                 break;
22035             case "bool":
22036             case "boolean":
22037                 cv = function(v){ return v === true || v === "true" || v == 1; };
22038                 break;
22039             case "date":
22040                 cv = function(v){
22041                     if(!v){
22042                         return '';
22043                     }
22044                     if(v instanceof Date){
22045                         return v;
22046                     }
22047                     if(dateFormat){
22048                         if(dateFormat == "timestamp"){
22049                             return new Date(v*1000);
22050                         }
22051                         return Date.parseDate(v, dateFormat);
22052                     }
22053                     var parsed = Date.parse(v);
22054                     return parsed ? new Date(parsed) : null;
22055                 };
22056              break;
22057             
22058         }
22059         this.convert = cv;
22060     }
22061 };
22062
22063 Roo.data.Field.prototype = {
22064     dateFormat: null,
22065     defaultValue: "",
22066     mapping: null,
22067     sortType : null,
22068     sortDir : "ASC"
22069 };/*
22070  * Based on:
22071  * Ext JS Library 1.1.1
22072  * Copyright(c) 2006-2007, Ext JS, LLC.
22073  *
22074  * Originally Released Under LGPL - original licence link has changed is not relivant.
22075  *
22076  * Fork - LGPL
22077  * <script type="text/javascript">
22078  */
22079  
22080 // Base class for reading structured data from a data source.  This class is intended to be
22081 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
22082
22083 /**
22084  * @class Roo.data.DataReader
22085  * Base class for reading structured data from a data source.  This class is intended to be
22086  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
22087  */
22088
22089 Roo.data.DataReader = function(meta, recordType){
22090     
22091     this.meta = meta;
22092     
22093     this.recordType = recordType instanceof Array ? 
22094         Roo.data.Record.create(recordType) : recordType;
22095 };
22096
22097 Roo.data.DataReader.prototype = {
22098      /**
22099      * Create an empty record
22100      * @param {Object} data (optional) - overlay some values
22101      * @return {Roo.data.Record} record created.
22102      */
22103     newRow :  function(d) {
22104         var da =  {};
22105         this.recordType.prototype.fields.each(function(c) {
22106             switch( c.type) {
22107                 case 'int' : da[c.name] = 0; break;
22108                 case 'date' : da[c.name] = new Date(); break;
22109                 case 'float' : da[c.name] = 0.0; break;
22110                 case 'boolean' : da[c.name] = false; break;
22111                 default : da[c.name] = ""; break;
22112             }
22113             
22114         });
22115         return new this.recordType(Roo.apply(da, d));
22116     }
22117     
22118 };/*
22119  * Based on:
22120  * Ext JS Library 1.1.1
22121  * Copyright(c) 2006-2007, Ext JS, LLC.
22122  *
22123  * Originally Released Under LGPL - original licence link has changed is not relivant.
22124  *
22125  * Fork - LGPL
22126  * <script type="text/javascript">
22127  */
22128
22129 /**
22130  * @class Roo.data.DataProxy
22131  * @extends Roo.data.Observable
22132  * This class is an abstract base class for implementations which provide retrieval of
22133  * unformatted data objects.<br>
22134  * <p>
22135  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
22136  * (of the appropriate type which knows how to parse the data object) to provide a block of
22137  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
22138  * <p>
22139  * Custom implementations must implement the load method as described in
22140  * {@link Roo.data.HttpProxy#load}.
22141  */
22142 Roo.data.DataProxy = function(){
22143     this.addEvents({
22144         /**
22145          * @event beforeload
22146          * Fires before a network request is made to retrieve a data object.
22147          * @param {Object} This DataProxy object.
22148          * @param {Object} params The params parameter to the load function.
22149          */
22150         beforeload : true,
22151         /**
22152          * @event load
22153          * Fires before the load method's callback is called.
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          */
22158         load : true,
22159         /**
22160          * @event loadexception
22161          * Fires if an Exception occurs during data retrieval.
22162          * @param {Object} This DataProxy object.
22163          * @param {Object} o The data object.
22164          * @param {Object} arg The callback argument object passed to the load function.
22165          * @param {Object} e The Exception.
22166          */
22167         loadexception : true
22168     });
22169     Roo.data.DataProxy.superclass.constructor.call(this);
22170 };
22171
22172 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22173
22174     /**
22175      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22176      */
22177 /*
22178  * Based on:
22179  * Ext JS Library 1.1.1
22180  * Copyright(c) 2006-2007, Ext JS, LLC.
22181  *
22182  * Originally Released Under LGPL - original licence link has changed is not relivant.
22183  *
22184  * Fork - LGPL
22185  * <script type="text/javascript">
22186  */
22187 /**
22188  * @class Roo.data.MemoryProxy
22189  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22190  * to the Reader when its load method is called.
22191  * @constructor
22192  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22193  */
22194 Roo.data.MemoryProxy = function(data){
22195     if (data.data) {
22196         data = data.data;
22197     }
22198     Roo.data.MemoryProxy.superclass.constructor.call(this);
22199     this.data = data;
22200 };
22201
22202 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22203     /**
22204      * Load data from the requested source (in this case an in-memory
22205      * data object passed to the constructor), read the data object into
22206      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22207      * process that block using the passed callback.
22208      * @param {Object} params This parameter is not used by the MemoryProxy class.
22209      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22210      * object into a block of Roo.data.Records.
22211      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22212      * The function must be passed <ul>
22213      * <li>The Record block object</li>
22214      * <li>The "arg" argument from the load function</li>
22215      * <li>A boolean success indicator</li>
22216      * </ul>
22217      * @param {Object} scope The scope in which to call the callback
22218      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22219      */
22220     load : function(params, reader, callback, scope, arg){
22221         params = params || {};
22222         var result;
22223         try {
22224             result = reader.readRecords(this.data);
22225         }catch(e){
22226             this.fireEvent("loadexception", this, arg, null, e);
22227             callback.call(scope, null, arg, false);
22228             return;
22229         }
22230         callback.call(scope, result, arg, true);
22231     },
22232     
22233     // private
22234     update : function(params, records){
22235         
22236     }
22237 });/*
22238  * Based on:
22239  * Ext JS Library 1.1.1
22240  * Copyright(c) 2006-2007, Ext JS, LLC.
22241  *
22242  * Originally Released Under LGPL - original licence link has changed is not relivant.
22243  *
22244  * Fork - LGPL
22245  * <script type="text/javascript">
22246  */
22247 /**
22248  * @class Roo.data.HttpProxy
22249  * @extends Roo.data.DataProxy
22250  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22251  * configured to reference a certain URL.<br><br>
22252  * <p>
22253  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22254  * from which the running page was served.<br><br>
22255  * <p>
22256  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22257  * <p>
22258  * Be aware that to enable the browser to parse an XML document, the server must set
22259  * the Content-Type header in the HTTP response to "text/xml".
22260  * @constructor
22261  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22262  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22263  * will be used to make the request.
22264  */
22265 Roo.data.HttpProxy = function(conn){
22266     Roo.data.HttpProxy.superclass.constructor.call(this);
22267     // is conn a conn config or a real conn?
22268     this.conn = conn;
22269     this.useAjax = !conn || !conn.events;
22270   
22271 };
22272
22273 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22274     // thse are take from connection...
22275     
22276     /**
22277      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22278      */
22279     /**
22280      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22281      * extra parameters to each request made by this object. (defaults to undefined)
22282      */
22283     /**
22284      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22285      *  to each request made by this object. (defaults to undefined)
22286      */
22287     /**
22288      * @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)
22289      */
22290     /**
22291      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22292      */
22293      /**
22294      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22295      * @type Boolean
22296      */
22297   
22298
22299     /**
22300      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22301      * @type Boolean
22302      */
22303     /**
22304      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22305      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22306      * a finer-grained basis than the DataProxy events.
22307      */
22308     getConnection : function(){
22309         return this.useAjax ? Roo.Ajax : this.conn;
22310     },
22311
22312     /**
22313      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22314      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22315      * process that block using the passed callback.
22316      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22317      * for the request to the remote server.
22318      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22319      * object into a block of Roo.data.Records.
22320      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22321      * The function must be passed <ul>
22322      * <li>The Record block object</li>
22323      * <li>The "arg" argument from the load function</li>
22324      * <li>A boolean success indicator</li>
22325      * </ul>
22326      * @param {Object} scope The scope in which to call the callback
22327      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22328      */
22329     load : function(params, reader, callback, scope, arg){
22330         if(this.fireEvent("beforeload", this, params) !== false){
22331             var  o = {
22332                 params : params || {},
22333                 request: {
22334                     callback : callback,
22335                     scope : scope,
22336                     arg : arg
22337                 },
22338                 reader: reader,
22339                 callback : this.loadResponse,
22340                 scope: this
22341             };
22342             if(this.useAjax){
22343                 Roo.applyIf(o, this.conn);
22344                 if(this.activeRequest){
22345                     Roo.Ajax.abort(this.activeRequest);
22346                 }
22347                 this.activeRequest = Roo.Ajax.request(o);
22348             }else{
22349                 this.conn.request(o);
22350             }
22351         }else{
22352             callback.call(scope||this, null, arg, false);
22353         }
22354     },
22355
22356     // private
22357     loadResponse : function(o, success, response){
22358         delete this.activeRequest;
22359         if(!success){
22360             this.fireEvent("loadexception", this, o, response);
22361             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22362             return;
22363         }
22364         var result;
22365         try {
22366             result = o.reader.read(response);
22367         }catch(e){
22368             this.fireEvent("loadexception", this, o, response, e);
22369             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22370             return;
22371         }
22372         
22373         this.fireEvent("load", this, o, o.request.arg);
22374         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22375     },
22376
22377     // private
22378     update : function(dataSet){
22379
22380     },
22381
22382     // private
22383     updateResponse : function(dataSet){
22384
22385     }
22386 });/*
22387  * Based on:
22388  * Ext JS Library 1.1.1
22389  * Copyright(c) 2006-2007, Ext JS, LLC.
22390  *
22391  * Originally Released Under LGPL - original licence link has changed is not relivant.
22392  *
22393  * Fork - LGPL
22394  * <script type="text/javascript">
22395  */
22396
22397 /**
22398  * @class Roo.data.ScriptTagProxy
22399  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22400  * other than the originating domain of the running page.<br><br>
22401  * <p>
22402  * <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
22403  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22404  * <p>
22405  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22406  * source code that is used as the source inside a &lt;script> tag.<br><br>
22407  * <p>
22408  * In order for the browser to process the returned data, the server must wrap the data object
22409  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22410  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22411  * depending on whether the callback name was passed:
22412  * <p>
22413  * <pre><code>
22414 boolean scriptTag = false;
22415 String cb = request.getParameter("callback");
22416 if (cb != null) {
22417     scriptTag = true;
22418     response.setContentType("text/javascript");
22419 } else {
22420     response.setContentType("application/x-json");
22421 }
22422 Writer out = response.getWriter();
22423 if (scriptTag) {
22424     out.write(cb + "(");
22425 }
22426 out.print(dataBlock.toJsonString());
22427 if (scriptTag) {
22428     out.write(");");
22429 }
22430 </pre></code>
22431  *
22432  * @constructor
22433  * @param {Object} config A configuration object.
22434  */
22435 Roo.data.ScriptTagProxy = function(config){
22436     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22437     Roo.apply(this, config);
22438     this.head = document.getElementsByTagName("head")[0];
22439 };
22440
22441 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22442
22443 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22444     /**
22445      * @cfg {String} url The URL from which to request the data object.
22446      */
22447     /**
22448      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22449      */
22450     timeout : 30000,
22451     /**
22452      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22453      * the server the name of the callback function set up by the load call to process the returned data object.
22454      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22455      * javascript output which calls this named function passing the data object as its only parameter.
22456      */
22457     callbackParam : "callback",
22458     /**
22459      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22460      * name to the request.
22461      */
22462     nocache : true,
22463
22464     /**
22465      * Load data from the configured URL, read the data object into
22466      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22467      * process that block using the passed callback.
22468      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22469      * for the request to the remote server.
22470      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22471      * object into a block of Roo.data.Records.
22472      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22473      * The function must be passed <ul>
22474      * <li>The Record block object</li>
22475      * <li>The "arg" argument from the load function</li>
22476      * <li>A boolean success indicator</li>
22477      * </ul>
22478      * @param {Object} scope The scope in which to call the callback
22479      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22480      */
22481     load : function(params, reader, callback, scope, arg){
22482         if(this.fireEvent("beforeload", this, params) !== false){
22483
22484             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22485
22486             var url = this.url;
22487             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22488             if(this.nocache){
22489                 url += "&_dc=" + (new Date().getTime());
22490             }
22491             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22492             var trans = {
22493                 id : transId,
22494                 cb : "stcCallback"+transId,
22495                 scriptId : "stcScript"+transId,
22496                 params : params,
22497                 arg : arg,
22498                 url : url,
22499                 callback : callback,
22500                 scope : scope,
22501                 reader : reader
22502             };
22503             var conn = this;
22504
22505             window[trans.cb] = function(o){
22506                 conn.handleResponse(o, trans);
22507             };
22508
22509             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22510
22511             if(this.autoAbort !== false){
22512                 this.abort();
22513             }
22514
22515             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22516
22517             var script = document.createElement("script");
22518             script.setAttribute("src", url);
22519             script.setAttribute("type", "text/javascript");
22520             script.setAttribute("id", trans.scriptId);
22521             this.head.appendChild(script);
22522
22523             this.trans = trans;
22524         }else{
22525             callback.call(scope||this, null, arg, false);
22526         }
22527     },
22528
22529     // private
22530     isLoading : function(){
22531         return this.trans ? true : false;
22532     },
22533
22534     /**
22535      * Abort the current server request.
22536      */
22537     abort : function(){
22538         if(this.isLoading()){
22539             this.destroyTrans(this.trans);
22540         }
22541     },
22542
22543     // private
22544     destroyTrans : function(trans, isLoaded){
22545         this.head.removeChild(document.getElementById(trans.scriptId));
22546         clearTimeout(trans.timeoutId);
22547         if(isLoaded){
22548             window[trans.cb] = undefined;
22549             try{
22550                 delete window[trans.cb];
22551             }catch(e){}
22552         }else{
22553             // if hasn't been loaded, wait for load to remove it to prevent script error
22554             window[trans.cb] = function(){
22555                 window[trans.cb] = undefined;
22556                 try{
22557                     delete window[trans.cb];
22558                 }catch(e){}
22559             };
22560         }
22561     },
22562
22563     // private
22564     handleResponse : function(o, trans){
22565         this.trans = false;
22566         this.destroyTrans(trans, true);
22567         var result;
22568         try {
22569             result = trans.reader.readRecords(o);
22570         }catch(e){
22571             this.fireEvent("loadexception", this, o, trans.arg, e);
22572             trans.callback.call(trans.scope||window, null, trans.arg, false);
22573             return;
22574         }
22575         this.fireEvent("load", this, o, trans.arg);
22576         trans.callback.call(trans.scope||window, result, trans.arg, true);
22577     },
22578
22579     // private
22580     handleFailure : function(trans){
22581         this.trans = false;
22582         this.destroyTrans(trans, false);
22583         this.fireEvent("loadexception", this, null, trans.arg);
22584         trans.callback.call(trans.scope||window, null, trans.arg, false);
22585     }
22586 });/*
22587  * Based on:
22588  * Ext JS Library 1.1.1
22589  * Copyright(c) 2006-2007, Ext JS, LLC.
22590  *
22591  * Originally Released Under LGPL - original licence link has changed is not relivant.
22592  *
22593  * Fork - LGPL
22594  * <script type="text/javascript">
22595  */
22596
22597 /**
22598  * @class Roo.data.JsonReader
22599  * @extends Roo.data.DataReader
22600  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22601  * based on mappings in a provided Roo.data.Record constructor.
22602  * 
22603  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22604  * in the reply previously. 
22605  * 
22606  * <p>
22607  * Example code:
22608  * <pre><code>
22609 var RecordDef = Roo.data.Record.create([
22610     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22611     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22612 ]);
22613 var myReader = new Roo.data.JsonReader({
22614     totalProperty: "results",    // The property which contains the total dataset size (optional)
22615     root: "rows",                // The property which contains an Array of row objects
22616     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22617 }, RecordDef);
22618 </code></pre>
22619  * <p>
22620  * This would consume a JSON file like this:
22621  * <pre><code>
22622 { 'results': 2, 'rows': [
22623     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22624     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22625 }
22626 </code></pre>
22627  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22628  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22629  * paged from the remote server.
22630  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22631  * @cfg {String} root name of the property which contains the Array of row objects.
22632  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22633  * @cfg {Array} fields Array of field definition objects
22634  * @constructor
22635  * Create a new JsonReader
22636  * @param {Object} meta Metadata configuration options
22637  * @param {Object} recordType Either an Array of field definition objects,
22638  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22639  */
22640 Roo.data.JsonReader = function(meta, recordType){
22641     
22642     meta = meta || {};
22643     // set some defaults:
22644     Roo.applyIf(meta, {
22645         totalProperty: 'total',
22646         successProperty : 'success',
22647         root : 'data',
22648         id : 'id'
22649     });
22650     
22651     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22652 };
22653 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22654     
22655     /**
22656      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22657      * Used by Store query builder to append _requestMeta to params.
22658      * 
22659      */
22660     metaFromRemote : false,
22661     /**
22662      * This method is only used by a DataProxy which has retrieved data from a remote server.
22663      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22664      * @return {Object} data A data block which is used by an Roo.data.Store object as
22665      * a cache of Roo.data.Records.
22666      */
22667     read : function(response){
22668         var json = response.responseText;
22669        
22670         var o = /* eval:var:o */ eval("("+json+")");
22671         if(!o) {
22672             throw {message: "JsonReader.read: Json object not found"};
22673         }
22674         
22675         if(o.metaData){
22676             
22677             delete this.ef;
22678             this.metaFromRemote = true;
22679             this.meta = o.metaData;
22680             this.recordType = Roo.data.Record.create(o.metaData.fields);
22681             this.onMetaChange(this.meta, this.recordType, o);
22682         }
22683         return this.readRecords(o);
22684     },
22685
22686     // private function a store will implement
22687     onMetaChange : function(meta, recordType, o){
22688
22689     },
22690
22691     /**
22692          * @ignore
22693          */
22694     simpleAccess: function(obj, subsc) {
22695         return obj[subsc];
22696     },
22697
22698         /**
22699          * @ignore
22700          */
22701     getJsonAccessor: function(){
22702         var re = /[\[\.]/;
22703         return function(expr) {
22704             try {
22705                 return(re.test(expr))
22706                     ? new Function("obj", "return obj." + expr)
22707                     : function(obj){
22708                         return obj[expr];
22709                     };
22710             } catch(e){}
22711             return Roo.emptyFn;
22712         };
22713     }(),
22714
22715     /**
22716      * Create a data block containing Roo.data.Records from an XML document.
22717      * @param {Object} o An object which contains an Array of row objects in the property specified
22718      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22719      * which contains the total size of the dataset.
22720      * @return {Object} data A data block which is used by an Roo.data.Store object as
22721      * a cache of Roo.data.Records.
22722      */
22723     readRecords : function(o){
22724         /**
22725          * After any data loads, the raw JSON data is available for further custom processing.
22726          * @type Object
22727          */
22728         this.o = o;
22729         var s = this.meta, Record = this.recordType,
22730             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
22731
22732 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22733         if (!this.ef) {
22734             if(s.totalProperty) {
22735                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22736                 }
22737                 if(s.successProperty) {
22738                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22739                 }
22740                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22741                 if (s.id) {
22742                         var g = this.getJsonAccessor(s.id);
22743                         this.getId = function(rec) {
22744                                 var r = g(rec);  
22745                                 return (r === undefined || r === "") ? null : r;
22746                         };
22747                 } else {
22748                         this.getId = function(){return null;};
22749                 }
22750             this.ef = [];
22751             for(var jj = 0; jj < fl; jj++){
22752                 f = fi[jj];
22753                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22754                 this.ef[jj] = this.getJsonAccessor(map);
22755             }
22756         }
22757
22758         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22759         if(s.totalProperty){
22760             var vt = parseInt(this.getTotal(o), 10);
22761             if(!isNaN(vt)){
22762                 totalRecords = vt;
22763             }
22764         }
22765         if(s.successProperty){
22766             var vs = this.getSuccess(o);
22767             if(vs === false || vs === 'false'){
22768                 success = false;
22769             }
22770         }
22771         var records = [];
22772         for(var i = 0; i < c; i++){
22773                 var n = root[i];
22774             var values = {};
22775             var id = this.getId(n);
22776             for(var j = 0; j < fl; j++){
22777                 f = fi[j];
22778             var v = this.ef[j](n);
22779             if (!f.convert) {
22780                 Roo.log('missing convert for ' + f.name);
22781                 Roo.log(f);
22782                 continue;
22783             }
22784             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22785             }
22786             var record = new Record(values, id);
22787             record.json = n;
22788             records[i] = record;
22789         }
22790         return {
22791             raw : o,
22792             success : success,
22793             records : records,
22794             totalRecords : totalRecords
22795         };
22796     }
22797 });/*
22798  * Based on:
22799  * Ext JS Library 1.1.1
22800  * Copyright(c) 2006-2007, Ext JS, LLC.
22801  *
22802  * Originally Released Under LGPL - original licence link has changed is not relivant.
22803  *
22804  * Fork - LGPL
22805  * <script type="text/javascript">
22806  */
22807
22808 /**
22809  * @class Roo.data.XmlReader
22810  * @extends Roo.data.DataReader
22811  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22812  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22813  * <p>
22814  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22815  * header in the HTTP response must be set to "text/xml".</em>
22816  * <p>
22817  * Example code:
22818  * <pre><code>
22819 var RecordDef = Roo.data.Record.create([
22820    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22821    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22822 ]);
22823 var myReader = new Roo.data.XmlReader({
22824    totalRecords: "results", // The element which contains the total dataset size (optional)
22825    record: "row",           // The repeated element which contains row information
22826    id: "id"                 // The element within the row that provides an ID for the record (optional)
22827 }, RecordDef);
22828 </code></pre>
22829  * <p>
22830  * This would consume an XML file like this:
22831  * <pre><code>
22832 &lt;?xml?>
22833 &lt;dataset>
22834  &lt;results>2&lt;/results>
22835  &lt;row>
22836    &lt;id>1&lt;/id>
22837    &lt;name>Bill&lt;/name>
22838    &lt;occupation>Gardener&lt;/occupation>
22839  &lt;/row>
22840  &lt;row>
22841    &lt;id>2&lt;/id>
22842    &lt;name>Ben&lt;/name>
22843    &lt;occupation>Horticulturalist&lt;/occupation>
22844  &lt;/row>
22845 &lt;/dataset>
22846 </code></pre>
22847  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22848  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22849  * paged from the remote server.
22850  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22851  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22852  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22853  * a record identifier value.
22854  * @constructor
22855  * Create a new XmlReader
22856  * @param {Object} meta Metadata configuration options
22857  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22858  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22859  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22860  */
22861 Roo.data.XmlReader = function(meta, recordType){
22862     meta = meta || {};
22863     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22864 };
22865 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22866     /**
22867      * This method is only used by a DataProxy which has retrieved data from a remote server.
22868          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22869          * to contain a method called 'responseXML' that returns an XML document object.
22870      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22871      * a cache of Roo.data.Records.
22872      */
22873     read : function(response){
22874         var doc = response.responseXML;
22875         if(!doc) {
22876             throw {message: "XmlReader.read: XML Document not available"};
22877         }
22878         return this.readRecords(doc);
22879     },
22880
22881     /**
22882      * Create a data block containing Roo.data.Records from an XML document.
22883          * @param {Object} doc A parsed XML document.
22884      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22885      * a cache of Roo.data.Records.
22886      */
22887     readRecords : function(doc){
22888         /**
22889          * After any data loads/reads, the raw XML Document is available for further custom processing.
22890          * @type XMLDocument
22891          */
22892         this.xmlData = doc;
22893         var root = doc.documentElement || doc;
22894         var q = Roo.DomQuery;
22895         var recordType = this.recordType, fields = recordType.prototype.fields;
22896         var sid = this.meta.id;
22897         var totalRecords = 0, success = true;
22898         if(this.meta.totalRecords){
22899             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22900         }
22901         
22902         if(this.meta.success){
22903             var sv = q.selectValue(this.meta.success, root, true);
22904             success = sv !== false && sv !== 'false';
22905         }
22906         var records = [];
22907         var ns = q.select(this.meta.record, root);
22908         for(var i = 0, len = ns.length; i < len; i++) {
22909                 var n = ns[i];
22910                 var values = {};
22911                 var id = sid ? q.selectValue(sid, n) : undefined;
22912                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22913                     var f = fields.items[j];
22914                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22915                     v = f.convert(v);
22916                     values[f.name] = v;
22917                 }
22918                 var record = new recordType(values, id);
22919                 record.node = n;
22920                 records[records.length] = record;
22921             }
22922
22923             return {
22924                 success : success,
22925                 records : records,
22926                 totalRecords : totalRecords || records.length
22927             };
22928     }
22929 });/*
22930  * Based on:
22931  * Ext JS Library 1.1.1
22932  * Copyright(c) 2006-2007, Ext JS, LLC.
22933  *
22934  * Originally Released Under LGPL - original licence link has changed is not relivant.
22935  *
22936  * Fork - LGPL
22937  * <script type="text/javascript">
22938  */
22939
22940 /**
22941  * @class Roo.data.ArrayReader
22942  * @extends Roo.data.DataReader
22943  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22944  * Each element of that Array represents a row of data fields. The
22945  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22946  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22947  * <p>
22948  * Example code:.
22949  * <pre><code>
22950 var RecordDef = Roo.data.Record.create([
22951     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22952     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22953 ]);
22954 var myReader = new Roo.data.ArrayReader({
22955     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22956 }, RecordDef);
22957 </code></pre>
22958  * <p>
22959  * This would consume an Array like this:
22960  * <pre><code>
22961 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22962   </code></pre>
22963  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22964  * @constructor
22965  * Create a new JsonReader
22966  * @param {Object} meta Metadata configuration options.
22967  * @param {Object} recordType Either an Array of field definition objects
22968  * as specified to {@link Roo.data.Record#create},
22969  * or an {@link Roo.data.Record} object
22970  * created using {@link Roo.data.Record#create}.
22971  */
22972 Roo.data.ArrayReader = function(meta, recordType){
22973     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22974 };
22975
22976 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22977     /**
22978      * Create a data block containing Roo.data.Records from an XML document.
22979      * @param {Object} o An Array of row objects which represents the dataset.
22980      * @return {Object} data A data block which is used by an Roo.data.Store object as
22981      * a cache of Roo.data.Records.
22982      */
22983     readRecords : function(o){
22984         var sid = this.meta ? this.meta.id : null;
22985         var recordType = this.recordType, fields = recordType.prototype.fields;
22986         var records = [];
22987         var root = o;
22988             for(var i = 0; i < root.length; i++){
22989                     var n = root[i];
22990                 var values = {};
22991                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22992                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22993                 var f = fields.items[j];
22994                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22995                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22996                 v = f.convert(v);
22997                 values[f.name] = v;
22998             }
22999                 var record = new recordType(values, id);
23000                 record.json = n;
23001                 records[records.length] = record;
23002             }
23003             return {
23004                 records : records,
23005                 totalRecords : records.length
23006             };
23007     }
23008 });/*
23009  * Based on:
23010  * Ext JS Library 1.1.1
23011  * Copyright(c) 2006-2007, Ext JS, LLC.
23012  *
23013  * Originally Released Under LGPL - original licence link has changed is not relivant.
23014  *
23015  * Fork - LGPL
23016  * <script type="text/javascript">
23017  */
23018
23019
23020 /**
23021  * @class Roo.data.Tree
23022  * @extends Roo.util.Observable
23023  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
23024  * in the tree have most standard DOM functionality.
23025  * @constructor
23026  * @param {Node} root (optional) The root node
23027  */
23028 Roo.data.Tree = function(root){
23029    this.nodeHash = {};
23030    /**
23031     * The root node for this tree
23032     * @type Node
23033     */
23034    this.root = null;
23035    if(root){
23036        this.setRootNode(root);
23037    }
23038    this.addEvents({
23039        /**
23040         * @event append
23041         * Fires when a new child node is appended to a node in this tree.
23042         * @param {Tree} tree The owner tree
23043         * @param {Node} parent The parent node
23044         * @param {Node} node The newly appended node
23045         * @param {Number} index The index of the newly appended node
23046         */
23047        "append" : true,
23048        /**
23049         * @event remove
23050         * Fires when a child node is removed from a node in this tree.
23051         * @param {Tree} tree The owner tree
23052         * @param {Node} parent The parent node
23053         * @param {Node} node The child node removed
23054         */
23055        "remove" : true,
23056        /**
23057         * @event move
23058         * Fires when a node is moved to a new location in the tree
23059         * @param {Tree} tree The owner tree
23060         * @param {Node} node The node moved
23061         * @param {Node} oldParent The old parent of this node
23062         * @param {Node} newParent The new parent of this node
23063         * @param {Number} index The index it was moved to
23064         */
23065        "move" : true,
23066        /**
23067         * @event insert
23068         * Fires when a new child node is inserted in a node in this tree.
23069         * @param {Tree} tree The owner tree
23070         * @param {Node} parent The parent node
23071         * @param {Node} node The child node inserted
23072         * @param {Node} refNode The child node the node was inserted before
23073         */
23074        "insert" : true,
23075        /**
23076         * @event beforeappend
23077         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
23078         * @param {Tree} tree The owner tree
23079         * @param {Node} parent The parent node
23080         * @param {Node} node The child node to be appended
23081         */
23082        "beforeappend" : true,
23083        /**
23084         * @event beforeremove
23085         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
23086         * @param {Tree} tree The owner tree
23087         * @param {Node} parent The parent node
23088         * @param {Node} node The child node to be removed
23089         */
23090        "beforeremove" : true,
23091        /**
23092         * @event beforemove
23093         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
23094         * @param {Tree} tree The owner tree
23095         * @param {Node} node The node being moved
23096         * @param {Node} oldParent The parent of the node
23097         * @param {Node} newParent The new parent the node is moving to
23098         * @param {Number} index The index it is being moved to
23099         */
23100        "beforemove" : true,
23101        /**
23102         * @event beforeinsert
23103         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
23104         * @param {Tree} tree The owner tree
23105         * @param {Node} parent The parent node
23106         * @param {Node} node The child node to be inserted
23107         * @param {Node} refNode The child node the node is being inserted before
23108         */
23109        "beforeinsert" : true
23110    });
23111
23112     Roo.data.Tree.superclass.constructor.call(this);
23113 };
23114
23115 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
23116     pathSeparator: "/",
23117
23118     proxyNodeEvent : function(){
23119         return this.fireEvent.apply(this, arguments);
23120     },
23121
23122     /**
23123      * Returns the root node for this tree.
23124      * @return {Node}
23125      */
23126     getRootNode : function(){
23127         return this.root;
23128     },
23129
23130     /**
23131      * Sets the root node for this tree.
23132      * @param {Node} node
23133      * @return {Node}
23134      */
23135     setRootNode : function(node){
23136         this.root = node;
23137         node.ownerTree = this;
23138         node.isRoot = true;
23139         this.registerNode(node);
23140         return node;
23141     },
23142
23143     /**
23144      * Gets a node in this tree by its id.
23145      * @param {String} id
23146      * @return {Node}
23147      */
23148     getNodeById : function(id){
23149         return this.nodeHash[id];
23150     },
23151
23152     registerNode : function(node){
23153         this.nodeHash[node.id] = node;
23154     },
23155
23156     unregisterNode : function(node){
23157         delete this.nodeHash[node.id];
23158     },
23159
23160     toString : function(){
23161         return "[Tree"+(this.id?" "+this.id:"")+"]";
23162     }
23163 });
23164
23165 /**
23166  * @class Roo.data.Node
23167  * @extends Roo.util.Observable
23168  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23169  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23170  * @constructor
23171  * @param {Object} attributes The attributes/config for the node
23172  */
23173 Roo.data.Node = function(attributes){
23174     /**
23175      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23176      * @type {Object}
23177      */
23178     this.attributes = attributes || {};
23179     this.leaf = this.attributes.leaf;
23180     /**
23181      * The node id. @type String
23182      */
23183     this.id = this.attributes.id;
23184     if(!this.id){
23185         this.id = Roo.id(null, "ynode-");
23186         this.attributes.id = this.id;
23187     }
23188      
23189     
23190     /**
23191      * All child nodes of this node. @type Array
23192      */
23193     this.childNodes = [];
23194     if(!this.childNodes.indexOf){ // indexOf is a must
23195         this.childNodes.indexOf = function(o){
23196             for(var i = 0, len = this.length; i < len; i++){
23197                 if(this[i] == o) {
23198                     return i;
23199                 }
23200             }
23201             return -1;
23202         };
23203     }
23204     /**
23205      * The parent node for this node. @type Node
23206      */
23207     this.parentNode = null;
23208     /**
23209      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23210      */
23211     this.firstChild = null;
23212     /**
23213      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23214      */
23215     this.lastChild = null;
23216     /**
23217      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23218      */
23219     this.previousSibling = null;
23220     /**
23221      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23222      */
23223     this.nextSibling = null;
23224
23225     this.addEvents({
23226        /**
23227         * @event append
23228         * Fires when a new child node is appended
23229         * @param {Tree} tree The owner tree
23230         * @param {Node} this This node
23231         * @param {Node} node The newly appended node
23232         * @param {Number} index The index of the newly appended node
23233         */
23234        "append" : true,
23235        /**
23236         * @event remove
23237         * Fires when a child node is removed
23238         * @param {Tree} tree The owner tree
23239         * @param {Node} this This node
23240         * @param {Node} node The removed node
23241         */
23242        "remove" : true,
23243        /**
23244         * @event move
23245         * Fires when this node is moved to a new location in the tree
23246         * @param {Tree} tree The owner tree
23247         * @param {Node} this This node
23248         * @param {Node} oldParent The old parent of this node
23249         * @param {Node} newParent The new parent of this node
23250         * @param {Number} index The index it was moved to
23251         */
23252        "move" : true,
23253        /**
23254         * @event insert
23255         * Fires when a new child node is inserted.
23256         * @param {Tree} tree The owner tree
23257         * @param {Node} this This node
23258         * @param {Node} node The child node inserted
23259         * @param {Node} refNode The child node the node was inserted before
23260         */
23261        "insert" : true,
23262        /**
23263         * @event beforeappend
23264         * Fires before a new child is appended, return false to cancel the append.
23265         * @param {Tree} tree The owner tree
23266         * @param {Node} this This node
23267         * @param {Node} node The child node to be appended
23268         */
23269        "beforeappend" : true,
23270        /**
23271         * @event beforeremove
23272         * Fires before a child is removed, return false to cancel the remove.
23273         * @param {Tree} tree The owner tree
23274         * @param {Node} this This node
23275         * @param {Node} node The child node to be removed
23276         */
23277        "beforeremove" : true,
23278        /**
23279         * @event beforemove
23280         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23281         * @param {Tree} tree The owner tree
23282         * @param {Node} this This node
23283         * @param {Node} oldParent The parent of this node
23284         * @param {Node} newParent The new parent this node is moving to
23285         * @param {Number} index The index it is being moved to
23286         */
23287        "beforemove" : true,
23288        /**
23289         * @event beforeinsert
23290         * Fires before a new child is inserted, return false to cancel the insert.
23291         * @param {Tree} tree The owner tree
23292         * @param {Node} this This node
23293         * @param {Node} node The child node to be inserted
23294         * @param {Node} refNode The child node the node is being inserted before
23295         */
23296        "beforeinsert" : true
23297    });
23298     this.listeners = this.attributes.listeners;
23299     Roo.data.Node.superclass.constructor.call(this);
23300 };
23301
23302 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23303     fireEvent : function(evtName){
23304         // first do standard event for this node
23305         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23306             return false;
23307         }
23308         // then bubble it up to the tree if the event wasn't cancelled
23309         var ot = this.getOwnerTree();
23310         if(ot){
23311             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23312                 return false;
23313             }
23314         }
23315         return true;
23316     },
23317
23318     /**
23319      * Returns true if this node is a leaf
23320      * @return {Boolean}
23321      */
23322     isLeaf : function(){
23323         return this.leaf === true;
23324     },
23325
23326     // private
23327     setFirstChild : function(node){
23328         this.firstChild = node;
23329     },
23330
23331     //private
23332     setLastChild : function(node){
23333         this.lastChild = node;
23334     },
23335
23336
23337     /**
23338      * Returns true if this node is the last child of its parent
23339      * @return {Boolean}
23340      */
23341     isLast : function(){
23342        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23343     },
23344
23345     /**
23346      * Returns true if this node is the first child of its parent
23347      * @return {Boolean}
23348      */
23349     isFirst : function(){
23350        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23351     },
23352
23353     hasChildNodes : function(){
23354         return !this.isLeaf() && this.childNodes.length > 0;
23355     },
23356
23357     /**
23358      * Insert node(s) as the last child node of this node.
23359      * @param {Node/Array} node The node or Array of nodes to append
23360      * @return {Node} The appended node if single append, or null if an array was passed
23361      */
23362     appendChild : function(node){
23363         var multi = false;
23364         if(node instanceof Array){
23365             multi = node;
23366         }else if(arguments.length > 1){
23367             multi = arguments;
23368         }
23369         // if passed an array or multiple args do them one by one
23370         if(multi){
23371             for(var i = 0, len = multi.length; i < len; i++) {
23372                 this.appendChild(multi[i]);
23373             }
23374         }else{
23375             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23376                 return false;
23377             }
23378             var index = this.childNodes.length;
23379             var oldParent = node.parentNode;
23380             // it's a move, make sure we move it cleanly
23381             if(oldParent){
23382                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23383                     return false;
23384                 }
23385                 oldParent.removeChild(node);
23386             }
23387             index = this.childNodes.length;
23388             if(index == 0){
23389                 this.setFirstChild(node);
23390             }
23391             this.childNodes.push(node);
23392             node.parentNode = this;
23393             var ps = this.childNodes[index-1];
23394             if(ps){
23395                 node.previousSibling = ps;
23396                 ps.nextSibling = node;
23397             }else{
23398                 node.previousSibling = null;
23399             }
23400             node.nextSibling = null;
23401             this.setLastChild(node);
23402             node.setOwnerTree(this.getOwnerTree());
23403             this.fireEvent("append", this.ownerTree, this, node, index);
23404             if(oldParent){
23405                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23406             }
23407             return node;
23408         }
23409     },
23410
23411     /**
23412      * Removes a child node from this node.
23413      * @param {Node} node The node to remove
23414      * @return {Node} The removed node
23415      */
23416     removeChild : function(node){
23417         var index = this.childNodes.indexOf(node);
23418         if(index == -1){
23419             return false;
23420         }
23421         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23422             return false;
23423         }
23424
23425         // remove it from childNodes collection
23426         this.childNodes.splice(index, 1);
23427
23428         // update siblings
23429         if(node.previousSibling){
23430             node.previousSibling.nextSibling = node.nextSibling;
23431         }
23432         if(node.nextSibling){
23433             node.nextSibling.previousSibling = node.previousSibling;
23434         }
23435
23436         // update child refs
23437         if(this.firstChild == node){
23438             this.setFirstChild(node.nextSibling);
23439         }
23440         if(this.lastChild == node){
23441             this.setLastChild(node.previousSibling);
23442         }
23443
23444         node.setOwnerTree(null);
23445         // clear any references from the node
23446         node.parentNode = null;
23447         node.previousSibling = null;
23448         node.nextSibling = null;
23449         this.fireEvent("remove", this.ownerTree, this, node);
23450         return node;
23451     },
23452
23453     /**
23454      * Inserts the first node before the second node in this nodes childNodes collection.
23455      * @param {Node} node The node to insert
23456      * @param {Node} refNode The node to insert before (if null the node is appended)
23457      * @return {Node} The inserted node
23458      */
23459     insertBefore : function(node, refNode){
23460         if(!refNode){ // like standard Dom, refNode can be null for append
23461             return this.appendChild(node);
23462         }
23463         // nothing to do
23464         if(node == refNode){
23465             return false;
23466         }
23467
23468         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23469             return false;
23470         }
23471         var index = this.childNodes.indexOf(refNode);
23472         var oldParent = node.parentNode;
23473         var refIndex = index;
23474
23475         // when moving internally, indexes will change after remove
23476         if(oldParent == this && this.childNodes.indexOf(node) < index){
23477             refIndex--;
23478         }
23479
23480         // it's a move, make sure we move it cleanly
23481         if(oldParent){
23482             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23483                 return false;
23484             }
23485             oldParent.removeChild(node);
23486         }
23487         if(refIndex == 0){
23488             this.setFirstChild(node);
23489         }
23490         this.childNodes.splice(refIndex, 0, node);
23491         node.parentNode = this;
23492         var ps = this.childNodes[refIndex-1];
23493         if(ps){
23494             node.previousSibling = ps;
23495             ps.nextSibling = node;
23496         }else{
23497             node.previousSibling = null;
23498         }
23499         node.nextSibling = refNode;
23500         refNode.previousSibling = node;
23501         node.setOwnerTree(this.getOwnerTree());
23502         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23503         if(oldParent){
23504             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23505         }
23506         return node;
23507     },
23508
23509     /**
23510      * Returns the child node at the specified index.
23511      * @param {Number} index
23512      * @return {Node}
23513      */
23514     item : function(index){
23515         return this.childNodes[index];
23516     },
23517
23518     /**
23519      * Replaces one child node in this node with another.
23520      * @param {Node} newChild The replacement node
23521      * @param {Node} oldChild The node to replace
23522      * @return {Node} The replaced node
23523      */
23524     replaceChild : function(newChild, oldChild){
23525         this.insertBefore(newChild, oldChild);
23526         this.removeChild(oldChild);
23527         return oldChild;
23528     },
23529
23530     /**
23531      * Returns the index of a child node
23532      * @param {Node} node
23533      * @return {Number} The index of the node or -1 if it was not found
23534      */
23535     indexOf : function(child){
23536         return this.childNodes.indexOf(child);
23537     },
23538
23539     /**
23540      * Returns the tree this node is in.
23541      * @return {Tree}
23542      */
23543     getOwnerTree : function(){
23544         // if it doesn't have one, look for one
23545         if(!this.ownerTree){
23546             var p = this;
23547             while(p){
23548                 if(p.ownerTree){
23549                     this.ownerTree = p.ownerTree;
23550                     break;
23551                 }
23552                 p = p.parentNode;
23553             }
23554         }
23555         return this.ownerTree;
23556     },
23557
23558     /**
23559      * Returns depth of this node (the root node has a depth of 0)
23560      * @return {Number}
23561      */
23562     getDepth : function(){
23563         var depth = 0;
23564         var p = this;
23565         while(p.parentNode){
23566             ++depth;
23567             p = p.parentNode;
23568         }
23569         return depth;
23570     },
23571
23572     // private
23573     setOwnerTree : function(tree){
23574         // if it's move, we need to update everyone
23575         if(tree != this.ownerTree){
23576             if(this.ownerTree){
23577                 this.ownerTree.unregisterNode(this);
23578             }
23579             this.ownerTree = tree;
23580             var cs = this.childNodes;
23581             for(var i = 0, len = cs.length; i < len; i++) {
23582                 cs[i].setOwnerTree(tree);
23583             }
23584             if(tree){
23585                 tree.registerNode(this);
23586             }
23587         }
23588     },
23589
23590     /**
23591      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23592      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23593      * @return {String} The path
23594      */
23595     getPath : function(attr){
23596         attr = attr || "id";
23597         var p = this.parentNode;
23598         var b = [this.attributes[attr]];
23599         while(p){
23600             b.unshift(p.attributes[attr]);
23601             p = p.parentNode;
23602         }
23603         var sep = this.getOwnerTree().pathSeparator;
23604         return sep + b.join(sep);
23605     },
23606
23607     /**
23608      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23609      * function call will be the scope provided or the current node. The arguments to the function
23610      * will be the args provided or the current node. If the function returns false at any point,
23611      * the bubble is stopped.
23612      * @param {Function} fn The function to call
23613      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23614      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23615      */
23616     bubble : function(fn, scope, args){
23617         var p = this;
23618         while(p){
23619             if(fn.call(scope || p, args || p) === false){
23620                 break;
23621             }
23622             p = p.parentNode;
23623         }
23624     },
23625
23626     /**
23627      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23628      * function call will be the scope provided or the current node. The arguments to the function
23629      * will be the args provided or the current node. If the function returns false at any point,
23630      * the cascade is stopped on that branch.
23631      * @param {Function} fn The function to call
23632      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23633      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23634      */
23635     cascade : function(fn, scope, args){
23636         if(fn.call(scope || this, args || this) !== false){
23637             var cs = this.childNodes;
23638             for(var i = 0, len = cs.length; i < len; i++) {
23639                 cs[i].cascade(fn, scope, args);
23640             }
23641         }
23642     },
23643
23644     /**
23645      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23646      * function call will be the scope provided or the current node. The arguments to the function
23647      * will be the args provided or the current node. If the function returns false at any point,
23648      * the iteration stops.
23649      * @param {Function} fn The function to call
23650      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23651      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23652      */
23653     eachChild : function(fn, scope, args){
23654         var cs = this.childNodes;
23655         for(var i = 0, len = cs.length; i < len; i++) {
23656                 if(fn.call(scope || this, args || cs[i]) === false){
23657                     break;
23658                 }
23659         }
23660     },
23661
23662     /**
23663      * Finds the first child that has the attribute with the specified value.
23664      * @param {String} attribute The attribute name
23665      * @param {Mixed} value The value to search for
23666      * @return {Node} The found child or null if none was found
23667      */
23668     findChild : function(attribute, value){
23669         var cs = this.childNodes;
23670         for(var i = 0, len = cs.length; i < len; i++) {
23671                 if(cs[i].attributes[attribute] == value){
23672                     return cs[i];
23673                 }
23674         }
23675         return null;
23676     },
23677
23678     /**
23679      * Finds the first child by a custom function. The child matches if the function passed
23680      * returns true.
23681      * @param {Function} fn
23682      * @param {Object} scope (optional)
23683      * @return {Node} The found child or null if none was found
23684      */
23685     findChildBy : function(fn, scope){
23686         var cs = this.childNodes;
23687         for(var i = 0, len = cs.length; i < len; i++) {
23688                 if(fn.call(scope||cs[i], cs[i]) === true){
23689                     return cs[i];
23690                 }
23691         }
23692         return null;
23693     },
23694
23695     /**
23696      * Sorts this nodes children using the supplied sort function
23697      * @param {Function} fn
23698      * @param {Object} scope (optional)
23699      */
23700     sort : function(fn, scope){
23701         var cs = this.childNodes;
23702         var len = cs.length;
23703         if(len > 0){
23704             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23705             cs.sort(sortFn);
23706             for(var i = 0; i < len; i++){
23707                 var n = cs[i];
23708                 n.previousSibling = cs[i-1];
23709                 n.nextSibling = cs[i+1];
23710                 if(i == 0){
23711                     this.setFirstChild(n);
23712                 }
23713                 if(i == len-1){
23714                     this.setLastChild(n);
23715                 }
23716             }
23717         }
23718     },
23719
23720     /**
23721      * Returns true if this node is an ancestor (at any point) of the passed node.
23722      * @param {Node} node
23723      * @return {Boolean}
23724      */
23725     contains : function(node){
23726         return node.isAncestor(this);
23727     },
23728
23729     /**
23730      * Returns true if the passed node is an ancestor (at any point) of this node.
23731      * @param {Node} node
23732      * @return {Boolean}
23733      */
23734     isAncestor : function(node){
23735         var p = this.parentNode;
23736         while(p){
23737             if(p == node){
23738                 return true;
23739             }
23740             p = p.parentNode;
23741         }
23742         return false;
23743     },
23744
23745     toString : function(){
23746         return "[Node"+(this.id?" "+this.id:"")+"]";
23747     }
23748 });/*
23749  * Based on:
23750  * Ext JS Library 1.1.1
23751  * Copyright(c) 2006-2007, Ext JS, LLC.
23752  *
23753  * Originally Released Under LGPL - original licence link has changed is not relivant.
23754  *
23755  * Fork - LGPL
23756  * <script type="text/javascript">
23757  */
23758  (function(){ 
23759 /**
23760  * @class Roo.Layer
23761  * @extends Roo.Element
23762  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23763  * automatic maintaining of shadow/shim positions.
23764  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23765  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23766  * you can pass a string with a CSS class name. False turns off the shadow.
23767  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23768  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23769  * @cfg {String} cls CSS class to add to the element
23770  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23771  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23772  * @constructor
23773  * @param {Object} config An object with config options.
23774  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23775  */
23776
23777 Roo.Layer = function(config, existingEl){
23778     config = config || {};
23779     var dh = Roo.DomHelper;
23780     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23781     if(existingEl){
23782         this.dom = Roo.getDom(existingEl);
23783     }
23784     if(!this.dom){
23785         var o = config.dh || {tag: "div", cls: "x-layer"};
23786         this.dom = dh.append(pel, o);
23787     }
23788     if(config.cls){
23789         this.addClass(config.cls);
23790     }
23791     this.constrain = config.constrain !== false;
23792     this.visibilityMode = Roo.Element.VISIBILITY;
23793     if(config.id){
23794         this.id = this.dom.id = config.id;
23795     }else{
23796         this.id = Roo.id(this.dom);
23797     }
23798     this.zindex = config.zindex || this.getZIndex();
23799     this.position("absolute", this.zindex);
23800     if(config.shadow){
23801         this.shadowOffset = config.shadowOffset || 4;
23802         this.shadow = new Roo.Shadow({
23803             offset : this.shadowOffset,
23804             mode : config.shadow
23805         });
23806     }else{
23807         this.shadowOffset = 0;
23808     }
23809     this.useShim = config.shim !== false && Roo.useShims;
23810     this.useDisplay = config.useDisplay;
23811     this.hide();
23812 };
23813
23814 var supr = Roo.Element.prototype;
23815
23816 // shims are shared among layer to keep from having 100 iframes
23817 var shims = [];
23818
23819 Roo.extend(Roo.Layer, Roo.Element, {
23820
23821     getZIndex : function(){
23822         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23823     },
23824
23825     getShim : function(){
23826         if(!this.useShim){
23827             return null;
23828         }
23829         if(this.shim){
23830             return this.shim;
23831         }
23832         var shim = shims.shift();
23833         if(!shim){
23834             shim = this.createShim();
23835             shim.enableDisplayMode('block');
23836             shim.dom.style.display = 'none';
23837             shim.dom.style.visibility = 'visible';
23838         }
23839         var pn = this.dom.parentNode;
23840         if(shim.dom.parentNode != pn){
23841             pn.insertBefore(shim.dom, this.dom);
23842         }
23843         shim.setStyle('z-index', this.getZIndex()-2);
23844         this.shim = shim;
23845         return shim;
23846     },
23847
23848     hideShim : function(){
23849         if(this.shim){
23850             this.shim.setDisplayed(false);
23851             shims.push(this.shim);
23852             delete this.shim;
23853         }
23854     },
23855
23856     disableShadow : function(){
23857         if(this.shadow){
23858             this.shadowDisabled = true;
23859             this.shadow.hide();
23860             this.lastShadowOffset = this.shadowOffset;
23861             this.shadowOffset = 0;
23862         }
23863     },
23864
23865     enableShadow : function(show){
23866         if(this.shadow){
23867             this.shadowDisabled = false;
23868             this.shadowOffset = this.lastShadowOffset;
23869             delete this.lastShadowOffset;
23870             if(show){
23871                 this.sync(true);
23872             }
23873         }
23874     },
23875
23876     // private
23877     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23878     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23879     sync : function(doShow){
23880         var sw = this.shadow;
23881         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23882             var sh = this.getShim();
23883
23884             var w = this.getWidth(),
23885                 h = this.getHeight();
23886
23887             var l = this.getLeft(true),
23888                 t = this.getTop(true);
23889
23890             if(sw && !this.shadowDisabled){
23891                 if(doShow && !sw.isVisible()){
23892                     sw.show(this);
23893                 }else{
23894                     sw.realign(l, t, w, h);
23895                 }
23896                 if(sh){
23897                     if(doShow){
23898                        sh.show();
23899                     }
23900                     // fit the shim behind the shadow, so it is shimmed too
23901                     var a = sw.adjusts, s = sh.dom.style;
23902                     s.left = (Math.min(l, l+a.l))+"px";
23903                     s.top = (Math.min(t, t+a.t))+"px";
23904                     s.width = (w+a.w)+"px";
23905                     s.height = (h+a.h)+"px";
23906                 }
23907             }else if(sh){
23908                 if(doShow){
23909                    sh.show();
23910                 }
23911                 sh.setSize(w, h);
23912                 sh.setLeftTop(l, t);
23913             }
23914             
23915         }
23916     },
23917
23918     // private
23919     destroy : function(){
23920         this.hideShim();
23921         if(this.shadow){
23922             this.shadow.hide();
23923         }
23924         this.removeAllListeners();
23925         var pn = this.dom.parentNode;
23926         if(pn){
23927             pn.removeChild(this.dom);
23928         }
23929         Roo.Element.uncache(this.id);
23930     },
23931
23932     remove : function(){
23933         this.destroy();
23934     },
23935
23936     // private
23937     beginUpdate : function(){
23938         this.updating = true;
23939     },
23940
23941     // private
23942     endUpdate : function(){
23943         this.updating = false;
23944         this.sync(true);
23945     },
23946
23947     // private
23948     hideUnders : function(negOffset){
23949         if(this.shadow){
23950             this.shadow.hide();
23951         }
23952         this.hideShim();
23953     },
23954
23955     // private
23956     constrainXY : function(){
23957         if(this.constrain){
23958             var vw = Roo.lib.Dom.getViewWidth(),
23959                 vh = Roo.lib.Dom.getViewHeight();
23960             var s = Roo.get(document).getScroll();
23961
23962             var xy = this.getXY();
23963             var x = xy[0], y = xy[1];   
23964             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23965             // only move it if it needs it
23966             var moved = false;
23967             // first validate right/bottom
23968             if((x + w) > vw+s.left){
23969                 x = vw - w - this.shadowOffset;
23970                 moved = true;
23971             }
23972             if((y + h) > vh+s.top){
23973                 y = vh - h - this.shadowOffset;
23974                 moved = true;
23975             }
23976             // then make sure top/left isn't negative
23977             if(x < s.left){
23978                 x = s.left;
23979                 moved = true;
23980             }
23981             if(y < s.top){
23982                 y = s.top;
23983                 moved = true;
23984             }
23985             if(moved){
23986                 if(this.avoidY){
23987                     var ay = this.avoidY;
23988                     if(y <= ay && (y+h) >= ay){
23989                         y = ay-h-5;   
23990                     }
23991                 }
23992                 xy = [x, y];
23993                 this.storeXY(xy);
23994                 supr.setXY.call(this, xy);
23995                 this.sync();
23996             }
23997         }
23998     },
23999
24000     isVisible : function(){
24001         return this.visible;    
24002     },
24003
24004     // private
24005     showAction : function(){
24006         this.visible = true; // track visibility to prevent getStyle calls
24007         if(this.useDisplay === true){
24008             this.setDisplayed("");
24009         }else if(this.lastXY){
24010             supr.setXY.call(this, this.lastXY);
24011         }else if(this.lastLT){
24012             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
24013         }
24014     },
24015
24016     // private
24017     hideAction : function(){
24018         this.visible = false;
24019         if(this.useDisplay === true){
24020             this.setDisplayed(false);
24021         }else{
24022             this.setLeftTop(-10000,-10000);
24023         }
24024     },
24025
24026     // overridden Element method
24027     setVisible : function(v, a, d, c, e){
24028         if(v){
24029             this.showAction();
24030         }
24031         if(a && v){
24032             var cb = function(){
24033                 this.sync(true);
24034                 if(c){
24035                     c();
24036                 }
24037             }.createDelegate(this);
24038             supr.setVisible.call(this, true, true, d, cb, e);
24039         }else{
24040             if(!v){
24041                 this.hideUnders(true);
24042             }
24043             var cb = c;
24044             if(a){
24045                 cb = function(){
24046                     this.hideAction();
24047                     if(c){
24048                         c();
24049                     }
24050                 }.createDelegate(this);
24051             }
24052             supr.setVisible.call(this, v, a, d, cb, e);
24053             if(v){
24054                 this.sync(true);
24055             }else if(!a){
24056                 this.hideAction();
24057             }
24058         }
24059     },
24060
24061     storeXY : function(xy){
24062         delete this.lastLT;
24063         this.lastXY = xy;
24064     },
24065
24066     storeLeftTop : function(left, top){
24067         delete this.lastXY;
24068         this.lastLT = [left, top];
24069     },
24070
24071     // private
24072     beforeFx : function(){
24073         this.beforeAction();
24074         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
24075     },
24076
24077     // private
24078     afterFx : function(){
24079         Roo.Layer.superclass.afterFx.apply(this, arguments);
24080         this.sync(this.isVisible());
24081     },
24082
24083     // private
24084     beforeAction : function(){
24085         if(!this.updating && this.shadow){
24086             this.shadow.hide();
24087         }
24088     },
24089
24090     // overridden Element method
24091     setLeft : function(left){
24092         this.storeLeftTop(left, this.getTop(true));
24093         supr.setLeft.apply(this, arguments);
24094         this.sync();
24095     },
24096
24097     setTop : function(top){
24098         this.storeLeftTop(this.getLeft(true), top);
24099         supr.setTop.apply(this, arguments);
24100         this.sync();
24101     },
24102
24103     setLeftTop : function(left, top){
24104         this.storeLeftTop(left, top);
24105         supr.setLeftTop.apply(this, arguments);
24106         this.sync();
24107     },
24108
24109     setXY : function(xy, a, d, c, e){
24110         this.fixDisplay();
24111         this.beforeAction();
24112         this.storeXY(xy);
24113         var cb = this.createCB(c);
24114         supr.setXY.call(this, xy, a, d, cb, e);
24115         if(!a){
24116             cb();
24117         }
24118     },
24119
24120     // private
24121     createCB : function(c){
24122         var el = this;
24123         return function(){
24124             el.constrainXY();
24125             el.sync(true);
24126             if(c){
24127                 c();
24128             }
24129         };
24130     },
24131
24132     // overridden Element method
24133     setX : function(x, a, d, c, e){
24134         this.setXY([x, this.getY()], a, d, c, e);
24135     },
24136
24137     // overridden Element method
24138     setY : function(y, a, d, c, e){
24139         this.setXY([this.getX(), y], a, d, c, e);
24140     },
24141
24142     // overridden Element method
24143     setSize : function(w, h, a, d, c, e){
24144         this.beforeAction();
24145         var cb = this.createCB(c);
24146         supr.setSize.call(this, w, h, a, d, cb, e);
24147         if(!a){
24148             cb();
24149         }
24150     },
24151
24152     // overridden Element method
24153     setWidth : function(w, a, d, c, e){
24154         this.beforeAction();
24155         var cb = this.createCB(c);
24156         supr.setWidth.call(this, w, a, d, cb, e);
24157         if(!a){
24158             cb();
24159         }
24160     },
24161
24162     // overridden Element method
24163     setHeight : function(h, a, d, c, e){
24164         this.beforeAction();
24165         var cb = this.createCB(c);
24166         supr.setHeight.call(this, h, a, d, cb, e);
24167         if(!a){
24168             cb();
24169         }
24170     },
24171
24172     // overridden Element method
24173     setBounds : function(x, y, w, h, a, d, c, e){
24174         this.beforeAction();
24175         var cb = this.createCB(c);
24176         if(!a){
24177             this.storeXY([x, y]);
24178             supr.setXY.call(this, [x, y]);
24179             supr.setSize.call(this, w, h, a, d, cb, e);
24180             cb();
24181         }else{
24182             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24183         }
24184         return this;
24185     },
24186     
24187     /**
24188      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24189      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24190      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24191      * @param {Number} zindex The new z-index to set
24192      * @return {this} The Layer
24193      */
24194     setZIndex : function(zindex){
24195         this.zindex = zindex;
24196         this.setStyle("z-index", zindex + 2);
24197         if(this.shadow){
24198             this.shadow.setZIndex(zindex + 1);
24199         }
24200         if(this.shim){
24201             this.shim.setStyle("z-index", zindex);
24202         }
24203     }
24204 });
24205 })();/*
24206  * Based on:
24207  * Ext JS Library 1.1.1
24208  * Copyright(c) 2006-2007, Ext JS, LLC.
24209  *
24210  * Originally Released Under LGPL - original licence link has changed is not relivant.
24211  *
24212  * Fork - LGPL
24213  * <script type="text/javascript">
24214  */
24215
24216
24217 /**
24218  * @class Roo.Shadow
24219  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24220  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24221  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24222  * @constructor
24223  * Create a new Shadow
24224  * @param {Object} config The config object
24225  */
24226 Roo.Shadow = function(config){
24227     Roo.apply(this, config);
24228     if(typeof this.mode != "string"){
24229         this.mode = this.defaultMode;
24230     }
24231     var o = this.offset, a = {h: 0};
24232     var rad = Math.floor(this.offset/2);
24233     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24234         case "drop":
24235             a.w = 0;
24236             a.l = a.t = o;
24237             a.t -= 1;
24238             if(Roo.isIE){
24239                 a.l -= this.offset + rad;
24240                 a.t -= this.offset + rad;
24241                 a.w -= rad;
24242                 a.h -= rad;
24243                 a.t += 1;
24244             }
24245         break;
24246         case "sides":
24247             a.w = (o*2);
24248             a.l = -o;
24249             a.t = o-1;
24250             if(Roo.isIE){
24251                 a.l -= (this.offset - rad);
24252                 a.t -= this.offset + rad;
24253                 a.l += 1;
24254                 a.w -= (this.offset - rad)*2;
24255                 a.w -= rad + 1;
24256                 a.h -= 1;
24257             }
24258         break;
24259         case "frame":
24260             a.w = a.h = (o*2);
24261             a.l = a.t = -o;
24262             a.t += 1;
24263             a.h -= 2;
24264             if(Roo.isIE){
24265                 a.l -= (this.offset - rad);
24266                 a.t -= (this.offset - rad);
24267                 a.l += 1;
24268                 a.w -= (this.offset + rad + 1);
24269                 a.h -= (this.offset + rad);
24270                 a.h += 1;
24271             }
24272         break;
24273     };
24274
24275     this.adjusts = a;
24276 };
24277
24278 Roo.Shadow.prototype = {
24279     /**
24280      * @cfg {String} mode
24281      * The shadow display mode.  Supports the following options:<br />
24282      * sides: Shadow displays on both sides and bottom only<br />
24283      * frame: Shadow displays equally on all four sides<br />
24284      * drop: Traditional bottom-right drop shadow (default)
24285      */
24286     /**
24287      * @cfg {String} offset
24288      * The number of pixels to offset the shadow from the element (defaults to 4)
24289      */
24290     offset: 4,
24291
24292     // private
24293     defaultMode: "drop",
24294
24295     /**
24296      * Displays the shadow under the target element
24297      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24298      */
24299     show : function(target){
24300         target = Roo.get(target);
24301         if(!this.el){
24302             this.el = Roo.Shadow.Pool.pull();
24303             if(this.el.dom.nextSibling != target.dom){
24304                 this.el.insertBefore(target);
24305             }
24306         }
24307         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24308         if(Roo.isIE){
24309             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24310         }
24311         this.realign(
24312             target.getLeft(true),
24313             target.getTop(true),
24314             target.getWidth(),
24315             target.getHeight()
24316         );
24317         this.el.dom.style.display = "block";
24318     },
24319
24320     /**
24321      * Returns true if the shadow is visible, else false
24322      */
24323     isVisible : function(){
24324         return this.el ? true : false;  
24325     },
24326
24327     /**
24328      * Direct alignment when values are already available. Show must be called at least once before
24329      * calling this method to ensure it is initialized.
24330      * @param {Number} left The target element left position
24331      * @param {Number} top The target element top position
24332      * @param {Number} width The target element width
24333      * @param {Number} height The target element height
24334      */
24335     realign : function(l, t, w, h){
24336         if(!this.el){
24337             return;
24338         }
24339         var a = this.adjusts, d = this.el.dom, s = d.style;
24340         var iea = 0;
24341         s.left = (l+a.l)+"px";
24342         s.top = (t+a.t)+"px";
24343         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24344  
24345         if(s.width != sws || s.height != shs){
24346             s.width = sws;
24347             s.height = shs;
24348             if(!Roo.isIE){
24349                 var cn = d.childNodes;
24350                 var sww = Math.max(0, (sw-12))+"px";
24351                 cn[0].childNodes[1].style.width = sww;
24352                 cn[1].childNodes[1].style.width = sww;
24353                 cn[2].childNodes[1].style.width = sww;
24354                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24355             }
24356         }
24357     },
24358
24359     /**
24360      * Hides this shadow
24361      */
24362     hide : function(){
24363         if(this.el){
24364             this.el.dom.style.display = "none";
24365             Roo.Shadow.Pool.push(this.el);
24366             delete this.el;
24367         }
24368     },
24369
24370     /**
24371      * Adjust the z-index of this shadow
24372      * @param {Number} zindex The new z-index
24373      */
24374     setZIndex : function(z){
24375         this.zIndex = z;
24376         if(this.el){
24377             this.el.setStyle("z-index", z);
24378         }
24379     }
24380 };
24381
24382 // Private utility class that manages the internal Shadow cache
24383 Roo.Shadow.Pool = function(){
24384     var p = [];
24385     var markup = Roo.isIE ?
24386                  '<div class="x-ie-shadow"></div>' :
24387                  '<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>';
24388     return {
24389         pull : function(){
24390             var sh = p.shift();
24391             if(!sh){
24392                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24393                 sh.autoBoxAdjust = false;
24394             }
24395             return sh;
24396         },
24397
24398         push : function(sh){
24399             p.push(sh);
24400         }
24401     };
24402 }();/*
24403  * Based on:
24404  * Ext JS Library 1.1.1
24405  * Copyright(c) 2006-2007, Ext JS, LLC.
24406  *
24407  * Originally Released Under LGPL - original licence link has changed is not relivant.
24408  *
24409  * Fork - LGPL
24410  * <script type="text/javascript">
24411  */
24412
24413
24414 /**
24415  * @class Roo.SplitBar
24416  * @extends Roo.util.Observable
24417  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24418  * <br><br>
24419  * Usage:
24420  * <pre><code>
24421 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24422                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24423 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24424 split.minSize = 100;
24425 split.maxSize = 600;
24426 split.animate = true;
24427 split.on('moved', splitterMoved);
24428 </code></pre>
24429  * @constructor
24430  * Create a new SplitBar
24431  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24432  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24433  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24434  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24435                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24436                         position of the SplitBar).
24437  */
24438 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24439     
24440     /** @private */
24441     this.el = Roo.get(dragElement, true);
24442     this.el.dom.unselectable = "on";
24443     /** @private */
24444     this.resizingEl = Roo.get(resizingElement, true);
24445
24446     /**
24447      * @private
24448      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24449      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24450      * @type Number
24451      */
24452     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24453     
24454     /**
24455      * The minimum size of the resizing element. (Defaults to 0)
24456      * @type Number
24457      */
24458     this.minSize = 0;
24459     
24460     /**
24461      * The maximum size of the resizing element. (Defaults to 2000)
24462      * @type Number
24463      */
24464     this.maxSize = 2000;
24465     
24466     /**
24467      * Whether to animate the transition to the new size
24468      * @type Boolean
24469      */
24470     this.animate = false;
24471     
24472     /**
24473      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24474      * @type Boolean
24475      */
24476     this.useShim = false;
24477     
24478     /** @private */
24479     this.shim = null;
24480     
24481     if(!existingProxy){
24482         /** @private */
24483         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24484     }else{
24485         this.proxy = Roo.get(existingProxy).dom;
24486     }
24487     /** @private */
24488     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24489     
24490     /** @private */
24491     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24492     
24493     /** @private */
24494     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24495     
24496     /** @private */
24497     this.dragSpecs = {};
24498     
24499     /**
24500      * @private The adapter to use to positon and resize elements
24501      */
24502     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24503     this.adapter.init(this);
24504     
24505     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24506         /** @private */
24507         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24508         this.el.addClass("x-splitbar-h");
24509     }else{
24510         /** @private */
24511         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24512         this.el.addClass("x-splitbar-v");
24513     }
24514     
24515     this.addEvents({
24516         /**
24517          * @event resize
24518          * Fires when the splitter is moved (alias for {@link #event-moved})
24519          * @param {Roo.SplitBar} this
24520          * @param {Number} newSize the new width or height
24521          */
24522         "resize" : true,
24523         /**
24524          * @event moved
24525          * Fires when the splitter is moved
24526          * @param {Roo.SplitBar} this
24527          * @param {Number} newSize the new width or height
24528          */
24529         "moved" : true,
24530         /**
24531          * @event beforeresize
24532          * Fires before the splitter is dragged
24533          * @param {Roo.SplitBar} this
24534          */
24535         "beforeresize" : true,
24536
24537         "beforeapply" : true
24538     });
24539
24540     Roo.util.Observable.call(this);
24541 };
24542
24543 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24544     onStartProxyDrag : function(x, y){
24545         this.fireEvent("beforeresize", this);
24546         if(!this.overlay){
24547             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24548             o.unselectable();
24549             o.enableDisplayMode("block");
24550             // all splitbars share the same overlay
24551             Roo.SplitBar.prototype.overlay = o;
24552         }
24553         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24554         this.overlay.show();
24555         Roo.get(this.proxy).setDisplayed("block");
24556         var size = this.adapter.getElementSize(this);
24557         this.activeMinSize = this.getMinimumSize();;
24558         this.activeMaxSize = this.getMaximumSize();;
24559         var c1 = size - this.activeMinSize;
24560         var c2 = Math.max(this.activeMaxSize - size, 0);
24561         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24562             this.dd.resetConstraints();
24563             this.dd.setXConstraint(
24564                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24565                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24566             );
24567             this.dd.setYConstraint(0, 0);
24568         }else{
24569             this.dd.resetConstraints();
24570             this.dd.setXConstraint(0, 0);
24571             this.dd.setYConstraint(
24572                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24573                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24574             );
24575          }
24576         this.dragSpecs.startSize = size;
24577         this.dragSpecs.startPoint = [x, y];
24578         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24579     },
24580     
24581     /** 
24582      * @private Called after the drag operation by the DDProxy
24583      */
24584     onEndProxyDrag : function(e){
24585         Roo.get(this.proxy).setDisplayed(false);
24586         var endPoint = Roo.lib.Event.getXY(e);
24587         if(this.overlay){
24588             this.overlay.hide();
24589         }
24590         var newSize;
24591         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24592             newSize = this.dragSpecs.startSize + 
24593                 (this.placement == Roo.SplitBar.LEFT ?
24594                     endPoint[0] - this.dragSpecs.startPoint[0] :
24595                     this.dragSpecs.startPoint[0] - endPoint[0]
24596                 );
24597         }else{
24598             newSize = this.dragSpecs.startSize + 
24599                 (this.placement == Roo.SplitBar.TOP ?
24600                     endPoint[1] - this.dragSpecs.startPoint[1] :
24601                     this.dragSpecs.startPoint[1] - endPoint[1]
24602                 );
24603         }
24604         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24605         if(newSize != this.dragSpecs.startSize){
24606             if(this.fireEvent('beforeapply', this, newSize) !== false){
24607                 this.adapter.setElementSize(this, newSize);
24608                 this.fireEvent("moved", this, newSize);
24609                 this.fireEvent("resize", this, newSize);
24610             }
24611         }
24612     },
24613     
24614     /**
24615      * Get the adapter this SplitBar uses
24616      * @return The adapter object
24617      */
24618     getAdapter : function(){
24619         return this.adapter;
24620     },
24621     
24622     /**
24623      * Set the adapter this SplitBar uses
24624      * @param {Object} adapter A SplitBar adapter object
24625      */
24626     setAdapter : function(adapter){
24627         this.adapter = adapter;
24628         this.adapter.init(this);
24629     },
24630     
24631     /**
24632      * Gets the minimum size for the resizing element
24633      * @return {Number} The minimum size
24634      */
24635     getMinimumSize : function(){
24636         return this.minSize;
24637     },
24638     
24639     /**
24640      * Sets the minimum size for the resizing element
24641      * @param {Number} minSize The minimum size
24642      */
24643     setMinimumSize : function(minSize){
24644         this.minSize = minSize;
24645     },
24646     
24647     /**
24648      * Gets the maximum size for the resizing element
24649      * @return {Number} The maximum size
24650      */
24651     getMaximumSize : function(){
24652         return this.maxSize;
24653     },
24654     
24655     /**
24656      * Sets the maximum size for the resizing element
24657      * @param {Number} maxSize The maximum size
24658      */
24659     setMaximumSize : function(maxSize){
24660         this.maxSize = maxSize;
24661     },
24662     
24663     /**
24664      * Sets the initialize size for the resizing element
24665      * @param {Number} size The initial size
24666      */
24667     setCurrentSize : function(size){
24668         var oldAnimate = this.animate;
24669         this.animate = false;
24670         this.adapter.setElementSize(this, size);
24671         this.animate = oldAnimate;
24672     },
24673     
24674     /**
24675      * Destroy this splitbar. 
24676      * @param {Boolean} removeEl True to remove the element
24677      */
24678     destroy : function(removeEl){
24679         if(this.shim){
24680             this.shim.remove();
24681         }
24682         this.dd.unreg();
24683         this.proxy.parentNode.removeChild(this.proxy);
24684         if(removeEl){
24685             this.el.remove();
24686         }
24687     }
24688 });
24689
24690 /**
24691  * @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.
24692  */
24693 Roo.SplitBar.createProxy = function(dir){
24694     var proxy = new Roo.Element(document.createElement("div"));
24695     proxy.unselectable();
24696     var cls = 'x-splitbar-proxy';
24697     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24698     document.body.appendChild(proxy.dom);
24699     return proxy.dom;
24700 };
24701
24702 /** 
24703  * @class Roo.SplitBar.BasicLayoutAdapter
24704  * Default Adapter. It assumes the splitter and resizing element are not positioned
24705  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24706  */
24707 Roo.SplitBar.BasicLayoutAdapter = function(){
24708 };
24709
24710 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24711     // do nothing for now
24712     init : function(s){
24713     
24714     },
24715     /**
24716      * Called before drag operations to get the current size of the resizing element. 
24717      * @param {Roo.SplitBar} s The SplitBar using this adapter
24718      */
24719      getElementSize : function(s){
24720         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24721             return s.resizingEl.getWidth();
24722         }else{
24723             return s.resizingEl.getHeight();
24724         }
24725     },
24726     
24727     /**
24728      * Called after drag operations to set the size of the resizing element.
24729      * @param {Roo.SplitBar} s The SplitBar using this adapter
24730      * @param {Number} newSize The new size to set
24731      * @param {Function} onComplete A function to be invoked when resizing is complete
24732      */
24733     setElementSize : function(s, newSize, onComplete){
24734         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24735             if(!s.animate){
24736                 s.resizingEl.setWidth(newSize);
24737                 if(onComplete){
24738                     onComplete(s, newSize);
24739                 }
24740             }else{
24741                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24742             }
24743         }else{
24744             
24745             if(!s.animate){
24746                 s.resizingEl.setHeight(newSize);
24747                 if(onComplete){
24748                     onComplete(s, newSize);
24749                 }
24750             }else{
24751                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24752             }
24753         }
24754     }
24755 };
24756
24757 /** 
24758  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24759  * @extends Roo.SplitBar.BasicLayoutAdapter
24760  * Adapter that  moves the splitter element to align with the resized sizing element. 
24761  * Used with an absolute positioned SplitBar.
24762  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24763  * document.body, make sure you assign an id to the body element.
24764  */
24765 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24766     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24767     this.container = Roo.get(container);
24768 };
24769
24770 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24771     init : function(s){
24772         this.basic.init(s);
24773     },
24774     
24775     getElementSize : function(s){
24776         return this.basic.getElementSize(s);
24777     },
24778     
24779     setElementSize : function(s, newSize, onComplete){
24780         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24781     },
24782     
24783     moveSplitter : function(s){
24784         var yes = Roo.SplitBar;
24785         switch(s.placement){
24786             case yes.LEFT:
24787                 s.el.setX(s.resizingEl.getRight());
24788                 break;
24789             case yes.RIGHT:
24790                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24791                 break;
24792             case yes.TOP:
24793                 s.el.setY(s.resizingEl.getBottom());
24794                 break;
24795             case yes.BOTTOM:
24796                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24797                 break;
24798         }
24799     }
24800 };
24801
24802 /**
24803  * Orientation constant - Create a vertical SplitBar
24804  * @static
24805  * @type Number
24806  */
24807 Roo.SplitBar.VERTICAL = 1;
24808
24809 /**
24810  * Orientation constant - Create a horizontal SplitBar
24811  * @static
24812  * @type Number
24813  */
24814 Roo.SplitBar.HORIZONTAL = 2;
24815
24816 /**
24817  * Placement constant - The resizing element is to the left of the splitter element
24818  * @static
24819  * @type Number
24820  */
24821 Roo.SplitBar.LEFT = 1;
24822
24823 /**
24824  * Placement constant - The resizing element is to the right of the splitter element
24825  * @static
24826  * @type Number
24827  */
24828 Roo.SplitBar.RIGHT = 2;
24829
24830 /**
24831  * Placement constant - The resizing element is positioned above the splitter element
24832  * @static
24833  * @type Number
24834  */
24835 Roo.SplitBar.TOP = 3;
24836
24837 /**
24838  * Placement constant - The resizing element is positioned under splitter element
24839  * @static
24840  * @type Number
24841  */
24842 Roo.SplitBar.BOTTOM = 4;
24843 /*
24844  * Based on:
24845  * Ext JS Library 1.1.1
24846  * Copyright(c) 2006-2007, Ext JS, LLC.
24847  *
24848  * Originally Released Under LGPL - original licence link has changed is not relivant.
24849  *
24850  * Fork - LGPL
24851  * <script type="text/javascript">
24852  */
24853
24854 /**
24855  * @class Roo.View
24856  * @extends Roo.util.Observable
24857  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24858  * This class also supports single and multi selection modes. <br>
24859  * Create a data model bound view:
24860  <pre><code>
24861  var store = new Roo.data.Store(...);
24862
24863  var view = new Roo.View({
24864     el : "my-element",
24865     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24866  
24867     singleSelect: true,
24868     selectedClass: "ydataview-selected",
24869     store: store
24870  });
24871
24872  // listen for node click?
24873  view.on("click", function(vw, index, node, e){
24874  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24875  });
24876
24877  // load XML data
24878  dataModel.load("foobar.xml");
24879  </code></pre>
24880  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24881  * <br><br>
24882  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24883  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24884  * 
24885  * Note: old style constructor is still suported (container, template, config)
24886  * 
24887  * @constructor
24888  * Create a new View
24889  * @param {Object} config The config object
24890  * 
24891  */
24892 Roo.View = function(config, depreciated_tpl, depreciated_config){
24893     
24894     this.parent = false;
24895     
24896     if (typeof(depreciated_tpl) == 'undefined') {
24897         // new way.. - universal constructor.
24898         Roo.apply(this, config);
24899         this.el  = Roo.get(this.el);
24900     } else {
24901         // old format..
24902         this.el  = Roo.get(config);
24903         this.tpl = depreciated_tpl;
24904         Roo.apply(this, depreciated_config);
24905     }
24906     this.wrapEl  = this.el.wrap().wrap();
24907     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24908     
24909     
24910     if(typeof(this.tpl) == "string"){
24911         this.tpl = new Roo.Template(this.tpl);
24912     } else {
24913         // support xtype ctors..
24914         this.tpl = new Roo.factory(this.tpl, Roo);
24915     }
24916     
24917     
24918     this.tpl.compile();
24919     
24920     /** @private */
24921     this.addEvents({
24922         /**
24923          * @event beforeclick
24924          * Fires before a click is processed. Returns false to cancel the default action.
24925          * @param {Roo.View} this
24926          * @param {Number} index The index of the target node
24927          * @param {HTMLElement} node The target node
24928          * @param {Roo.EventObject} e The raw event object
24929          */
24930             "beforeclick" : true,
24931         /**
24932          * @event click
24933          * Fires when a template node is clicked.
24934          * @param {Roo.View} this
24935          * @param {Number} index The index of the target node
24936          * @param {HTMLElement} node The target node
24937          * @param {Roo.EventObject} e The raw event object
24938          */
24939             "click" : true,
24940         /**
24941          * @event dblclick
24942          * Fires when a template node is double clicked.
24943          * @param {Roo.View} this
24944          * @param {Number} index The index of the target node
24945          * @param {HTMLElement} node The target node
24946          * @param {Roo.EventObject} e The raw event object
24947          */
24948             "dblclick" : true,
24949         /**
24950          * @event contextmenu
24951          * Fires when a template node is right clicked.
24952          * @param {Roo.View} this
24953          * @param {Number} index The index of the target node
24954          * @param {HTMLElement} node The target node
24955          * @param {Roo.EventObject} e The raw event object
24956          */
24957             "contextmenu" : true,
24958         /**
24959          * @event selectionchange
24960          * Fires when the selected nodes change.
24961          * @param {Roo.View} this
24962          * @param {Array} selections Array of the selected nodes
24963          */
24964             "selectionchange" : true,
24965     
24966         /**
24967          * @event beforeselect
24968          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24969          * @param {Roo.View} this
24970          * @param {HTMLElement} node The node to be selected
24971          * @param {Array} selections Array of currently selected nodes
24972          */
24973             "beforeselect" : true,
24974         /**
24975          * @event preparedata
24976          * Fires on every row to render, to allow you to change the data.
24977          * @param {Roo.View} this
24978          * @param {Object} data to be rendered (change this)
24979          */
24980           "preparedata" : true
24981           
24982           
24983         });
24984
24985
24986
24987     this.el.on({
24988         "click": this.onClick,
24989         "dblclick": this.onDblClick,
24990         "contextmenu": this.onContextMenu,
24991         scope:this
24992     });
24993
24994     this.selections = [];
24995     this.nodes = [];
24996     this.cmp = new Roo.CompositeElementLite([]);
24997     if(this.store){
24998         this.store = Roo.factory(this.store, Roo.data);
24999         this.setStore(this.store, true);
25000     }
25001     
25002     if ( this.footer && this.footer.xtype) {
25003            
25004          var fctr = this.wrapEl.appendChild(document.createElement("div"));
25005         
25006         this.footer.dataSource = this.store;
25007         this.footer.container = fctr;
25008         this.footer = Roo.factory(this.footer, Roo);
25009         fctr.insertFirst(this.el);
25010         
25011         // this is a bit insane - as the paging toolbar seems to detach the el..
25012 //        dom.parentNode.parentNode.parentNode
25013          // they get detached?
25014     }
25015     
25016     
25017     Roo.View.superclass.constructor.call(this);
25018     
25019     
25020 };
25021
25022 Roo.extend(Roo.View, Roo.util.Observable, {
25023     
25024      /**
25025      * @cfg {Roo.data.Store} store Data store to load data from.
25026      */
25027     store : false,
25028     
25029     /**
25030      * @cfg {String|Roo.Element} el The container element.
25031      */
25032     el : '',
25033     
25034     /**
25035      * @cfg {String|Roo.Template} tpl The template used by this View 
25036      */
25037     tpl : false,
25038     /**
25039      * @cfg {String} dataName the named area of the template to use as the data area
25040      *                          Works with domtemplates roo-name="name"
25041      */
25042     dataName: false,
25043     /**
25044      * @cfg {String} selectedClass The css class to add to selected nodes
25045      */
25046     selectedClass : "x-view-selected",
25047      /**
25048      * @cfg {String} emptyText The empty text to show when nothing is loaded.
25049      */
25050     emptyText : "",
25051     
25052     /**
25053      * @cfg {String} text to display on mask (default Loading)
25054      */
25055     mask : false,
25056     /**
25057      * @cfg {Boolean} multiSelect Allow multiple selection
25058      */
25059     multiSelect : false,
25060     /**
25061      * @cfg {Boolean} singleSelect Allow single selection
25062      */
25063     singleSelect:  false,
25064     
25065     /**
25066      * @cfg {Boolean} toggleSelect - selecting 
25067      */
25068     toggleSelect : false,
25069     
25070     /**
25071      * @cfg {Boolean} tickable - selecting 
25072      */
25073     tickable : false,
25074     
25075     /**
25076      * Returns the element this view is bound to.
25077      * @return {Roo.Element}
25078      */
25079     getEl : function(){
25080         return this.wrapEl;
25081     },
25082     
25083     
25084
25085     /**
25086      * Refreshes the view. - called by datachanged on the store. - do not call directly.
25087      */
25088     refresh : function(){
25089         //Roo.log('refresh');
25090         var t = this.tpl;
25091         
25092         // if we are using something like 'domtemplate', then
25093         // the what gets used is:
25094         // t.applySubtemplate(NAME, data, wrapping data..)
25095         // the outer template then get' applied with
25096         //     the store 'extra data'
25097         // and the body get's added to the
25098         //      roo-name="data" node?
25099         //      <span class='roo-tpl-{name}'></span> ?????
25100         
25101         
25102         
25103         this.clearSelections();
25104         this.el.update("");
25105         var html = [];
25106         var records = this.store.getRange();
25107         if(records.length < 1) {
25108             
25109             // is this valid??  = should it render a template??
25110             
25111             this.el.update(this.emptyText);
25112             return;
25113         }
25114         var el = this.el;
25115         if (this.dataName) {
25116             this.el.update(t.apply(this.store.meta)); //????
25117             el = this.el.child('.roo-tpl-' + this.dataName);
25118         }
25119         
25120         for(var i = 0, len = records.length; i < len; i++){
25121             var data = this.prepareData(records[i].data, i, records[i]);
25122             this.fireEvent("preparedata", this, data, i, records[i]);
25123             
25124             var d = Roo.apply({}, data);
25125             
25126             if(this.tickable){
25127                 Roo.apply(d, {'roo-id' : Roo.id()});
25128                 
25129                 var _this = this;
25130             
25131                 Roo.each(this.parent.item, function(item){
25132                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
25133                         return;
25134                     }
25135                     Roo.apply(d, {'roo-data-checked' : 'checked'});
25136                 });
25137             }
25138             
25139             html[html.length] = Roo.util.Format.trim(
25140                 this.dataName ?
25141                     t.applySubtemplate(this.dataName, d, this.store.meta) :
25142                     t.apply(d)
25143             );
25144         }
25145         
25146         
25147         
25148         el.update(html.join(""));
25149         this.nodes = el.dom.childNodes;
25150         this.updateIndexes(0);
25151     },
25152     
25153
25154     /**
25155      * Function to override to reformat the data that is sent to
25156      * the template for each node.
25157      * DEPRICATED - use the preparedata event handler.
25158      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
25159      * a JSON object for an UpdateManager bound view).
25160      */
25161     prepareData : function(data, index, record)
25162     {
25163         this.fireEvent("preparedata", this, data, index, record);
25164         return data;
25165     },
25166
25167     onUpdate : function(ds, record){
25168         // Roo.log('on update');   
25169         this.clearSelections();
25170         var index = this.store.indexOf(record);
25171         var n = this.nodes[index];
25172         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25173         n.parentNode.removeChild(n);
25174         this.updateIndexes(index, index);
25175     },
25176
25177     
25178     
25179 // --------- FIXME     
25180     onAdd : function(ds, records, index)
25181     {
25182         //Roo.log(['on Add', ds, records, index] );        
25183         this.clearSelections();
25184         if(this.nodes.length == 0){
25185             this.refresh();
25186             return;
25187         }
25188         var n = this.nodes[index];
25189         for(var i = 0, len = records.length; i < len; i++){
25190             var d = this.prepareData(records[i].data, i, records[i]);
25191             if(n){
25192                 this.tpl.insertBefore(n, d);
25193             }else{
25194                 
25195                 this.tpl.append(this.el, d);
25196             }
25197         }
25198         this.updateIndexes(index);
25199     },
25200
25201     onRemove : function(ds, record, index){
25202        // Roo.log('onRemove');
25203         this.clearSelections();
25204         var el = this.dataName  ?
25205             this.el.child('.roo-tpl-' + this.dataName) :
25206             this.el; 
25207         
25208         el.dom.removeChild(this.nodes[index]);
25209         this.updateIndexes(index);
25210     },
25211
25212     /**
25213      * Refresh an individual node.
25214      * @param {Number} index
25215      */
25216     refreshNode : function(index){
25217         this.onUpdate(this.store, this.store.getAt(index));
25218     },
25219
25220     updateIndexes : function(startIndex, endIndex){
25221         var ns = this.nodes;
25222         startIndex = startIndex || 0;
25223         endIndex = endIndex || ns.length - 1;
25224         for(var i = startIndex; i <= endIndex; i++){
25225             ns[i].nodeIndex = i;
25226         }
25227     },
25228
25229     /**
25230      * Changes the data store this view uses and refresh the view.
25231      * @param {Store} store
25232      */
25233     setStore : function(store, initial){
25234         if(!initial && this.store){
25235             this.store.un("datachanged", this.refresh);
25236             this.store.un("add", this.onAdd);
25237             this.store.un("remove", this.onRemove);
25238             this.store.un("update", this.onUpdate);
25239             this.store.un("clear", this.refresh);
25240             this.store.un("beforeload", this.onBeforeLoad);
25241             this.store.un("load", this.onLoad);
25242             this.store.un("loadexception", this.onLoad);
25243         }
25244         if(store){
25245           
25246             store.on("datachanged", this.refresh, this);
25247             store.on("add", this.onAdd, this);
25248             store.on("remove", this.onRemove, this);
25249             store.on("update", this.onUpdate, this);
25250             store.on("clear", this.refresh, this);
25251             store.on("beforeload", this.onBeforeLoad, this);
25252             store.on("load", this.onLoad, this);
25253             store.on("loadexception", this.onLoad, this);
25254         }
25255         
25256         if(store){
25257             this.refresh();
25258         }
25259     },
25260     /**
25261      * onbeforeLoad - masks the loading area.
25262      *
25263      */
25264     onBeforeLoad : function(store,opts)
25265     {
25266          //Roo.log('onBeforeLoad');   
25267         if (!opts.add) {
25268             this.el.update("");
25269         }
25270         this.el.mask(this.mask ? this.mask : "Loading" ); 
25271     },
25272     onLoad : function ()
25273     {
25274         this.el.unmask();
25275     },
25276     
25277
25278     /**
25279      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25280      * @param {HTMLElement} node
25281      * @return {HTMLElement} The template node
25282      */
25283     findItemFromChild : function(node){
25284         var el = this.dataName  ?
25285             this.el.child('.roo-tpl-' + this.dataName,true) :
25286             this.el.dom; 
25287         
25288         if(!node || node.parentNode == el){
25289                     return node;
25290             }
25291             var p = node.parentNode;
25292             while(p && p != el){
25293             if(p.parentNode == el){
25294                 return p;
25295             }
25296             p = p.parentNode;
25297         }
25298             return null;
25299     },
25300
25301     /** @ignore */
25302     onClick : function(e){
25303         var item = this.findItemFromChild(e.getTarget());
25304         if(item){
25305             var index = this.indexOf(item);
25306             if(this.onItemClick(item, index, e) !== false){
25307                 this.fireEvent("click", this, index, item, e);
25308             }
25309         }else{
25310             this.clearSelections();
25311         }
25312     },
25313
25314     /** @ignore */
25315     onContextMenu : function(e){
25316         var item = this.findItemFromChild(e.getTarget());
25317         if(item){
25318             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25319         }
25320     },
25321
25322     /** @ignore */
25323     onDblClick : function(e){
25324         var item = this.findItemFromChild(e.getTarget());
25325         if(item){
25326             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25327         }
25328     },
25329
25330     onItemClick : function(item, index, e)
25331     {
25332         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25333             return false;
25334         }
25335         if (this.toggleSelect) {
25336             var m = this.isSelected(item) ? 'unselect' : 'select';
25337             //Roo.log(m);
25338             var _t = this;
25339             _t[m](item, true, false);
25340             return true;
25341         }
25342         if(this.multiSelect || this.singleSelect){
25343             if(this.multiSelect && e.shiftKey && this.lastSelection){
25344                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25345             }else{
25346                 this.select(item, this.multiSelect && e.ctrlKey);
25347                 this.lastSelection = item;
25348             }
25349             
25350             if(!this.tickable){
25351                 e.preventDefault();
25352             }
25353             
25354         }
25355         return true;
25356     },
25357
25358     /**
25359      * Get the number of selected nodes.
25360      * @return {Number}
25361      */
25362     getSelectionCount : function(){
25363         return this.selections.length;
25364     },
25365
25366     /**
25367      * Get the currently selected nodes.
25368      * @return {Array} An array of HTMLElements
25369      */
25370     getSelectedNodes : function(){
25371         return this.selections;
25372     },
25373
25374     /**
25375      * Get the indexes of the selected nodes.
25376      * @return {Array}
25377      */
25378     getSelectedIndexes : function(){
25379         var indexes = [], s = this.selections;
25380         for(var i = 0, len = s.length; i < len; i++){
25381             indexes.push(s[i].nodeIndex);
25382         }
25383         return indexes;
25384     },
25385
25386     /**
25387      * Clear all selections
25388      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25389      */
25390     clearSelections : function(suppressEvent){
25391         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25392             this.cmp.elements = this.selections;
25393             this.cmp.removeClass(this.selectedClass);
25394             this.selections = [];
25395             if(!suppressEvent){
25396                 this.fireEvent("selectionchange", this, this.selections);
25397             }
25398         }
25399     },
25400
25401     /**
25402      * Returns true if the passed node is selected
25403      * @param {HTMLElement/Number} node The node or node index
25404      * @return {Boolean}
25405      */
25406     isSelected : function(node){
25407         var s = this.selections;
25408         if(s.length < 1){
25409             return false;
25410         }
25411         node = this.getNode(node);
25412         return s.indexOf(node) !== -1;
25413     },
25414
25415     /**
25416      * Selects nodes.
25417      * @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
25418      * @param {Boolean} keepExisting (optional) true to keep existing selections
25419      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25420      */
25421     select : function(nodeInfo, keepExisting, suppressEvent){
25422         if(nodeInfo instanceof Array){
25423             if(!keepExisting){
25424                 this.clearSelections(true);
25425             }
25426             for(var i = 0, len = nodeInfo.length; i < len; i++){
25427                 this.select(nodeInfo[i], true, true);
25428             }
25429             return;
25430         } 
25431         var node = this.getNode(nodeInfo);
25432         if(!node || this.isSelected(node)){
25433             return; // already selected.
25434         }
25435         if(!keepExisting){
25436             this.clearSelections(true);
25437         }
25438         
25439         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25440             Roo.fly(node).addClass(this.selectedClass);
25441             this.selections.push(node);
25442             if(!suppressEvent){
25443                 this.fireEvent("selectionchange", this, this.selections);
25444             }
25445         }
25446         
25447         
25448     },
25449       /**
25450      * Unselects nodes.
25451      * @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
25452      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25453      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25454      */
25455     unselect : function(nodeInfo, keepExisting, suppressEvent)
25456     {
25457         if(nodeInfo instanceof Array){
25458             Roo.each(this.selections, function(s) {
25459                 this.unselect(s, nodeInfo);
25460             }, this);
25461             return;
25462         }
25463         var node = this.getNode(nodeInfo);
25464         if(!node || !this.isSelected(node)){
25465             //Roo.log("not selected");
25466             return; // not selected.
25467         }
25468         // fireevent???
25469         var ns = [];
25470         Roo.each(this.selections, function(s) {
25471             if (s == node ) {
25472                 Roo.fly(node).removeClass(this.selectedClass);
25473
25474                 return;
25475             }
25476             ns.push(s);
25477         },this);
25478         
25479         this.selections= ns;
25480         this.fireEvent("selectionchange", this, this.selections);
25481     },
25482
25483     /**
25484      * Gets a template node.
25485      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25486      * @return {HTMLElement} The node or null if it wasn't found
25487      */
25488     getNode : function(nodeInfo){
25489         if(typeof nodeInfo == "string"){
25490             return document.getElementById(nodeInfo);
25491         }else if(typeof nodeInfo == "number"){
25492             return this.nodes[nodeInfo];
25493         }
25494         return nodeInfo;
25495     },
25496
25497     /**
25498      * Gets a range template nodes.
25499      * @param {Number} startIndex
25500      * @param {Number} endIndex
25501      * @return {Array} An array of nodes
25502      */
25503     getNodes : function(start, end){
25504         var ns = this.nodes;
25505         start = start || 0;
25506         end = typeof end == "undefined" ? ns.length - 1 : end;
25507         var nodes = [];
25508         if(start <= end){
25509             for(var i = start; i <= end; i++){
25510                 nodes.push(ns[i]);
25511             }
25512         } else{
25513             for(var i = start; i >= end; i--){
25514                 nodes.push(ns[i]);
25515             }
25516         }
25517         return nodes;
25518     },
25519
25520     /**
25521      * Finds the index of the passed node
25522      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25523      * @return {Number} The index of the node or -1
25524      */
25525     indexOf : function(node){
25526         node = this.getNode(node);
25527         if(typeof node.nodeIndex == "number"){
25528             return node.nodeIndex;
25529         }
25530         var ns = this.nodes;
25531         for(var i = 0, len = ns.length; i < len; i++){
25532             if(ns[i] == node){
25533                 return i;
25534             }
25535         }
25536         return -1;
25537     }
25538 });
25539 /*
25540  * Based on:
25541  * Ext JS Library 1.1.1
25542  * Copyright(c) 2006-2007, Ext JS, LLC.
25543  *
25544  * Originally Released Under LGPL - original licence link has changed is not relivant.
25545  *
25546  * Fork - LGPL
25547  * <script type="text/javascript">
25548  */
25549
25550 /**
25551  * @class Roo.JsonView
25552  * @extends Roo.View
25553  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25554 <pre><code>
25555 var view = new Roo.JsonView({
25556     container: "my-element",
25557     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25558     multiSelect: true, 
25559     jsonRoot: "data" 
25560 });
25561
25562 // listen for node click?
25563 view.on("click", function(vw, index, node, e){
25564     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25565 });
25566
25567 // direct load of JSON data
25568 view.load("foobar.php");
25569
25570 // Example from my blog list
25571 var tpl = new Roo.Template(
25572     '&lt;div class="entry"&gt;' +
25573     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25574     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25575     "&lt;/div&gt;&lt;hr /&gt;"
25576 );
25577
25578 var moreView = new Roo.JsonView({
25579     container :  "entry-list", 
25580     template : tpl,
25581     jsonRoot: "posts"
25582 });
25583 moreView.on("beforerender", this.sortEntries, this);
25584 moreView.load({
25585     url: "/blog/get-posts.php",
25586     params: "allposts=true",
25587     text: "Loading Blog Entries..."
25588 });
25589 </code></pre>
25590
25591 * Note: old code is supported with arguments : (container, template, config)
25592
25593
25594  * @constructor
25595  * Create a new JsonView
25596  * 
25597  * @param {Object} config The config object
25598  * 
25599  */
25600 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25601     
25602     
25603     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25604
25605     var um = this.el.getUpdateManager();
25606     um.setRenderer(this);
25607     um.on("update", this.onLoad, this);
25608     um.on("failure", this.onLoadException, this);
25609
25610     /**
25611      * @event beforerender
25612      * Fires before rendering of the downloaded JSON data.
25613      * @param {Roo.JsonView} this
25614      * @param {Object} data The JSON data loaded
25615      */
25616     /**
25617      * @event load
25618      * Fires when data is loaded.
25619      * @param {Roo.JsonView} this
25620      * @param {Object} data The JSON data loaded
25621      * @param {Object} response The raw Connect response object
25622      */
25623     /**
25624      * @event loadexception
25625      * Fires when loading fails.
25626      * @param {Roo.JsonView} this
25627      * @param {Object} response The raw Connect response object
25628      */
25629     this.addEvents({
25630         'beforerender' : true,
25631         'load' : true,
25632         'loadexception' : true
25633     });
25634 };
25635 Roo.extend(Roo.JsonView, Roo.View, {
25636     /**
25637      * @type {String} The root property in the loaded JSON object that contains the data
25638      */
25639     jsonRoot : "",
25640
25641     /**
25642      * Refreshes the view.
25643      */
25644     refresh : function(){
25645         this.clearSelections();
25646         this.el.update("");
25647         var html = [];
25648         var o = this.jsonData;
25649         if(o && o.length > 0){
25650             for(var i = 0, len = o.length; i < len; i++){
25651                 var data = this.prepareData(o[i], i, o);
25652                 html[html.length] = this.tpl.apply(data);
25653             }
25654         }else{
25655             html.push(this.emptyText);
25656         }
25657         this.el.update(html.join(""));
25658         this.nodes = this.el.dom.childNodes;
25659         this.updateIndexes(0);
25660     },
25661
25662     /**
25663      * 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.
25664      * @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:
25665      <pre><code>
25666      view.load({
25667          url: "your-url.php",
25668          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25669          callback: yourFunction,
25670          scope: yourObject, //(optional scope)
25671          discardUrl: false,
25672          nocache: false,
25673          text: "Loading...",
25674          timeout: 30,
25675          scripts: false
25676      });
25677      </code></pre>
25678      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25679      * 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.
25680      * @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}
25681      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25682      * @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.
25683      */
25684     load : function(){
25685         var um = this.el.getUpdateManager();
25686         um.update.apply(um, arguments);
25687     },
25688
25689     render : function(el, response){
25690         this.clearSelections();
25691         this.el.update("");
25692         var o;
25693         try{
25694             o = Roo.util.JSON.decode(response.responseText);
25695             if(this.jsonRoot){
25696                 
25697                 o = o[this.jsonRoot];
25698             }
25699         } catch(e){
25700         }
25701         /**
25702          * The current JSON data or null
25703          */
25704         this.jsonData = o;
25705         this.beforeRender();
25706         this.refresh();
25707     },
25708
25709 /**
25710  * Get the number of records in the current JSON dataset
25711  * @return {Number}
25712  */
25713     getCount : function(){
25714         return this.jsonData ? this.jsonData.length : 0;
25715     },
25716
25717 /**
25718  * Returns the JSON object for the specified node(s)
25719  * @param {HTMLElement/Array} node The node or an array of nodes
25720  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25721  * you get the JSON object for the node
25722  */
25723     getNodeData : function(node){
25724         if(node instanceof Array){
25725             var data = [];
25726             for(var i = 0, len = node.length; i < len; i++){
25727                 data.push(this.getNodeData(node[i]));
25728             }
25729             return data;
25730         }
25731         return this.jsonData[this.indexOf(node)] || null;
25732     },
25733
25734     beforeRender : function(){
25735         this.snapshot = this.jsonData;
25736         if(this.sortInfo){
25737             this.sort.apply(this, this.sortInfo);
25738         }
25739         this.fireEvent("beforerender", this, this.jsonData);
25740     },
25741
25742     onLoad : function(el, o){
25743         this.fireEvent("load", this, this.jsonData, o);
25744     },
25745
25746     onLoadException : function(el, o){
25747         this.fireEvent("loadexception", this, o);
25748     },
25749
25750 /**
25751  * Filter the data by a specific property.
25752  * @param {String} property A property on your JSON objects
25753  * @param {String/RegExp} value Either string that the property values
25754  * should start with, or a RegExp to test against the property
25755  */
25756     filter : function(property, value){
25757         if(this.jsonData){
25758             var data = [];
25759             var ss = this.snapshot;
25760             if(typeof value == "string"){
25761                 var vlen = value.length;
25762                 if(vlen == 0){
25763                     this.clearFilter();
25764                     return;
25765                 }
25766                 value = value.toLowerCase();
25767                 for(var i = 0, len = ss.length; i < len; i++){
25768                     var o = ss[i];
25769                     if(o[property].substr(0, vlen).toLowerCase() == value){
25770                         data.push(o);
25771                     }
25772                 }
25773             } else if(value.exec){ // regex?
25774                 for(var i = 0, len = ss.length; i < len; i++){
25775                     var o = ss[i];
25776                     if(value.test(o[property])){
25777                         data.push(o);
25778                     }
25779                 }
25780             } else{
25781                 return;
25782             }
25783             this.jsonData = data;
25784             this.refresh();
25785         }
25786     },
25787
25788 /**
25789  * Filter by a function. The passed function will be called with each
25790  * object in the current dataset. If the function returns true the value is kept,
25791  * otherwise it is filtered.
25792  * @param {Function} fn
25793  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25794  */
25795     filterBy : function(fn, scope){
25796         if(this.jsonData){
25797             var data = [];
25798             var ss = this.snapshot;
25799             for(var i = 0, len = ss.length; i < len; i++){
25800                 var o = ss[i];
25801                 if(fn.call(scope || this, o)){
25802                     data.push(o);
25803                 }
25804             }
25805             this.jsonData = data;
25806             this.refresh();
25807         }
25808     },
25809
25810 /**
25811  * Clears the current filter.
25812  */
25813     clearFilter : function(){
25814         if(this.snapshot && this.jsonData != this.snapshot){
25815             this.jsonData = this.snapshot;
25816             this.refresh();
25817         }
25818     },
25819
25820
25821 /**
25822  * Sorts the data for this view and refreshes it.
25823  * @param {String} property A property on your JSON objects to sort on
25824  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25825  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25826  */
25827     sort : function(property, dir, sortType){
25828         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25829         if(this.jsonData){
25830             var p = property;
25831             var dsc = dir && dir.toLowerCase() == "desc";
25832             var f = function(o1, o2){
25833                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25834                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25835                 ;
25836                 if(v1 < v2){
25837                     return dsc ? +1 : -1;
25838                 } else if(v1 > v2){
25839                     return dsc ? -1 : +1;
25840                 } else{
25841                     return 0;
25842                 }
25843             };
25844             this.jsonData.sort(f);
25845             this.refresh();
25846             if(this.jsonData != this.snapshot){
25847                 this.snapshot.sort(f);
25848             }
25849         }
25850     }
25851 });/*
25852  * Based on:
25853  * Ext JS Library 1.1.1
25854  * Copyright(c) 2006-2007, Ext JS, LLC.
25855  *
25856  * Originally Released Under LGPL - original licence link has changed is not relivant.
25857  *
25858  * Fork - LGPL
25859  * <script type="text/javascript">
25860  */
25861  
25862
25863 /**
25864  * @class Roo.ColorPalette
25865  * @extends Roo.Component
25866  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25867  * Here's an example of typical usage:
25868  * <pre><code>
25869 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25870 cp.render('my-div');
25871
25872 cp.on('select', function(palette, selColor){
25873     // do something with selColor
25874 });
25875 </code></pre>
25876  * @constructor
25877  * Create a new ColorPalette
25878  * @param {Object} config The config object
25879  */
25880 Roo.ColorPalette = function(config){
25881     Roo.ColorPalette.superclass.constructor.call(this, config);
25882     this.addEvents({
25883         /**
25884              * @event select
25885              * Fires when a color is selected
25886              * @param {ColorPalette} this
25887              * @param {String} color The 6-digit color hex code (without the # symbol)
25888              */
25889         select: true
25890     });
25891
25892     if(this.handler){
25893         this.on("select", this.handler, this.scope, true);
25894     }
25895 };
25896 Roo.extend(Roo.ColorPalette, Roo.Component, {
25897     /**
25898      * @cfg {String} itemCls
25899      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25900      */
25901     itemCls : "x-color-palette",
25902     /**
25903      * @cfg {String} value
25904      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25905      * the hex codes are case-sensitive.
25906      */
25907     value : null,
25908     clickEvent:'click',
25909     // private
25910     ctype: "Roo.ColorPalette",
25911
25912     /**
25913      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25914      */
25915     allowReselect : false,
25916
25917     /**
25918      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25919      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25920      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25921      * of colors with the width setting until the box is symmetrical.</p>
25922      * <p>You can override individual colors if needed:</p>
25923      * <pre><code>
25924 var cp = new Roo.ColorPalette();
25925 cp.colors[0] = "FF0000";  // change the first box to red
25926 </code></pre>
25927
25928 Or you can provide a custom array of your own for complete control:
25929 <pre><code>
25930 var cp = new Roo.ColorPalette();
25931 cp.colors = ["000000", "993300", "333300"];
25932 </code></pre>
25933      * @type Array
25934      */
25935     colors : [
25936         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25937         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25938         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25939         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25940         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25941     ],
25942
25943     // private
25944     onRender : function(container, position){
25945         var t = new Roo.MasterTemplate(
25946             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25947         );
25948         var c = this.colors;
25949         for(var i = 0, len = c.length; i < len; i++){
25950             t.add([c[i]]);
25951         }
25952         var el = document.createElement("div");
25953         el.className = this.itemCls;
25954         t.overwrite(el);
25955         container.dom.insertBefore(el, position);
25956         this.el = Roo.get(el);
25957         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25958         if(this.clickEvent != 'click'){
25959             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25960         }
25961     },
25962
25963     // private
25964     afterRender : function(){
25965         Roo.ColorPalette.superclass.afterRender.call(this);
25966         if(this.value){
25967             var s = this.value;
25968             this.value = null;
25969             this.select(s);
25970         }
25971     },
25972
25973     // private
25974     handleClick : function(e, t){
25975         e.preventDefault();
25976         if(!this.disabled){
25977             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25978             this.select(c.toUpperCase());
25979         }
25980     },
25981
25982     /**
25983      * Selects the specified color in the palette (fires the select event)
25984      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25985      */
25986     select : function(color){
25987         color = color.replace("#", "");
25988         if(color != this.value || this.allowReselect){
25989             var el = this.el;
25990             if(this.value){
25991                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25992             }
25993             el.child("a.color-"+color).addClass("x-color-palette-sel");
25994             this.value = color;
25995             this.fireEvent("select", this, color);
25996         }
25997     }
25998 });/*
25999  * Based on:
26000  * Ext JS Library 1.1.1
26001  * Copyright(c) 2006-2007, Ext JS, LLC.
26002  *
26003  * Originally Released Under LGPL - original licence link has changed is not relivant.
26004  *
26005  * Fork - LGPL
26006  * <script type="text/javascript">
26007  */
26008  
26009 /**
26010  * @class Roo.DatePicker
26011  * @extends Roo.Component
26012  * Simple date picker class.
26013  * @constructor
26014  * Create a new DatePicker
26015  * @param {Object} config The config object
26016  */
26017 Roo.DatePicker = function(config){
26018     Roo.DatePicker.superclass.constructor.call(this, config);
26019
26020     this.value = config && config.value ?
26021                  config.value.clearTime() : new Date().clearTime();
26022
26023     this.addEvents({
26024         /**
26025              * @event select
26026              * Fires when a date is selected
26027              * @param {DatePicker} this
26028              * @param {Date} date The selected date
26029              */
26030         'select': true,
26031         /**
26032              * @event monthchange
26033              * Fires when the displayed month changes 
26034              * @param {DatePicker} this
26035              * @param {Date} date The selected month
26036              */
26037         'monthchange': true
26038     });
26039
26040     if(this.handler){
26041         this.on("select", this.handler,  this.scope || this);
26042     }
26043     // build the disabledDatesRE
26044     if(!this.disabledDatesRE && this.disabledDates){
26045         var dd = this.disabledDates;
26046         var re = "(?:";
26047         for(var i = 0; i < dd.length; i++){
26048             re += dd[i];
26049             if(i != dd.length-1) {
26050                 re += "|";
26051             }
26052         }
26053         this.disabledDatesRE = new RegExp(re + ")");
26054     }
26055 };
26056
26057 Roo.extend(Roo.DatePicker, Roo.Component, {
26058     /**
26059      * @cfg {String} todayText
26060      * The text to display on the button that selects the current date (defaults to "Today")
26061      */
26062     todayText : "Today",
26063     /**
26064      * @cfg {String} okText
26065      * The text to display on the ok button
26066      */
26067     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
26068     /**
26069      * @cfg {String} cancelText
26070      * The text to display on the cancel button
26071      */
26072     cancelText : "Cancel",
26073     /**
26074      * @cfg {String} todayTip
26075      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
26076      */
26077     todayTip : "{0} (Spacebar)",
26078     /**
26079      * @cfg {Date} minDate
26080      * Minimum allowable date (JavaScript date object, defaults to null)
26081      */
26082     minDate : null,
26083     /**
26084      * @cfg {Date} maxDate
26085      * Maximum allowable date (JavaScript date object, defaults to null)
26086      */
26087     maxDate : null,
26088     /**
26089      * @cfg {String} minText
26090      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
26091      */
26092     minText : "This date is before the minimum date",
26093     /**
26094      * @cfg {String} maxText
26095      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
26096      */
26097     maxText : "This date is after the maximum date",
26098     /**
26099      * @cfg {String} format
26100      * The default date format string which can be overriden for localization support.  The format must be
26101      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
26102      */
26103     format : "m/d/y",
26104     /**
26105      * @cfg {Array} disabledDays
26106      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
26107      */
26108     disabledDays : null,
26109     /**
26110      * @cfg {String} disabledDaysText
26111      * The tooltip to display when the date falls on a disabled day (defaults to "")
26112      */
26113     disabledDaysText : "",
26114     /**
26115      * @cfg {RegExp} disabledDatesRE
26116      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
26117      */
26118     disabledDatesRE : null,
26119     /**
26120      * @cfg {String} disabledDatesText
26121      * The tooltip text to display when the date falls on a disabled date (defaults to "")
26122      */
26123     disabledDatesText : "",
26124     /**
26125      * @cfg {Boolean} constrainToViewport
26126      * True to constrain the date picker to the viewport (defaults to true)
26127      */
26128     constrainToViewport : true,
26129     /**
26130      * @cfg {Array} monthNames
26131      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
26132      */
26133     monthNames : Date.monthNames,
26134     /**
26135      * @cfg {Array} dayNames
26136      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
26137      */
26138     dayNames : Date.dayNames,
26139     /**
26140      * @cfg {String} nextText
26141      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
26142      */
26143     nextText: 'Next Month (Control+Right)',
26144     /**
26145      * @cfg {String} prevText
26146      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
26147      */
26148     prevText: 'Previous Month (Control+Left)',
26149     /**
26150      * @cfg {String} monthYearText
26151      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
26152      */
26153     monthYearText: 'Choose a month (Control+Up/Down to move years)',
26154     /**
26155      * @cfg {Number} startDay
26156      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
26157      */
26158     startDay : 0,
26159     /**
26160      * @cfg {Bool} showClear
26161      * Show a clear button (usefull for date form elements that can be blank.)
26162      */
26163     
26164     showClear: false,
26165     
26166     /**
26167      * Sets the value of the date field
26168      * @param {Date} value The date to set
26169      */
26170     setValue : function(value){
26171         var old = this.value;
26172         
26173         if (typeof(value) == 'string') {
26174          
26175             value = Date.parseDate(value, this.format);
26176         }
26177         if (!value) {
26178             value = new Date();
26179         }
26180         
26181         this.value = value.clearTime(true);
26182         if(this.el){
26183             this.update(this.value);
26184         }
26185     },
26186
26187     /**
26188      * Gets the current selected value of the date field
26189      * @return {Date} The selected date
26190      */
26191     getValue : function(){
26192         return this.value;
26193     },
26194
26195     // private
26196     focus : function(){
26197         if(this.el){
26198             this.update(this.activeDate);
26199         }
26200     },
26201
26202     // privateval
26203     onRender : function(container, position){
26204         
26205         var m = [
26206              '<table cellspacing="0">',
26207                 '<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>',
26208                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26209         var dn = this.dayNames;
26210         for(var i = 0; i < 7; i++){
26211             var d = this.startDay+i;
26212             if(d > 6){
26213                 d = d-7;
26214             }
26215             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26216         }
26217         m[m.length] = "</tr></thead><tbody><tr>";
26218         for(var i = 0; i < 42; i++) {
26219             if(i % 7 == 0 && i != 0){
26220                 m[m.length] = "</tr><tr>";
26221             }
26222             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26223         }
26224         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26225             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26226
26227         var el = document.createElement("div");
26228         el.className = "x-date-picker";
26229         el.innerHTML = m.join("");
26230
26231         container.dom.insertBefore(el, position);
26232
26233         this.el = Roo.get(el);
26234         this.eventEl = Roo.get(el.firstChild);
26235
26236         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26237             handler: this.showPrevMonth,
26238             scope: this,
26239             preventDefault:true,
26240             stopDefault:true
26241         });
26242
26243         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26244             handler: this.showNextMonth,
26245             scope: this,
26246             preventDefault:true,
26247             stopDefault:true
26248         });
26249
26250         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26251
26252         this.monthPicker = this.el.down('div.x-date-mp');
26253         this.monthPicker.enableDisplayMode('block');
26254         
26255         var kn = new Roo.KeyNav(this.eventEl, {
26256             "left" : function(e){
26257                 e.ctrlKey ?
26258                     this.showPrevMonth() :
26259                     this.update(this.activeDate.add("d", -1));
26260             },
26261
26262             "right" : function(e){
26263                 e.ctrlKey ?
26264                     this.showNextMonth() :
26265                     this.update(this.activeDate.add("d", 1));
26266             },
26267
26268             "up" : function(e){
26269                 e.ctrlKey ?
26270                     this.showNextYear() :
26271                     this.update(this.activeDate.add("d", -7));
26272             },
26273
26274             "down" : function(e){
26275                 e.ctrlKey ?
26276                     this.showPrevYear() :
26277                     this.update(this.activeDate.add("d", 7));
26278             },
26279
26280             "pageUp" : function(e){
26281                 this.showNextMonth();
26282             },
26283
26284             "pageDown" : function(e){
26285                 this.showPrevMonth();
26286             },
26287
26288             "enter" : function(e){
26289                 e.stopPropagation();
26290                 return true;
26291             },
26292
26293             scope : this
26294         });
26295
26296         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26297
26298         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26299
26300         this.el.unselectable();
26301         
26302         this.cells = this.el.select("table.x-date-inner tbody td");
26303         this.textNodes = this.el.query("table.x-date-inner tbody span");
26304
26305         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26306             text: "&#160;",
26307             tooltip: this.monthYearText
26308         });
26309
26310         this.mbtn.on('click', this.showMonthPicker, this);
26311         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26312
26313
26314         var today = (new Date()).dateFormat(this.format);
26315         
26316         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26317         if (this.showClear) {
26318             baseTb.add( new Roo.Toolbar.Fill());
26319         }
26320         baseTb.add({
26321             text: String.format(this.todayText, today),
26322             tooltip: String.format(this.todayTip, today),
26323             handler: this.selectToday,
26324             scope: this
26325         });
26326         
26327         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26328             
26329         //});
26330         if (this.showClear) {
26331             
26332             baseTb.add( new Roo.Toolbar.Fill());
26333             baseTb.add({
26334                 text: '&#160;',
26335                 cls: 'x-btn-icon x-btn-clear',
26336                 handler: function() {
26337                     //this.value = '';
26338                     this.fireEvent("select", this, '');
26339                 },
26340                 scope: this
26341             });
26342         }
26343         
26344         
26345         if(Roo.isIE){
26346             this.el.repaint();
26347         }
26348         this.update(this.value);
26349     },
26350
26351     createMonthPicker : function(){
26352         if(!this.monthPicker.dom.firstChild){
26353             var buf = ['<table border="0" cellspacing="0">'];
26354             for(var i = 0; i < 6; i++){
26355                 buf.push(
26356                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26357                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26358                     i == 0 ?
26359                     '<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>' :
26360                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26361                 );
26362             }
26363             buf.push(
26364                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26365                     this.okText,
26366                     '</button><button type="button" class="x-date-mp-cancel">',
26367                     this.cancelText,
26368                     '</button></td></tr>',
26369                 '</table>'
26370             );
26371             this.monthPicker.update(buf.join(''));
26372             this.monthPicker.on('click', this.onMonthClick, this);
26373             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26374
26375             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26376             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26377
26378             this.mpMonths.each(function(m, a, i){
26379                 i += 1;
26380                 if((i%2) == 0){
26381                     m.dom.xmonth = 5 + Math.round(i * .5);
26382                 }else{
26383                     m.dom.xmonth = Math.round((i-1) * .5);
26384                 }
26385             });
26386         }
26387     },
26388
26389     showMonthPicker : function(){
26390         this.createMonthPicker();
26391         var size = this.el.getSize();
26392         this.monthPicker.setSize(size);
26393         this.monthPicker.child('table').setSize(size);
26394
26395         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26396         this.updateMPMonth(this.mpSelMonth);
26397         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26398         this.updateMPYear(this.mpSelYear);
26399
26400         this.monthPicker.slideIn('t', {duration:.2});
26401     },
26402
26403     updateMPYear : function(y){
26404         this.mpyear = y;
26405         var ys = this.mpYears.elements;
26406         for(var i = 1; i <= 10; i++){
26407             var td = ys[i-1], y2;
26408             if((i%2) == 0){
26409                 y2 = y + Math.round(i * .5);
26410                 td.firstChild.innerHTML = y2;
26411                 td.xyear = y2;
26412             }else{
26413                 y2 = y - (5-Math.round(i * .5));
26414                 td.firstChild.innerHTML = y2;
26415                 td.xyear = y2;
26416             }
26417             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26418         }
26419     },
26420
26421     updateMPMonth : function(sm){
26422         this.mpMonths.each(function(m, a, i){
26423             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26424         });
26425     },
26426
26427     selectMPMonth: function(m){
26428         
26429     },
26430
26431     onMonthClick : function(e, t){
26432         e.stopEvent();
26433         var el = new Roo.Element(t), pn;
26434         if(el.is('button.x-date-mp-cancel')){
26435             this.hideMonthPicker();
26436         }
26437         else if(el.is('button.x-date-mp-ok')){
26438             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26439             this.hideMonthPicker();
26440         }
26441         else if(pn = el.up('td.x-date-mp-month', 2)){
26442             this.mpMonths.removeClass('x-date-mp-sel');
26443             pn.addClass('x-date-mp-sel');
26444             this.mpSelMonth = pn.dom.xmonth;
26445         }
26446         else if(pn = el.up('td.x-date-mp-year', 2)){
26447             this.mpYears.removeClass('x-date-mp-sel');
26448             pn.addClass('x-date-mp-sel');
26449             this.mpSelYear = pn.dom.xyear;
26450         }
26451         else if(el.is('a.x-date-mp-prev')){
26452             this.updateMPYear(this.mpyear-10);
26453         }
26454         else if(el.is('a.x-date-mp-next')){
26455             this.updateMPYear(this.mpyear+10);
26456         }
26457     },
26458
26459     onMonthDblClick : function(e, t){
26460         e.stopEvent();
26461         var el = new Roo.Element(t), pn;
26462         if(pn = el.up('td.x-date-mp-month', 2)){
26463             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26464             this.hideMonthPicker();
26465         }
26466         else if(pn = el.up('td.x-date-mp-year', 2)){
26467             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26468             this.hideMonthPicker();
26469         }
26470     },
26471
26472     hideMonthPicker : function(disableAnim){
26473         if(this.monthPicker){
26474             if(disableAnim === true){
26475                 this.monthPicker.hide();
26476             }else{
26477                 this.monthPicker.slideOut('t', {duration:.2});
26478             }
26479         }
26480     },
26481
26482     // private
26483     showPrevMonth : function(e){
26484         this.update(this.activeDate.add("mo", -1));
26485     },
26486
26487     // private
26488     showNextMonth : function(e){
26489         this.update(this.activeDate.add("mo", 1));
26490     },
26491
26492     // private
26493     showPrevYear : function(){
26494         this.update(this.activeDate.add("y", -1));
26495     },
26496
26497     // private
26498     showNextYear : function(){
26499         this.update(this.activeDate.add("y", 1));
26500     },
26501
26502     // private
26503     handleMouseWheel : function(e){
26504         var delta = e.getWheelDelta();
26505         if(delta > 0){
26506             this.showPrevMonth();
26507             e.stopEvent();
26508         } else if(delta < 0){
26509             this.showNextMonth();
26510             e.stopEvent();
26511         }
26512     },
26513
26514     // private
26515     handleDateClick : function(e, t){
26516         e.stopEvent();
26517         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26518             this.setValue(new Date(t.dateValue));
26519             this.fireEvent("select", this, this.value);
26520         }
26521     },
26522
26523     // private
26524     selectToday : function(){
26525         this.setValue(new Date().clearTime());
26526         this.fireEvent("select", this, this.value);
26527     },
26528
26529     // private
26530     update : function(date)
26531     {
26532         var vd = this.activeDate;
26533         this.activeDate = date;
26534         if(vd && this.el){
26535             var t = date.getTime();
26536             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26537                 this.cells.removeClass("x-date-selected");
26538                 this.cells.each(function(c){
26539                    if(c.dom.firstChild.dateValue == t){
26540                        c.addClass("x-date-selected");
26541                        setTimeout(function(){
26542                             try{c.dom.firstChild.focus();}catch(e){}
26543                        }, 50);
26544                        return false;
26545                    }
26546                 });
26547                 return;
26548             }
26549         }
26550         
26551         var days = date.getDaysInMonth();
26552         var firstOfMonth = date.getFirstDateOfMonth();
26553         var startingPos = firstOfMonth.getDay()-this.startDay;
26554
26555         if(startingPos <= this.startDay){
26556             startingPos += 7;
26557         }
26558
26559         var pm = date.add("mo", -1);
26560         var prevStart = pm.getDaysInMonth()-startingPos;
26561
26562         var cells = this.cells.elements;
26563         var textEls = this.textNodes;
26564         days += startingPos;
26565
26566         // convert everything to numbers so it's fast
26567         var day = 86400000;
26568         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26569         var today = new Date().clearTime().getTime();
26570         var sel = date.clearTime().getTime();
26571         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26572         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26573         var ddMatch = this.disabledDatesRE;
26574         var ddText = this.disabledDatesText;
26575         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26576         var ddaysText = this.disabledDaysText;
26577         var format = this.format;
26578
26579         var setCellClass = function(cal, cell){
26580             cell.title = "";
26581             var t = d.getTime();
26582             cell.firstChild.dateValue = t;
26583             if(t == today){
26584                 cell.className += " x-date-today";
26585                 cell.title = cal.todayText;
26586             }
26587             if(t == sel){
26588                 cell.className += " x-date-selected";
26589                 setTimeout(function(){
26590                     try{cell.firstChild.focus();}catch(e){}
26591                 }, 50);
26592             }
26593             // disabling
26594             if(t < min) {
26595                 cell.className = " x-date-disabled";
26596                 cell.title = cal.minText;
26597                 return;
26598             }
26599             if(t > max) {
26600                 cell.className = " x-date-disabled";
26601                 cell.title = cal.maxText;
26602                 return;
26603             }
26604             if(ddays){
26605                 if(ddays.indexOf(d.getDay()) != -1){
26606                     cell.title = ddaysText;
26607                     cell.className = " x-date-disabled";
26608                 }
26609             }
26610             if(ddMatch && format){
26611                 var fvalue = d.dateFormat(format);
26612                 if(ddMatch.test(fvalue)){
26613                     cell.title = ddText.replace("%0", fvalue);
26614                     cell.className = " x-date-disabled";
26615                 }
26616             }
26617         };
26618
26619         var i = 0;
26620         for(; i < startingPos; i++) {
26621             textEls[i].innerHTML = (++prevStart);
26622             d.setDate(d.getDate()+1);
26623             cells[i].className = "x-date-prevday";
26624             setCellClass(this, cells[i]);
26625         }
26626         for(; i < days; i++){
26627             intDay = i - startingPos + 1;
26628             textEls[i].innerHTML = (intDay);
26629             d.setDate(d.getDate()+1);
26630             cells[i].className = "x-date-active";
26631             setCellClass(this, cells[i]);
26632         }
26633         var extraDays = 0;
26634         for(; i < 42; i++) {
26635              textEls[i].innerHTML = (++extraDays);
26636              d.setDate(d.getDate()+1);
26637              cells[i].className = "x-date-nextday";
26638              setCellClass(this, cells[i]);
26639         }
26640
26641         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26642         this.fireEvent('monthchange', this, date);
26643         
26644         if(!this.internalRender){
26645             var main = this.el.dom.firstChild;
26646             var w = main.offsetWidth;
26647             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26648             Roo.fly(main).setWidth(w);
26649             this.internalRender = true;
26650             // opera does not respect the auto grow header center column
26651             // then, after it gets a width opera refuses to recalculate
26652             // without a second pass
26653             if(Roo.isOpera && !this.secondPass){
26654                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26655                 this.secondPass = true;
26656                 this.update.defer(10, this, [date]);
26657             }
26658         }
26659         
26660         
26661     }
26662 });        /*
26663  * Based on:
26664  * Ext JS Library 1.1.1
26665  * Copyright(c) 2006-2007, Ext JS, LLC.
26666  *
26667  * Originally Released Under LGPL - original licence link has changed is not relivant.
26668  *
26669  * Fork - LGPL
26670  * <script type="text/javascript">
26671  */
26672 /**
26673  * @class Roo.TabPanel
26674  * @extends Roo.util.Observable
26675  * A lightweight tab container.
26676  * <br><br>
26677  * Usage:
26678  * <pre><code>
26679 // basic tabs 1, built from existing content
26680 var tabs = new Roo.TabPanel("tabs1");
26681 tabs.addTab("script", "View Script");
26682 tabs.addTab("markup", "View Markup");
26683 tabs.activate("script");
26684
26685 // more advanced tabs, built from javascript
26686 var jtabs = new Roo.TabPanel("jtabs");
26687 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26688
26689 // set up the UpdateManager
26690 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26691 var updater = tab2.getUpdateManager();
26692 updater.setDefaultUrl("ajax1.htm");
26693 tab2.on('activate', updater.refresh, updater, true);
26694
26695 // Use setUrl for Ajax loading
26696 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26697 tab3.setUrl("ajax2.htm", null, true);
26698
26699 // Disabled tab
26700 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26701 tab4.disable();
26702
26703 jtabs.activate("jtabs-1");
26704  * </code></pre>
26705  * @constructor
26706  * Create a new TabPanel.
26707  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26708  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26709  */
26710 Roo.TabPanel = function(container, config){
26711     /**
26712     * The container element for this TabPanel.
26713     * @type Roo.Element
26714     */
26715     this.el = Roo.get(container, true);
26716     if(config){
26717         if(typeof config == "boolean"){
26718             this.tabPosition = config ? "bottom" : "top";
26719         }else{
26720             Roo.apply(this, config);
26721         }
26722     }
26723     if(this.tabPosition == "bottom"){
26724         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26725         this.el.addClass("x-tabs-bottom");
26726     }
26727     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26728     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26729     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26730     if(Roo.isIE){
26731         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26732     }
26733     if(this.tabPosition != "bottom"){
26734         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26735          * @type Roo.Element
26736          */
26737         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26738         this.el.addClass("x-tabs-top");
26739     }
26740     this.items = [];
26741
26742     this.bodyEl.setStyle("position", "relative");
26743
26744     this.active = null;
26745     this.activateDelegate = this.activate.createDelegate(this);
26746
26747     this.addEvents({
26748         /**
26749          * @event tabchange
26750          * Fires when the active tab changes
26751          * @param {Roo.TabPanel} this
26752          * @param {Roo.TabPanelItem} activePanel The new active tab
26753          */
26754         "tabchange": true,
26755         /**
26756          * @event beforetabchange
26757          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26758          * @param {Roo.TabPanel} this
26759          * @param {Object} e Set cancel to true on this object to cancel the tab change
26760          * @param {Roo.TabPanelItem} tab The tab being changed to
26761          */
26762         "beforetabchange" : true
26763     });
26764
26765     Roo.EventManager.onWindowResize(this.onResize, this);
26766     this.cpad = this.el.getPadding("lr");
26767     this.hiddenCount = 0;
26768
26769
26770     // toolbar on the tabbar support...
26771     if (this.toolbar) {
26772         var tcfg = this.toolbar;
26773         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26774         this.toolbar = new Roo.Toolbar(tcfg);
26775         if (Roo.isSafari) {
26776             var tbl = tcfg.container.child('table', true);
26777             tbl.setAttribute('width', '100%');
26778         }
26779         
26780     }
26781    
26782
26783
26784     Roo.TabPanel.superclass.constructor.call(this);
26785 };
26786
26787 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26788     /*
26789      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26790      */
26791     tabPosition : "top",
26792     /*
26793      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26794      */
26795     currentTabWidth : 0,
26796     /*
26797      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26798      */
26799     minTabWidth : 40,
26800     /*
26801      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26802      */
26803     maxTabWidth : 250,
26804     /*
26805      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26806      */
26807     preferredTabWidth : 175,
26808     /*
26809      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26810      */
26811     resizeTabs : false,
26812     /*
26813      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26814      */
26815     monitorResize : true,
26816     /*
26817      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26818      */
26819     toolbar : false,
26820
26821     /**
26822      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26823      * @param {String} id The id of the div to use <b>or create</b>
26824      * @param {String} text The text for the tab
26825      * @param {String} content (optional) Content to put in the TabPanelItem body
26826      * @param {Boolean} closable (optional) True to create a close icon on the tab
26827      * @return {Roo.TabPanelItem} The created TabPanelItem
26828      */
26829     addTab : function(id, text, content, closable){
26830         var item = new Roo.TabPanelItem(this, id, text, closable);
26831         this.addTabItem(item);
26832         if(content){
26833             item.setContent(content);
26834         }
26835         return item;
26836     },
26837
26838     /**
26839      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26840      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26841      * @return {Roo.TabPanelItem}
26842      */
26843     getTab : function(id){
26844         return this.items[id];
26845     },
26846
26847     /**
26848      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26849      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26850      */
26851     hideTab : function(id){
26852         var t = this.items[id];
26853         if(!t.isHidden()){
26854            t.setHidden(true);
26855            this.hiddenCount++;
26856            this.autoSizeTabs();
26857         }
26858     },
26859
26860     /**
26861      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26862      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26863      */
26864     unhideTab : function(id){
26865         var t = this.items[id];
26866         if(t.isHidden()){
26867            t.setHidden(false);
26868            this.hiddenCount--;
26869            this.autoSizeTabs();
26870         }
26871     },
26872
26873     /**
26874      * Adds an existing {@link Roo.TabPanelItem}.
26875      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26876      */
26877     addTabItem : function(item){
26878         this.items[item.id] = item;
26879         this.items.push(item);
26880         if(this.resizeTabs){
26881            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26882            this.autoSizeTabs();
26883         }else{
26884             item.autoSize();
26885         }
26886     },
26887
26888     /**
26889      * Removes a {@link Roo.TabPanelItem}.
26890      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26891      */
26892     removeTab : function(id){
26893         var items = this.items;
26894         var tab = items[id];
26895         if(!tab) { return; }
26896         var index = items.indexOf(tab);
26897         if(this.active == tab && items.length > 1){
26898             var newTab = this.getNextAvailable(index);
26899             if(newTab) {
26900                 newTab.activate();
26901             }
26902         }
26903         this.stripEl.dom.removeChild(tab.pnode.dom);
26904         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26905             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26906         }
26907         items.splice(index, 1);
26908         delete this.items[tab.id];
26909         tab.fireEvent("close", tab);
26910         tab.purgeListeners();
26911         this.autoSizeTabs();
26912     },
26913
26914     getNextAvailable : function(start){
26915         var items = this.items;
26916         var index = start;
26917         // look for a next tab that will slide over to
26918         // replace the one being removed
26919         while(index < items.length){
26920             var item = items[++index];
26921             if(item && !item.isHidden()){
26922                 return item;
26923             }
26924         }
26925         // if one isn't found select the previous tab (on the left)
26926         index = start;
26927         while(index >= 0){
26928             var item = items[--index];
26929             if(item && !item.isHidden()){
26930                 return item;
26931             }
26932         }
26933         return null;
26934     },
26935
26936     /**
26937      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26938      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26939      */
26940     disableTab : function(id){
26941         var tab = this.items[id];
26942         if(tab && this.active != tab){
26943             tab.disable();
26944         }
26945     },
26946
26947     /**
26948      * Enables a {@link Roo.TabPanelItem} that is disabled.
26949      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26950      */
26951     enableTab : function(id){
26952         var tab = this.items[id];
26953         tab.enable();
26954     },
26955
26956     /**
26957      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26958      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26959      * @return {Roo.TabPanelItem} The TabPanelItem.
26960      */
26961     activate : function(id){
26962         var tab = this.items[id];
26963         if(!tab){
26964             return null;
26965         }
26966         if(tab == this.active || tab.disabled){
26967             return tab;
26968         }
26969         var e = {};
26970         this.fireEvent("beforetabchange", this, e, tab);
26971         if(e.cancel !== true && !tab.disabled){
26972             if(this.active){
26973                 this.active.hide();
26974             }
26975             this.active = this.items[id];
26976             this.active.show();
26977             this.fireEvent("tabchange", this, this.active);
26978         }
26979         return tab;
26980     },
26981
26982     /**
26983      * Gets the active {@link Roo.TabPanelItem}.
26984      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26985      */
26986     getActiveTab : function(){
26987         return this.active;
26988     },
26989
26990     /**
26991      * Updates the tab body element to fit the height of the container element
26992      * for overflow scrolling
26993      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26994      */
26995     syncHeight : function(targetHeight){
26996         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26997         var bm = this.bodyEl.getMargins();
26998         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26999         this.bodyEl.setHeight(newHeight);
27000         return newHeight;
27001     },
27002
27003     onResize : function(){
27004         if(this.monitorResize){
27005             this.autoSizeTabs();
27006         }
27007     },
27008
27009     /**
27010      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
27011      */
27012     beginUpdate : function(){
27013         this.updating = true;
27014     },
27015
27016     /**
27017      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
27018      */
27019     endUpdate : function(){
27020         this.updating = false;
27021         this.autoSizeTabs();
27022     },
27023
27024     /**
27025      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
27026      */
27027     autoSizeTabs : function(){
27028         var count = this.items.length;
27029         var vcount = count - this.hiddenCount;
27030         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
27031             return;
27032         }
27033         var w = Math.max(this.el.getWidth() - this.cpad, 10);
27034         var availWidth = Math.floor(w / vcount);
27035         var b = this.stripBody;
27036         if(b.getWidth() > w){
27037             var tabs = this.items;
27038             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
27039             if(availWidth < this.minTabWidth){
27040                 /*if(!this.sleft){    // incomplete scrolling code
27041                     this.createScrollButtons();
27042                 }
27043                 this.showScroll();
27044                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
27045             }
27046         }else{
27047             if(this.currentTabWidth < this.preferredTabWidth){
27048                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
27049             }
27050         }
27051     },
27052
27053     /**
27054      * Returns the number of tabs in this TabPanel.
27055      * @return {Number}
27056      */
27057      getCount : function(){
27058          return this.items.length;
27059      },
27060
27061     /**
27062      * Resizes all the tabs to the passed width
27063      * @param {Number} The new width
27064      */
27065     setTabWidth : function(width){
27066         this.currentTabWidth = width;
27067         for(var i = 0, len = this.items.length; i < len; i++) {
27068                 if(!this.items[i].isHidden()) {
27069                 this.items[i].setWidth(width);
27070             }
27071         }
27072     },
27073
27074     /**
27075      * Destroys this TabPanel
27076      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
27077      */
27078     destroy : function(removeEl){
27079         Roo.EventManager.removeResizeListener(this.onResize, this);
27080         for(var i = 0, len = this.items.length; i < len; i++){
27081             this.items[i].purgeListeners();
27082         }
27083         if(removeEl === true){
27084             this.el.update("");
27085             this.el.remove();
27086         }
27087     }
27088 });
27089
27090 /**
27091  * @class Roo.TabPanelItem
27092  * @extends Roo.util.Observable
27093  * Represents an individual item (tab plus body) in a TabPanel.
27094  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
27095  * @param {String} id The id of this TabPanelItem
27096  * @param {String} text The text for the tab of this TabPanelItem
27097  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
27098  */
27099 Roo.TabPanelItem = function(tabPanel, id, text, closable){
27100     /**
27101      * The {@link Roo.TabPanel} this TabPanelItem belongs to
27102      * @type Roo.TabPanel
27103      */
27104     this.tabPanel = tabPanel;
27105     /**
27106      * The id for this TabPanelItem
27107      * @type String
27108      */
27109     this.id = id;
27110     /** @private */
27111     this.disabled = false;
27112     /** @private */
27113     this.text = text;
27114     /** @private */
27115     this.loaded = false;
27116     this.closable = closable;
27117
27118     /**
27119      * The body element for this TabPanelItem.
27120      * @type Roo.Element
27121      */
27122     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
27123     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
27124     this.bodyEl.setStyle("display", "block");
27125     this.bodyEl.setStyle("zoom", "1");
27126     this.hideAction();
27127
27128     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
27129     /** @private */
27130     this.el = Roo.get(els.el, true);
27131     this.inner = Roo.get(els.inner, true);
27132     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
27133     this.pnode = Roo.get(els.el.parentNode, true);
27134     this.el.on("mousedown", this.onTabMouseDown, this);
27135     this.el.on("click", this.onTabClick, this);
27136     /** @private */
27137     if(closable){
27138         var c = Roo.get(els.close, true);
27139         c.dom.title = this.closeText;
27140         c.addClassOnOver("close-over");
27141         c.on("click", this.closeClick, this);
27142      }
27143
27144     this.addEvents({
27145          /**
27146          * @event activate
27147          * Fires when this tab becomes the active tab.
27148          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27149          * @param {Roo.TabPanelItem} this
27150          */
27151         "activate": true,
27152         /**
27153          * @event beforeclose
27154          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
27155          * @param {Roo.TabPanelItem} this
27156          * @param {Object} e Set cancel to true on this object to cancel the close.
27157          */
27158         "beforeclose": true,
27159         /**
27160          * @event close
27161          * Fires when this tab is closed.
27162          * @param {Roo.TabPanelItem} this
27163          */
27164          "close": true,
27165         /**
27166          * @event deactivate
27167          * Fires when this tab is no longer the active tab.
27168          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27169          * @param {Roo.TabPanelItem} this
27170          */
27171          "deactivate" : true
27172     });
27173     this.hidden = false;
27174
27175     Roo.TabPanelItem.superclass.constructor.call(this);
27176 };
27177
27178 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27179     purgeListeners : function(){
27180        Roo.util.Observable.prototype.purgeListeners.call(this);
27181        this.el.removeAllListeners();
27182     },
27183     /**
27184      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27185      */
27186     show : function(){
27187         this.pnode.addClass("on");
27188         this.showAction();
27189         if(Roo.isOpera){
27190             this.tabPanel.stripWrap.repaint();
27191         }
27192         this.fireEvent("activate", this.tabPanel, this);
27193     },
27194
27195     /**
27196      * Returns true if this tab is the active tab.
27197      * @return {Boolean}
27198      */
27199     isActive : function(){
27200         return this.tabPanel.getActiveTab() == this;
27201     },
27202
27203     /**
27204      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27205      */
27206     hide : function(){
27207         this.pnode.removeClass("on");
27208         this.hideAction();
27209         this.fireEvent("deactivate", this.tabPanel, this);
27210     },
27211
27212     hideAction : function(){
27213         this.bodyEl.hide();
27214         this.bodyEl.setStyle("position", "absolute");
27215         this.bodyEl.setLeft("-20000px");
27216         this.bodyEl.setTop("-20000px");
27217     },
27218
27219     showAction : function(){
27220         this.bodyEl.setStyle("position", "relative");
27221         this.bodyEl.setTop("");
27222         this.bodyEl.setLeft("");
27223         this.bodyEl.show();
27224     },
27225
27226     /**
27227      * Set the tooltip for the tab.
27228      * @param {String} tooltip The tab's tooltip
27229      */
27230     setTooltip : function(text){
27231         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27232             this.textEl.dom.qtip = text;
27233             this.textEl.dom.removeAttribute('title');
27234         }else{
27235             this.textEl.dom.title = text;
27236         }
27237     },
27238
27239     onTabClick : function(e){
27240         e.preventDefault();
27241         this.tabPanel.activate(this.id);
27242     },
27243
27244     onTabMouseDown : function(e){
27245         e.preventDefault();
27246         this.tabPanel.activate(this.id);
27247     },
27248
27249     getWidth : function(){
27250         return this.inner.getWidth();
27251     },
27252
27253     setWidth : function(width){
27254         var iwidth = width - this.pnode.getPadding("lr");
27255         this.inner.setWidth(iwidth);
27256         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27257         this.pnode.setWidth(width);
27258     },
27259
27260     /**
27261      * Show or hide the tab
27262      * @param {Boolean} hidden True to hide or false to show.
27263      */
27264     setHidden : function(hidden){
27265         this.hidden = hidden;
27266         this.pnode.setStyle("display", hidden ? "none" : "");
27267     },
27268
27269     /**
27270      * Returns true if this tab is "hidden"
27271      * @return {Boolean}
27272      */
27273     isHidden : function(){
27274         return this.hidden;
27275     },
27276
27277     /**
27278      * Returns the text for this tab
27279      * @return {String}
27280      */
27281     getText : function(){
27282         return this.text;
27283     },
27284
27285     autoSize : function(){
27286         //this.el.beginMeasure();
27287         this.textEl.setWidth(1);
27288         /*
27289          *  #2804 [new] Tabs in Roojs
27290          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
27291          */
27292         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
27293         //this.el.endMeasure();
27294     },
27295
27296     /**
27297      * Sets the text for the tab (Note: this also sets the tooltip text)
27298      * @param {String} text The tab's text and tooltip
27299      */
27300     setText : function(text){
27301         this.text = text;
27302         this.textEl.update(text);
27303         this.setTooltip(text);
27304         if(!this.tabPanel.resizeTabs){
27305             this.autoSize();
27306         }
27307     },
27308     /**
27309      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27310      */
27311     activate : function(){
27312         this.tabPanel.activate(this.id);
27313     },
27314
27315     /**
27316      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27317      */
27318     disable : function(){
27319         if(this.tabPanel.active != this){
27320             this.disabled = true;
27321             this.pnode.addClass("disabled");
27322         }
27323     },
27324
27325     /**
27326      * Enables this TabPanelItem if it was previously disabled.
27327      */
27328     enable : function(){
27329         this.disabled = false;
27330         this.pnode.removeClass("disabled");
27331     },
27332
27333     /**
27334      * Sets the content for this TabPanelItem.
27335      * @param {String} content The content
27336      * @param {Boolean} loadScripts true to look for and load scripts
27337      */
27338     setContent : function(content, loadScripts){
27339         this.bodyEl.update(content, loadScripts);
27340     },
27341
27342     /**
27343      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27344      * @return {Roo.UpdateManager} The UpdateManager
27345      */
27346     getUpdateManager : function(){
27347         return this.bodyEl.getUpdateManager();
27348     },
27349
27350     /**
27351      * Set a URL to be used to load the content for this TabPanelItem.
27352      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27353      * @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)
27354      * @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)
27355      * @return {Roo.UpdateManager} The UpdateManager
27356      */
27357     setUrl : function(url, params, loadOnce){
27358         if(this.refreshDelegate){
27359             this.un('activate', this.refreshDelegate);
27360         }
27361         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27362         this.on("activate", this.refreshDelegate);
27363         return this.bodyEl.getUpdateManager();
27364     },
27365
27366     /** @private */
27367     _handleRefresh : function(url, params, loadOnce){
27368         if(!loadOnce || !this.loaded){
27369             var updater = this.bodyEl.getUpdateManager();
27370             updater.update(url, params, this._setLoaded.createDelegate(this));
27371         }
27372     },
27373
27374     /**
27375      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27376      *   Will fail silently if the setUrl method has not been called.
27377      *   This does not activate the panel, just updates its content.
27378      */
27379     refresh : function(){
27380         if(this.refreshDelegate){
27381            this.loaded = false;
27382            this.refreshDelegate();
27383         }
27384     },
27385
27386     /** @private */
27387     _setLoaded : function(){
27388         this.loaded = true;
27389     },
27390
27391     /** @private */
27392     closeClick : function(e){
27393         var o = {};
27394         e.stopEvent();
27395         this.fireEvent("beforeclose", this, o);
27396         if(o.cancel !== true){
27397             this.tabPanel.removeTab(this.id);
27398         }
27399     },
27400     /**
27401      * The text displayed in the tooltip for the close icon.
27402      * @type String
27403      */
27404     closeText : "Close this tab"
27405 });
27406
27407 /** @private */
27408 Roo.TabPanel.prototype.createStrip = function(container){
27409     var strip = document.createElement("div");
27410     strip.className = "x-tabs-wrap";
27411     container.appendChild(strip);
27412     return strip;
27413 };
27414 /** @private */
27415 Roo.TabPanel.prototype.createStripList = function(strip){
27416     // div wrapper for retard IE
27417     // returns the "tr" element.
27418     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27419         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27420         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27421     return strip.firstChild.firstChild.firstChild.firstChild;
27422 };
27423 /** @private */
27424 Roo.TabPanel.prototype.createBody = function(container){
27425     var body = document.createElement("div");
27426     Roo.id(body, "tab-body");
27427     Roo.fly(body).addClass("x-tabs-body");
27428     container.appendChild(body);
27429     return body;
27430 };
27431 /** @private */
27432 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27433     var body = Roo.getDom(id);
27434     if(!body){
27435         body = document.createElement("div");
27436         body.id = id;
27437     }
27438     Roo.fly(body).addClass("x-tabs-item-body");
27439     bodyEl.insertBefore(body, bodyEl.firstChild);
27440     return body;
27441 };
27442 /** @private */
27443 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27444     var td = document.createElement("td");
27445     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27446     //stripEl.appendChild(td);
27447     if(closable){
27448         td.className = "x-tabs-closable";
27449         if(!this.closeTpl){
27450             this.closeTpl = new Roo.Template(
27451                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27452                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27453                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27454             );
27455         }
27456         var el = this.closeTpl.overwrite(td, {"text": text});
27457         var close = el.getElementsByTagName("div")[0];
27458         var inner = el.getElementsByTagName("em")[0];
27459         return {"el": el, "close": close, "inner": inner};
27460     } else {
27461         if(!this.tabTpl){
27462             this.tabTpl = new Roo.Template(
27463                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27464                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27465             );
27466         }
27467         var el = this.tabTpl.overwrite(td, {"text": text});
27468         var inner = el.getElementsByTagName("em")[0];
27469         return {"el": el, "inner": inner};
27470     }
27471 };/*
27472  * Based on:
27473  * Ext JS Library 1.1.1
27474  * Copyright(c) 2006-2007, Ext JS, LLC.
27475  *
27476  * Originally Released Under LGPL - original licence link has changed is not relivant.
27477  *
27478  * Fork - LGPL
27479  * <script type="text/javascript">
27480  */
27481
27482 /**
27483  * @class Roo.Button
27484  * @extends Roo.util.Observable
27485  * Simple Button class
27486  * @cfg {String} text The button text
27487  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27488  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27489  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27490  * @cfg {Object} scope The scope of the handler
27491  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27492  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27493  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27494  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27495  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27496  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27497    applies if enableToggle = true)
27498  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27499  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27500   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27501  * @constructor
27502  * Create a new button
27503  * @param {Object} config The config object
27504  */
27505 Roo.Button = function(renderTo, config)
27506 {
27507     if (!config) {
27508         config = renderTo;
27509         renderTo = config.renderTo || false;
27510     }
27511     
27512     Roo.apply(this, config);
27513     this.addEvents({
27514         /**
27515              * @event click
27516              * Fires when this button is clicked
27517              * @param {Button} this
27518              * @param {EventObject} e The click event
27519              */
27520             "click" : true,
27521         /**
27522              * @event toggle
27523              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27524              * @param {Button} this
27525              * @param {Boolean} pressed
27526              */
27527             "toggle" : true,
27528         /**
27529              * @event mouseover
27530              * Fires when the mouse hovers over the button
27531              * @param {Button} this
27532              * @param {Event} e The event object
27533              */
27534         'mouseover' : true,
27535         /**
27536              * @event mouseout
27537              * Fires when the mouse exits the button
27538              * @param {Button} this
27539              * @param {Event} e The event object
27540              */
27541         'mouseout': true,
27542          /**
27543              * @event render
27544              * Fires when the button is rendered
27545              * @param {Button} this
27546              */
27547         'render': true
27548     });
27549     if(this.menu){
27550         this.menu = Roo.menu.MenuMgr.get(this.menu);
27551     }
27552     // register listeners first!!  - so render can be captured..
27553     Roo.util.Observable.call(this);
27554     if(renderTo){
27555         this.render(renderTo);
27556     }
27557     
27558   
27559 };
27560
27561 Roo.extend(Roo.Button, Roo.util.Observable, {
27562     /**
27563      * 
27564      */
27565     
27566     /**
27567      * Read-only. True if this button is hidden
27568      * @type Boolean
27569      */
27570     hidden : false,
27571     /**
27572      * Read-only. True if this button is disabled
27573      * @type Boolean
27574      */
27575     disabled : false,
27576     /**
27577      * Read-only. True if this button is pressed (only if enableToggle = true)
27578      * @type Boolean
27579      */
27580     pressed : false,
27581
27582     /**
27583      * @cfg {Number} tabIndex 
27584      * The DOM tabIndex for this button (defaults to undefined)
27585      */
27586     tabIndex : undefined,
27587
27588     /**
27589      * @cfg {Boolean} enableToggle
27590      * True to enable pressed/not pressed toggling (defaults to false)
27591      */
27592     enableToggle: false,
27593     /**
27594      * @cfg {Mixed} menu
27595      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27596      */
27597     menu : undefined,
27598     /**
27599      * @cfg {String} menuAlign
27600      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27601      */
27602     menuAlign : "tl-bl?",
27603
27604     /**
27605      * @cfg {String} iconCls
27606      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27607      */
27608     iconCls : undefined,
27609     /**
27610      * @cfg {String} type
27611      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27612      */
27613     type : 'button',
27614
27615     // private
27616     menuClassTarget: 'tr',
27617
27618     /**
27619      * @cfg {String} clickEvent
27620      * The type of event to map to the button's event handler (defaults to 'click')
27621      */
27622     clickEvent : 'click',
27623
27624     /**
27625      * @cfg {Boolean} handleMouseEvents
27626      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27627      */
27628     handleMouseEvents : true,
27629
27630     /**
27631      * @cfg {String} tooltipType
27632      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27633      */
27634     tooltipType : 'qtip',
27635
27636     /**
27637      * @cfg {String} cls
27638      * A CSS class to apply to the button's main element.
27639      */
27640     
27641     /**
27642      * @cfg {Roo.Template} template (Optional)
27643      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27644      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27645      * require code modifications if required elements (e.g. a button) aren't present.
27646      */
27647
27648     // private
27649     render : function(renderTo){
27650         var btn;
27651         if(this.hideParent){
27652             this.parentEl = Roo.get(renderTo);
27653         }
27654         if(!this.dhconfig){
27655             if(!this.template){
27656                 if(!Roo.Button.buttonTemplate){
27657                     // hideous table template
27658                     Roo.Button.buttonTemplate = new Roo.Template(
27659                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27660                         '<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>',
27661                         "</tr></tbody></table>");
27662                 }
27663                 this.template = Roo.Button.buttonTemplate;
27664             }
27665             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27666             var btnEl = btn.child("button:first");
27667             btnEl.on('focus', this.onFocus, this);
27668             btnEl.on('blur', this.onBlur, this);
27669             if(this.cls){
27670                 btn.addClass(this.cls);
27671             }
27672             if(this.icon){
27673                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27674             }
27675             if(this.iconCls){
27676                 btnEl.addClass(this.iconCls);
27677                 if(!this.cls){
27678                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27679                 }
27680             }
27681             if(this.tabIndex !== undefined){
27682                 btnEl.dom.tabIndex = this.tabIndex;
27683             }
27684             if(this.tooltip){
27685                 if(typeof this.tooltip == 'object'){
27686                     Roo.QuickTips.tips(Roo.apply({
27687                           target: btnEl.id
27688                     }, this.tooltip));
27689                 } else {
27690                     btnEl.dom[this.tooltipType] = this.tooltip;
27691                 }
27692             }
27693         }else{
27694             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27695         }
27696         this.el = btn;
27697         if(this.id){
27698             this.el.dom.id = this.el.id = this.id;
27699         }
27700         if(this.menu){
27701             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27702             this.menu.on("show", this.onMenuShow, this);
27703             this.menu.on("hide", this.onMenuHide, this);
27704         }
27705         btn.addClass("x-btn");
27706         if(Roo.isIE && !Roo.isIE7){
27707             this.autoWidth.defer(1, this);
27708         }else{
27709             this.autoWidth();
27710         }
27711         if(this.handleMouseEvents){
27712             btn.on("mouseover", this.onMouseOver, this);
27713             btn.on("mouseout", this.onMouseOut, this);
27714             btn.on("mousedown", this.onMouseDown, this);
27715         }
27716         btn.on(this.clickEvent, this.onClick, this);
27717         //btn.on("mouseup", this.onMouseUp, this);
27718         if(this.hidden){
27719             this.hide();
27720         }
27721         if(this.disabled){
27722             this.disable();
27723         }
27724         Roo.ButtonToggleMgr.register(this);
27725         if(this.pressed){
27726             this.el.addClass("x-btn-pressed");
27727         }
27728         if(this.repeat){
27729             var repeater = new Roo.util.ClickRepeater(btn,
27730                 typeof this.repeat == "object" ? this.repeat : {}
27731             );
27732             repeater.on("click", this.onClick,  this);
27733         }
27734         
27735         this.fireEvent('render', this);
27736         
27737     },
27738     /**
27739      * Returns the button's underlying element
27740      * @return {Roo.Element} The element
27741      */
27742     getEl : function(){
27743         return this.el;  
27744     },
27745     
27746     /**
27747      * Destroys this Button and removes any listeners.
27748      */
27749     destroy : function(){
27750         Roo.ButtonToggleMgr.unregister(this);
27751         this.el.removeAllListeners();
27752         this.purgeListeners();
27753         this.el.remove();
27754     },
27755
27756     // private
27757     autoWidth : function(){
27758         if(this.el){
27759             this.el.setWidth("auto");
27760             if(Roo.isIE7 && Roo.isStrict){
27761                 var ib = this.el.child('button');
27762                 if(ib && ib.getWidth() > 20){
27763                     ib.clip();
27764                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27765                 }
27766             }
27767             if(this.minWidth){
27768                 if(this.hidden){
27769                     this.el.beginMeasure();
27770                 }
27771                 if(this.el.getWidth() < this.minWidth){
27772                     this.el.setWidth(this.minWidth);
27773                 }
27774                 if(this.hidden){
27775                     this.el.endMeasure();
27776                 }
27777             }
27778         }
27779     },
27780
27781     /**
27782      * Assigns this button's click handler
27783      * @param {Function} handler The function to call when the button is clicked
27784      * @param {Object} scope (optional) Scope for the function passed in
27785      */
27786     setHandler : function(handler, scope){
27787         this.handler = handler;
27788         this.scope = scope;  
27789     },
27790     
27791     /**
27792      * Sets this button's text
27793      * @param {String} text The button text
27794      */
27795     setText : function(text){
27796         this.text = text;
27797         if(this.el){
27798             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27799         }
27800         this.autoWidth();
27801     },
27802     
27803     /**
27804      * Gets the text for this button
27805      * @return {String} The button text
27806      */
27807     getText : function(){
27808         return this.text;  
27809     },
27810     
27811     /**
27812      * Show this button
27813      */
27814     show: function(){
27815         this.hidden = false;
27816         if(this.el){
27817             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27818         }
27819     },
27820     
27821     /**
27822      * Hide this button
27823      */
27824     hide: function(){
27825         this.hidden = true;
27826         if(this.el){
27827             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27828         }
27829     },
27830     
27831     /**
27832      * Convenience function for boolean show/hide
27833      * @param {Boolean} visible True to show, false to hide
27834      */
27835     setVisible: function(visible){
27836         if(visible) {
27837             this.show();
27838         }else{
27839             this.hide();
27840         }
27841     },
27842     
27843     /**
27844      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27845      * @param {Boolean} state (optional) Force a particular state
27846      */
27847     toggle : function(state){
27848         state = state === undefined ? !this.pressed : state;
27849         if(state != this.pressed){
27850             if(state){
27851                 this.el.addClass("x-btn-pressed");
27852                 this.pressed = true;
27853                 this.fireEvent("toggle", this, true);
27854             }else{
27855                 this.el.removeClass("x-btn-pressed");
27856                 this.pressed = false;
27857                 this.fireEvent("toggle", this, false);
27858             }
27859             if(this.toggleHandler){
27860                 this.toggleHandler.call(this.scope || this, this, state);
27861             }
27862         }
27863     },
27864     
27865     /**
27866      * Focus the button
27867      */
27868     focus : function(){
27869         this.el.child('button:first').focus();
27870     },
27871     
27872     /**
27873      * Disable this button
27874      */
27875     disable : function(){
27876         if(this.el){
27877             this.el.addClass("x-btn-disabled");
27878         }
27879         this.disabled = true;
27880     },
27881     
27882     /**
27883      * Enable this button
27884      */
27885     enable : function(){
27886         if(this.el){
27887             this.el.removeClass("x-btn-disabled");
27888         }
27889         this.disabled = false;
27890     },
27891
27892     /**
27893      * Convenience function for boolean enable/disable
27894      * @param {Boolean} enabled True to enable, false to disable
27895      */
27896     setDisabled : function(v){
27897         this[v !== true ? "enable" : "disable"]();
27898     },
27899
27900     // private
27901     onClick : function(e)
27902     {
27903         if(e){
27904             e.preventDefault();
27905         }
27906         if(e.button != 0){
27907             return;
27908         }
27909         if(!this.disabled){
27910             if(this.enableToggle){
27911                 this.toggle();
27912             }
27913             if(this.menu && !this.menu.isVisible()){
27914                 this.menu.show(this.el, this.menuAlign);
27915             }
27916             this.fireEvent("click", this, e);
27917             if(this.handler){
27918                 this.el.removeClass("x-btn-over");
27919                 this.handler.call(this.scope || this, this, e);
27920             }
27921         }
27922     },
27923     // private
27924     onMouseOver : function(e){
27925         if(!this.disabled){
27926             this.el.addClass("x-btn-over");
27927             this.fireEvent('mouseover', this, e);
27928         }
27929     },
27930     // private
27931     onMouseOut : function(e){
27932         if(!e.within(this.el,  true)){
27933             this.el.removeClass("x-btn-over");
27934             this.fireEvent('mouseout', this, e);
27935         }
27936     },
27937     // private
27938     onFocus : function(e){
27939         if(!this.disabled){
27940             this.el.addClass("x-btn-focus");
27941         }
27942     },
27943     // private
27944     onBlur : function(e){
27945         this.el.removeClass("x-btn-focus");
27946     },
27947     // private
27948     onMouseDown : function(e){
27949         if(!this.disabled && e.button == 0){
27950             this.el.addClass("x-btn-click");
27951             Roo.get(document).on('mouseup', this.onMouseUp, this);
27952         }
27953     },
27954     // private
27955     onMouseUp : function(e){
27956         if(e.button == 0){
27957             this.el.removeClass("x-btn-click");
27958             Roo.get(document).un('mouseup', this.onMouseUp, this);
27959         }
27960     },
27961     // private
27962     onMenuShow : function(e){
27963         this.el.addClass("x-btn-menu-active");
27964     },
27965     // private
27966     onMenuHide : function(e){
27967         this.el.removeClass("x-btn-menu-active");
27968     }   
27969 });
27970
27971 // Private utility class used by Button
27972 Roo.ButtonToggleMgr = function(){
27973    var groups = {};
27974    
27975    function toggleGroup(btn, state){
27976        if(state){
27977            var g = groups[btn.toggleGroup];
27978            for(var i = 0, l = g.length; i < l; i++){
27979                if(g[i] != btn){
27980                    g[i].toggle(false);
27981                }
27982            }
27983        }
27984    }
27985    
27986    return {
27987        register : function(btn){
27988            if(!btn.toggleGroup){
27989                return;
27990            }
27991            var g = groups[btn.toggleGroup];
27992            if(!g){
27993                g = groups[btn.toggleGroup] = [];
27994            }
27995            g.push(btn);
27996            btn.on("toggle", toggleGroup);
27997        },
27998        
27999        unregister : function(btn){
28000            if(!btn.toggleGroup){
28001                return;
28002            }
28003            var g = groups[btn.toggleGroup];
28004            if(g){
28005                g.remove(btn);
28006                btn.un("toggle", toggleGroup);
28007            }
28008        }
28009    };
28010 }();/*
28011  * Based on:
28012  * Ext JS Library 1.1.1
28013  * Copyright(c) 2006-2007, Ext JS, LLC.
28014  *
28015  * Originally Released Under LGPL - original licence link has changed is not relivant.
28016  *
28017  * Fork - LGPL
28018  * <script type="text/javascript">
28019  */
28020  
28021 /**
28022  * @class Roo.SplitButton
28023  * @extends Roo.Button
28024  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
28025  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
28026  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
28027  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
28028  * @cfg {String} arrowTooltip The title attribute of the arrow
28029  * @constructor
28030  * Create a new menu button
28031  * @param {String/HTMLElement/Element} renderTo The element to append the button to
28032  * @param {Object} config The config object
28033  */
28034 Roo.SplitButton = function(renderTo, config){
28035     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
28036     /**
28037      * @event arrowclick
28038      * Fires when this button's arrow is clicked
28039      * @param {SplitButton} this
28040      * @param {EventObject} e The click event
28041      */
28042     this.addEvents({"arrowclick":true});
28043 };
28044
28045 Roo.extend(Roo.SplitButton, Roo.Button, {
28046     render : function(renderTo){
28047         // this is one sweet looking template!
28048         var tpl = new Roo.Template(
28049             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
28050             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
28051             '<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>',
28052             "</tbody></table></td><td>",
28053             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
28054             '<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>',
28055             "</tbody></table></td></tr></table>"
28056         );
28057         var btn = tpl.append(renderTo, [this.text, this.type], true);
28058         var btnEl = btn.child("button");
28059         if(this.cls){
28060             btn.addClass(this.cls);
28061         }
28062         if(this.icon){
28063             btnEl.setStyle('background-image', 'url(' +this.icon +')');
28064         }
28065         if(this.iconCls){
28066             btnEl.addClass(this.iconCls);
28067             if(!this.cls){
28068                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
28069             }
28070         }
28071         this.el = btn;
28072         if(this.handleMouseEvents){
28073             btn.on("mouseover", this.onMouseOver, this);
28074             btn.on("mouseout", this.onMouseOut, this);
28075             btn.on("mousedown", this.onMouseDown, this);
28076             btn.on("mouseup", this.onMouseUp, this);
28077         }
28078         btn.on(this.clickEvent, this.onClick, this);
28079         if(this.tooltip){
28080             if(typeof this.tooltip == 'object'){
28081                 Roo.QuickTips.tips(Roo.apply({
28082                       target: btnEl.id
28083                 }, this.tooltip));
28084             } else {
28085                 btnEl.dom[this.tooltipType] = this.tooltip;
28086             }
28087         }
28088         if(this.arrowTooltip){
28089             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
28090         }
28091         if(this.hidden){
28092             this.hide();
28093         }
28094         if(this.disabled){
28095             this.disable();
28096         }
28097         if(this.pressed){
28098             this.el.addClass("x-btn-pressed");
28099         }
28100         if(Roo.isIE && !Roo.isIE7){
28101             this.autoWidth.defer(1, this);
28102         }else{
28103             this.autoWidth();
28104         }
28105         if(this.menu){
28106             this.menu.on("show", this.onMenuShow, this);
28107             this.menu.on("hide", this.onMenuHide, this);
28108         }
28109         this.fireEvent('render', this);
28110     },
28111
28112     // private
28113     autoWidth : function(){
28114         if(this.el){
28115             var tbl = this.el.child("table:first");
28116             var tbl2 = this.el.child("table:last");
28117             this.el.setWidth("auto");
28118             tbl.setWidth("auto");
28119             if(Roo.isIE7 && Roo.isStrict){
28120                 var ib = this.el.child('button:first');
28121                 if(ib && ib.getWidth() > 20){
28122                     ib.clip();
28123                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
28124                 }
28125             }
28126             if(this.minWidth){
28127                 if(this.hidden){
28128                     this.el.beginMeasure();
28129                 }
28130                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
28131                     tbl.setWidth(this.minWidth-tbl2.getWidth());
28132                 }
28133                 if(this.hidden){
28134                     this.el.endMeasure();
28135                 }
28136             }
28137             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
28138         } 
28139     },
28140     /**
28141      * Sets this button's click handler
28142      * @param {Function} handler The function to call when the button is clicked
28143      * @param {Object} scope (optional) Scope for the function passed above
28144      */
28145     setHandler : function(handler, scope){
28146         this.handler = handler;
28147         this.scope = scope;  
28148     },
28149     
28150     /**
28151      * Sets this button's arrow click handler
28152      * @param {Function} handler The function to call when the arrow is clicked
28153      * @param {Object} scope (optional) Scope for the function passed above
28154      */
28155     setArrowHandler : function(handler, scope){
28156         this.arrowHandler = handler;
28157         this.scope = scope;  
28158     },
28159     
28160     /**
28161      * Focus the button
28162      */
28163     focus : function(){
28164         if(this.el){
28165             this.el.child("button:first").focus();
28166         }
28167     },
28168
28169     // private
28170     onClick : function(e){
28171         e.preventDefault();
28172         if(!this.disabled){
28173             if(e.getTarget(".x-btn-menu-arrow-wrap")){
28174                 if(this.menu && !this.menu.isVisible()){
28175                     this.menu.show(this.el, this.menuAlign);
28176                 }
28177                 this.fireEvent("arrowclick", this, e);
28178                 if(this.arrowHandler){
28179                     this.arrowHandler.call(this.scope || this, this, e);
28180                 }
28181             }else{
28182                 this.fireEvent("click", this, e);
28183                 if(this.handler){
28184                     this.handler.call(this.scope || this, this, e);
28185                 }
28186             }
28187         }
28188     },
28189     // private
28190     onMouseDown : function(e){
28191         if(!this.disabled){
28192             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28193         }
28194     },
28195     // private
28196     onMouseUp : function(e){
28197         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28198     }   
28199 });
28200
28201
28202 // backwards compat
28203 Roo.MenuButton = Roo.SplitButton;/*
28204  * Based on:
28205  * Ext JS Library 1.1.1
28206  * Copyright(c) 2006-2007, Ext JS, LLC.
28207  *
28208  * Originally Released Under LGPL - original licence link has changed is not relivant.
28209  *
28210  * Fork - LGPL
28211  * <script type="text/javascript">
28212  */
28213
28214 /**
28215  * @class Roo.Toolbar
28216  * Basic Toolbar class.
28217  * @constructor
28218  * Creates a new Toolbar
28219  * @param {Object} container The config object
28220  */ 
28221 Roo.Toolbar = function(container, buttons, config)
28222 {
28223     /// old consturctor format still supported..
28224     if(container instanceof Array){ // omit the container for later rendering
28225         buttons = container;
28226         config = buttons;
28227         container = null;
28228     }
28229     if (typeof(container) == 'object' && container.xtype) {
28230         config = container;
28231         container = config.container;
28232         buttons = config.buttons || []; // not really - use items!!
28233     }
28234     var xitems = [];
28235     if (config && config.items) {
28236         xitems = config.items;
28237         delete config.items;
28238     }
28239     Roo.apply(this, config);
28240     this.buttons = buttons;
28241     
28242     if(container){
28243         this.render(container);
28244     }
28245     this.xitems = xitems;
28246     Roo.each(xitems, function(b) {
28247         this.add(b);
28248     }, this);
28249     
28250 };
28251
28252 Roo.Toolbar.prototype = {
28253     /**
28254      * @cfg {Array} items
28255      * array of button configs or elements to add (will be converted to a MixedCollection)
28256      */
28257     
28258     /**
28259      * @cfg {String/HTMLElement/Element} container
28260      * The id or element that will contain the toolbar
28261      */
28262     // private
28263     render : function(ct){
28264         this.el = Roo.get(ct);
28265         if(this.cls){
28266             this.el.addClass(this.cls);
28267         }
28268         // using a table allows for vertical alignment
28269         // 100% width is needed by Safari...
28270         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28271         this.tr = this.el.child("tr", true);
28272         var autoId = 0;
28273         this.items = new Roo.util.MixedCollection(false, function(o){
28274             return o.id || ("item" + (++autoId));
28275         });
28276         if(this.buttons){
28277             this.add.apply(this, this.buttons);
28278             delete this.buttons;
28279         }
28280     },
28281
28282     /**
28283      * Adds element(s) to the toolbar -- this function takes a variable number of 
28284      * arguments of mixed type and adds them to the toolbar.
28285      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28286      * <ul>
28287      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28288      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28289      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28290      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28291      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28292      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28293      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28294      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28295      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28296      * </ul>
28297      * @param {Mixed} arg2
28298      * @param {Mixed} etc.
28299      */
28300     add : function(){
28301         var a = arguments, l = a.length;
28302         for(var i = 0; i < l; i++){
28303             this._add(a[i]);
28304         }
28305     },
28306     // private..
28307     _add : function(el) {
28308         
28309         if (el.xtype) {
28310             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28311         }
28312         
28313         if (el.applyTo){ // some kind of form field
28314             return this.addField(el);
28315         } 
28316         if (el.render){ // some kind of Toolbar.Item
28317             return this.addItem(el);
28318         }
28319         if (typeof el == "string"){ // string
28320             if(el == "separator" || el == "-"){
28321                 return this.addSeparator();
28322             }
28323             if (el == " "){
28324                 return this.addSpacer();
28325             }
28326             if(el == "->"){
28327                 return this.addFill();
28328             }
28329             return this.addText(el);
28330             
28331         }
28332         if(el.tagName){ // element
28333             return this.addElement(el);
28334         }
28335         if(typeof el == "object"){ // must be button config?
28336             return this.addButton(el);
28337         }
28338         // and now what?!?!
28339         return false;
28340         
28341     },
28342     
28343     /**
28344      * Add an Xtype element
28345      * @param {Object} xtype Xtype Object
28346      * @return {Object} created Object
28347      */
28348     addxtype : function(e){
28349         return this.add(e);  
28350     },
28351     
28352     /**
28353      * Returns the Element for this toolbar.
28354      * @return {Roo.Element}
28355      */
28356     getEl : function(){
28357         return this.el;  
28358     },
28359     
28360     /**
28361      * Adds a separator
28362      * @return {Roo.Toolbar.Item} The separator item
28363      */
28364     addSeparator : function(){
28365         return this.addItem(new Roo.Toolbar.Separator());
28366     },
28367
28368     /**
28369      * Adds a spacer element
28370      * @return {Roo.Toolbar.Spacer} The spacer item
28371      */
28372     addSpacer : function(){
28373         return this.addItem(new Roo.Toolbar.Spacer());
28374     },
28375
28376     /**
28377      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28378      * @return {Roo.Toolbar.Fill} The fill item
28379      */
28380     addFill : function(){
28381         return this.addItem(new Roo.Toolbar.Fill());
28382     },
28383
28384     /**
28385      * Adds any standard HTML element to the toolbar
28386      * @param {String/HTMLElement/Element} el The element or id of the element to add
28387      * @return {Roo.Toolbar.Item} The element's item
28388      */
28389     addElement : function(el){
28390         return this.addItem(new Roo.Toolbar.Item(el));
28391     },
28392     /**
28393      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28394      * @type Roo.util.MixedCollection  
28395      */
28396     items : false,
28397      
28398     /**
28399      * Adds any Toolbar.Item or subclass
28400      * @param {Roo.Toolbar.Item} item
28401      * @return {Roo.Toolbar.Item} The item
28402      */
28403     addItem : function(item){
28404         var td = this.nextBlock();
28405         item.render(td);
28406         this.items.add(item);
28407         return item;
28408     },
28409     
28410     /**
28411      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28412      * @param {Object/Array} config A button config or array of configs
28413      * @return {Roo.Toolbar.Button/Array}
28414      */
28415     addButton : function(config){
28416         if(config instanceof Array){
28417             var buttons = [];
28418             for(var i = 0, len = config.length; i < len; i++) {
28419                 buttons.push(this.addButton(config[i]));
28420             }
28421             return buttons;
28422         }
28423         var b = config;
28424         if(!(config instanceof Roo.Toolbar.Button)){
28425             b = config.split ?
28426                 new Roo.Toolbar.SplitButton(config) :
28427                 new Roo.Toolbar.Button(config);
28428         }
28429         var td = this.nextBlock();
28430         b.render(td);
28431         this.items.add(b);
28432         return b;
28433     },
28434     
28435     /**
28436      * Adds text to the toolbar
28437      * @param {String} text The text to add
28438      * @return {Roo.Toolbar.Item} The element's item
28439      */
28440     addText : function(text){
28441         return this.addItem(new Roo.Toolbar.TextItem(text));
28442     },
28443     
28444     /**
28445      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28446      * @param {Number} index The index where the item is to be inserted
28447      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28448      * @return {Roo.Toolbar.Button/Item}
28449      */
28450     insertButton : function(index, item){
28451         if(item instanceof Array){
28452             var buttons = [];
28453             for(var i = 0, len = item.length; i < len; i++) {
28454                buttons.push(this.insertButton(index + i, item[i]));
28455             }
28456             return buttons;
28457         }
28458         if (!(item instanceof Roo.Toolbar.Button)){
28459            item = new Roo.Toolbar.Button(item);
28460         }
28461         var td = document.createElement("td");
28462         this.tr.insertBefore(td, this.tr.childNodes[index]);
28463         item.render(td);
28464         this.items.insert(index, item);
28465         return item;
28466     },
28467     
28468     /**
28469      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28470      * @param {Object} config
28471      * @return {Roo.Toolbar.Item} The element's item
28472      */
28473     addDom : function(config, returnEl){
28474         var td = this.nextBlock();
28475         Roo.DomHelper.overwrite(td, config);
28476         var ti = new Roo.Toolbar.Item(td.firstChild);
28477         ti.render(td);
28478         this.items.add(ti);
28479         return ti;
28480     },
28481
28482     /**
28483      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28484      * @type Roo.util.MixedCollection  
28485      */
28486     fields : false,
28487     
28488     /**
28489      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28490      * Note: the field should not have been rendered yet. For a field that has already been
28491      * rendered, use {@link #addElement}.
28492      * @param {Roo.form.Field} field
28493      * @return {Roo.ToolbarItem}
28494      */
28495      
28496       
28497     addField : function(field) {
28498         if (!this.fields) {
28499             var autoId = 0;
28500             this.fields = new Roo.util.MixedCollection(false, function(o){
28501                 return o.id || ("item" + (++autoId));
28502             });
28503
28504         }
28505         
28506         var td = this.nextBlock();
28507         field.render(td);
28508         var ti = new Roo.Toolbar.Item(td.firstChild);
28509         ti.render(td);
28510         this.items.add(ti);
28511         this.fields.add(field);
28512         return ti;
28513     },
28514     /**
28515      * Hide the toolbar
28516      * @method hide
28517      */
28518      
28519       
28520     hide : function()
28521     {
28522         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28523         this.el.child('div').hide();
28524     },
28525     /**
28526      * Show the toolbar
28527      * @method show
28528      */
28529     show : function()
28530     {
28531         this.el.child('div').show();
28532     },
28533       
28534     // private
28535     nextBlock : function(){
28536         var td = document.createElement("td");
28537         this.tr.appendChild(td);
28538         return td;
28539     },
28540
28541     // private
28542     destroy : function(){
28543         if(this.items){ // rendered?
28544             Roo.destroy.apply(Roo, this.items.items);
28545         }
28546         if(this.fields){ // rendered?
28547             Roo.destroy.apply(Roo, this.fields.items);
28548         }
28549         Roo.Element.uncache(this.el, this.tr);
28550     }
28551 };
28552
28553 /**
28554  * @class Roo.Toolbar.Item
28555  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28556  * @constructor
28557  * Creates a new Item
28558  * @param {HTMLElement} el 
28559  */
28560 Roo.Toolbar.Item = function(el){
28561     var cfg = {};
28562     if (typeof (el.xtype) != 'undefined') {
28563         cfg = el;
28564         el = cfg.el;
28565     }
28566     
28567     this.el = Roo.getDom(el);
28568     this.id = Roo.id(this.el);
28569     this.hidden = false;
28570     
28571     this.addEvents({
28572          /**
28573              * @event render
28574              * Fires when the button is rendered
28575              * @param {Button} this
28576              */
28577         'render': true
28578     });
28579     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
28580 };
28581 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
28582 //Roo.Toolbar.Item.prototype = {
28583     
28584     /**
28585      * Get this item's HTML Element
28586      * @return {HTMLElement}
28587      */
28588     getEl : function(){
28589        return this.el;  
28590     },
28591
28592     // private
28593     render : function(td){
28594         
28595          this.td = td;
28596         td.appendChild(this.el);
28597         
28598         this.fireEvent('render', this);
28599     },
28600     
28601     /**
28602      * Removes and destroys this item.
28603      */
28604     destroy : function(){
28605         this.td.parentNode.removeChild(this.td);
28606     },
28607     
28608     /**
28609      * Shows this item.
28610      */
28611     show: function(){
28612         this.hidden = false;
28613         this.td.style.display = "";
28614     },
28615     
28616     /**
28617      * Hides this item.
28618      */
28619     hide: function(){
28620         this.hidden = true;
28621         this.td.style.display = "none";
28622     },
28623     
28624     /**
28625      * Convenience function for boolean show/hide.
28626      * @param {Boolean} visible true to show/false to hide
28627      */
28628     setVisible: function(visible){
28629         if(visible) {
28630             this.show();
28631         }else{
28632             this.hide();
28633         }
28634     },
28635     
28636     /**
28637      * Try to focus this item.
28638      */
28639     focus : function(){
28640         Roo.fly(this.el).focus();
28641     },
28642     
28643     /**
28644      * Disables this item.
28645      */
28646     disable : function(){
28647         Roo.fly(this.td).addClass("x-item-disabled");
28648         this.disabled = true;
28649         this.el.disabled = true;
28650     },
28651     
28652     /**
28653      * Enables this item.
28654      */
28655     enable : function(){
28656         Roo.fly(this.td).removeClass("x-item-disabled");
28657         this.disabled = false;
28658         this.el.disabled = false;
28659     }
28660 });
28661
28662
28663 /**
28664  * @class Roo.Toolbar.Separator
28665  * @extends Roo.Toolbar.Item
28666  * A simple toolbar separator class
28667  * @constructor
28668  * Creates a new Separator
28669  */
28670 Roo.Toolbar.Separator = function(cfg){
28671     
28672     var s = document.createElement("span");
28673     s.className = "ytb-sep";
28674     if (cfg) {
28675         cfg.el = s;
28676     }
28677     
28678     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
28679 };
28680 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28681     enable:Roo.emptyFn,
28682     disable:Roo.emptyFn,
28683     focus:Roo.emptyFn
28684 });
28685
28686 /**
28687  * @class Roo.Toolbar.Spacer
28688  * @extends Roo.Toolbar.Item
28689  * A simple element that adds extra horizontal space to a toolbar.
28690  * @constructor
28691  * Creates a new Spacer
28692  */
28693 Roo.Toolbar.Spacer = function(cfg){
28694     var s = document.createElement("div");
28695     s.className = "ytb-spacer";
28696     if (cfg) {
28697         cfg.el = s;
28698     }
28699     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
28700 };
28701 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28702     enable:Roo.emptyFn,
28703     disable:Roo.emptyFn,
28704     focus:Roo.emptyFn
28705 });
28706
28707 /**
28708  * @class Roo.Toolbar.Fill
28709  * @extends Roo.Toolbar.Spacer
28710  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28711  * @constructor
28712  * Creates a new Spacer
28713  */
28714 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28715     // private
28716     render : function(td){
28717         td.style.width = '100%';
28718         Roo.Toolbar.Fill.superclass.render.call(this, td);
28719     }
28720 });
28721
28722 /**
28723  * @class Roo.Toolbar.TextItem
28724  * @extends Roo.Toolbar.Item
28725  * A simple class that renders text directly into a toolbar.
28726  * @constructor
28727  * Creates a new TextItem
28728  * @param {String} text
28729  */
28730 Roo.Toolbar.TextItem = function(cfg){
28731     var  text = cfg || "";
28732     if (typeof(cfg) == 'object') {
28733         text = cfg.text || "";
28734     }  else {
28735         cfg = null;
28736     }
28737     var s = document.createElement("span");
28738     s.className = "ytb-text";
28739     s.innerHTML = text;
28740     if (cfg) {
28741         cfg.el  = s;
28742     }
28743     
28744     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
28745 };
28746 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28747     
28748      
28749     enable:Roo.emptyFn,
28750     disable:Roo.emptyFn,
28751     focus:Roo.emptyFn
28752 });
28753
28754 /**
28755  * @class Roo.Toolbar.Button
28756  * @extends Roo.Button
28757  * A button that renders into a toolbar.
28758  * @constructor
28759  * Creates a new Button
28760  * @param {Object} config A standard {@link Roo.Button} config object
28761  */
28762 Roo.Toolbar.Button = function(config){
28763     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28764 };
28765 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28766     render : function(td){
28767         this.td = td;
28768         Roo.Toolbar.Button.superclass.render.call(this, td);
28769     },
28770     
28771     /**
28772      * Removes and destroys this button
28773      */
28774     destroy : function(){
28775         Roo.Toolbar.Button.superclass.destroy.call(this);
28776         this.td.parentNode.removeChild(this.td);
28777     },
28778     
28779     /**
28780      * Shows this button
28781      */
28782     show: function(){
28783         this.hidden = false;
28784         this.td.style.display = "";
28785     },
28786     
28787     /**
28788      * Hides this button
28789      */
28790     hide: function(){
28791         this.hidden = true;
28792         this.td.style.display = "none";
28793     },
28794
28795     /**
28796      * Disables this item
28797      */
28798     disable : function(){
28799         Roo.fly(this.td).addClass("x-item-disabled");
28800         this.disabled = true;
28801     },
28802
28803     /**
28804      * Enables this item
28805      */
28806     enable : function(){
28807         Roo.fly(this.td).removeClass("x-item-disabled");
28808         this.disabled = false;
28809     }
28810 });
28811 // backwards compat
28812 Roo.ToolbarButton = Roo.Toolbar.Button;
28813
28814 /**
28815  * @class Roo.Toolbar.SplitButton
28816  * @extends Roo.SplitButton
28817  * A menu button that renders into a toolbar.
28818  * @constructor
28819  * Creates a new SplitButton
28820  * @param {Object} config A standard {@link Roo.SplitButton} config object
28821  */
28822 Roo.Toolbar.SplitButton = function(config){
28823     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28824 };
28825 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28826     render : function(td){
28827         this.td = td;
28828         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28829     },
28830     
28831     /**
28832      * Removes and destroys this button
28833      */
28834     destroy : function(){
28835         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28836         this.td.parentNode.removeChild(this.td);
28837     },
28838     
28839     /**
28840      * Shows this button
28841      */
28842     show: function(){
28843         this.hidden = false;
28844         this.td.style.display = "";
28845     },
28846     
28847     /**
28848      * Hides this button
28849      */
28850     hide: function(){
28851         this.hidden = true;
28852         this.td.style.display = "none";
28853     }
28854 });
28855
28856 // backwards compat
28857 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28858  * Based on:
28859  * Ext JS Library 1.1.1
28860  * Copyright(c) 2006-2007, Ext JS, LLC.
28861  *
28862  * Originally Released Under LGPL - original licence link has changed is not relivant.
28863  *
28864  * Fork - LGPL
28865  * <script type="text/javascript">
28866  */
28867  
28868 /**
28869  * @class Roo.PagingToolbar
28870  * @extends Roo.Toolbar
28871  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28872  * @constructor
28873  * Create a new PagingToolbar
28874  * @param {Object} config The config object
28875  */
28876 Roo.PagingToolbar = function(el, ds, config)
28877 {
28878     // old args format still supported... - xtype is prefered..
28879     if (typeof(el) == 'object' && el.xtype) {
28880         // created from xtype...
28881         config = el;
28882         ds = el.dataSource;
28883         el = config.container;
28884     }
28885     var items = [];
28886     if (config.items) {
28887         items = config.items;
28888         config.items = [];
28889     }
28890     
28891     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28892     this.ds = ds;
28893     this.cursor = 0;
28894     this.renderButtons(this.el);
28895     this.bind(ds);
28896     
28897     // supprot items array.
28898    
28899     Roo.each(items, function(e) {
28900         this.add(Roo.factory(e));
28901     },this);
28902     
28903 };
28904
28905 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28906     /**
28907      * @cfg {Roo.data.Store} dataSource
28908      * The underlying data store providing the paged data
28909      */
28910     /**
28911      * @cfg {String/HTMLElement/Element} container
28912      * container The id or element that will contain the toolbar
28913      */
28914     /**
28915      * @cfg {Boolean} displayInfo
28916      * True to display the displayMsg (defaults to false)
28917      */
28918     /**
28919      * @cfg {Number} pageSize
28920      * The number of records to display per page (defaults to 20)
28921      */
28922     pageSize: 20,
28923     /**
28924      * @cfg {String} displayMsg
28925      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28926      */
28927     displayMsg : 'Displaying {0} - {1} of {2}',
28928     /**
28929      * @cfg {String} emptyMsg
28930      * The message to display when no records are found (defaults to "No data to display")
28931      */
28932     emptyMsg : 'No data to display',
28933     /**
28934      * Customizable piece of the default paging text (defaults to "Page")
28935      * @type String
28936      */
28937     beforePageText : "Page",
28938     /**
28939      * Customizable piece of the default paging text (defaults to "of %0")
28940      * @type String
28941      */
28942     afterPageText : "of {0}",
28943     /**
28944      * Customizable piece of the default paging text (defaults to "First Page")
28945      * @type String
28946      */
28947     firstText : "First Page",
28948     /**
28949      * Customizable piece of the default paging text (defaults to "Previous Page")
28950      * @type String
28951      */
28952     prevText : "Previous Page",
28953     /**
28954      * Customizable piece of the default paging text (defaults to "Next Page")
28955      * @type String
28956      */
28957     nextText : "Next Page",
28958     /**
28959      * Customizable piece of the default paging text (defaults to "Last Page")
28960      * @type String
28961      */
28962     lastText : "Last Page",
28963     /**
28964      * Customizable piece of the default paging text (defaults to "Refresh")
28965      * @type String
28966      */
28967     refreshText : "Refresh",
28968
28969     // private
28970     renderButtons : function(el){
28971         Roo.PagingToolbar.superclass.render.call(this, el);
28972         this.first = this.addButton({
28973             tooltip: this.firstText,
28974             cls: "x-btn-icon x-grid-page-first",
28975             disabled: true,
28976             handler: this.onClick.createDelegate(this, ["first"])
28977         });
28978         this.prev = this.addButton({
28979             tooltip: this.prevText,
28980             cls: "x-btn-icon x-grid-page-prev",
28981             disabled: true,
28982             handler: this.onClick.createDelegate(this, ["prev"])
28983         });
28984         //this.addSeparator();
28985         this.add(this.beforePageText);
28986         this.field = Roo.get(this.addDom({
28987            tag: "input",
28988            type: "text",
28989            size: "3",
28990            value: "1",
28991            cls: "x-grid-page-number"
28992         }).el);
28993         this.field.on("keydown", this.onPagingKeydown, this);
28994         this.field.on("focus", function(){this.dom.select();});
28995         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28996         this.field.setHeight(18);
28997         //this.addSeparator();
28998         this.next = this.addButton({
28999             tooltip: this.nextText,
29000             cls: "x-btn-icon x-grid-page-next",
29001             disabled: true,
29002             handler: this.onClick.createDelegate(this, ["next"])
29003         });
29004         this.last = this.addButton({
29005             tooltip: this.lastText,
29006             cls: "x-btn-icon x-grid-page-last",
29007             disabled: true,
29008             handler: this.onClick.createDelegate(this, ["last"])
29009         });
29010         //this.addSeparator();
29011         this.loading = this.addButton({
29012             tooltip: this.refreshText,
29013             cls: "x-btn-icon x-grid-loading",
29014             handler: this.onClick.createDelegate(this, ["refresh"])
29015         });
29016
29017         if(this.displayInfo){
29018             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
29019         }
29020     },
29021
29022     // private
29023     updateInfo : function(){
29024         if(this.displayEl){
29025             var count = this.ds.getCount();
29026             var msg = count == 0 ?
29027                 this.emptyMsg :
29028                 String.format(
29029                     this.displayMsg,
29030                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
29031                 );
29032             this.displayEl.update(msg);
29033         }
29034     },
29035
29036     // private
29037     onLoad : function(ds, r, o){
29038        this.cursor = o.params ? o.params.start : 0;
29039        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
29040
29041        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
29042        this.field.dom.value = ap;
29043        this.first.setDisabled(ap == 1);
29044        this.prev.setDisabled(ap == 1);
29045        this.next.setDisabled(ap == ps);
29046        this.last.setDisabled(ap == ps);
29047        this.loading.enable();
29048        this.updateInfo();
29049     },
29050
29051     // private
29052     getPageData : function(){
29053         var total = this.ds.getTotalCount();
29054         return {
29055             total : total,
29056             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
29057             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
29058         };
29059     },
29060
29061     // private
29062     onLoadError : function(){
29063         this.loading.enable();
29064     },
29065
29066     // private
29067     onPagingKeydown : function(e){
29068         var k = e.getKey();
29069         var d = this.getPageData();
29070         if(k == e.RETURN){
29071             var v = this.field.dom.value, pageNum;
29072             if(!v || isNaN(pageNum = parseInt(v, 10))){
29073                 this.field.dom.value = d.activePage;
29074                 return;
29075             }
29076             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
29077             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29078             e.stopEvent();
29079         }
29080         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))
29081         {
29082           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
29083           this.field.dom.value = pageNum;
29084           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
29085           e.stopEvent();
29086         }
29087         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29088         {
29089           var v = this.field.dom.value, pageNum; 
29090           var increment = (e.shiftKey) ? 10 : 1;
29091           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
29092             increment *= -1;
29093           }
29094           if(!v || isNaN(pageNum = parseInt(v, 10))) {
29095             this.field.dom.value = d.activePage;
29096             return;
29097           }
29098           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
29099           {
29100             this.field.dom.value = parseInt(v, 10) + increment;
29101             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
29102             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29103           }
29104           e.stopEvent();
29105         }
29106     },
29107
29108     // private
29109     beforeLoad : function(){
29110         if(this.loading){
29111             this.loading.disable();
29112         }
29113     },
29114
29115     // private
29116     onClick : function(which){
29117         var ds = this.ds;
29118         switch(which){
29119             case "first":
29120                 ds.load({params:{start: 0, limit: this.pageSize}});
29121             break;
29122             case "prev":
29123                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
29124             break;
29125             case "next":
29126                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
29127             break;
29128             case "last":
29129                 var total = ds.getTotalCount();
29130                 var extra = total % this.pageSize;
29131                 var lastStart = extra ? (total - extra) : total-this.pageSize;
29132                 ds.load({params:{start: lastStart, limit: this.pageSize}});
29133             break;
29134             case "refresh":
29135                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
29136             break;
29137         }
29138     },
29139
29140     /**
29141      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
29142      * @param {Roo.data.Store} store The data store to unbind
29143      */
29144     unbind : function(ds){
29145         ds.un("beforeload", this.beforeLoad, this);
29146         ds.un("load", this.onLoad, this);
29147         ds.un("loadexception", this.onLoadError, this);
29148         ds.un("remove", this.updateInfo, this);
29149         ds.un("add", this.updateInfo, this);
29150         this.ds = undefined;
29151     },
29152
29153     /**
29154      * Binds the paging toolbar to the specified {@link Roo.data.Store}
29155      * @param {Roo.data.Store} store The data store to bind
29156      */
29157     bind : function(ds){
29158         ds.on("beforeload", this.beforeLoad, this);
29159         ds.on("load", this.onLoad, this);
29160         ds.on("loadexception", this.onLoadError, this);
29161         ds.on("remove", this.updateInfo, this);
29162         ds.on("add", this.updateInfo, this);
29163         this.ds = ds;
29164     }
29165 });/*
29166  * Based on:
29167  * Ext JS Library 1.1.1
29168  * Copyright(c) 2006-2007, Ext JS, LLC.
29169  *
29170  * Originally Released Under LGPL - original licence link has changed is not relivant.
29171  *
29172  * Fork - LGPL
29173  * <script type="text/javascript">
29174  */
29175
29176 /**
29177  * @class Roo.Resizable
29178  * @extends Roo.util.Observable
29179  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
29180  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
29181  * 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
29182  * the element will be wrapped for you automatically.</p>
29183  * <p>Here is the list of valid resize handles:</p>
29184  * <pre>
29185 Value   Description
29186 ------  -------------------
29187  'n'     north
29188  's'     south
29189  'e'     east
29190  'w'     west
29191  'nw'    northwest
29192  'sw'    southwest
29193  'se'    southeast
29194  'ne'    northeast
29195  'hd'    horizontal drag
29196  'all'   all
29197 </pre>
29198  * <p>Here's an example showing the creation of a typical Resizable:</p>
29199  * <pre><code>
29200 var resizer = new Roo.Resizable("element-id", {
29201     handles: 'all',
29202     minWidth: 200,
29203     minHeight: 100,
29204     maxWidth: 500,
29205     maxHeight: 400,
29206     pinned: true
29207 });
29208 resizer.on("resize", myHandler);
29209 </code></pre>
29210  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
29211  * resizer.east.setDisplayed(false);</p>
29212  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
29213  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
29214  * resize operation's new size (defaults to [0, 0])
29215  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
29216  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
29217  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29218  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29219  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29220  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29221  * @cfg {Number} width The width of the element in pixels (defaults to null)
29222  * @cfg {Number} height The height of the element in pixels (defaults to null)
29223  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29224  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29225  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29226  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29227  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29228  * in favor of the handles config option (defaults to false)
29229  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29230  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29231  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29232  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29233  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29234  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29235  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29236  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29237  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29238  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29239  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29240  * @constructor
29241  * Create a new resizable component
29242  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29243  * @param {Object} config configuration options
29244   */
29245 Roo.Resizable = function(el, config)
29246 {
29247     this.el = Roo.get(el);
29248
29249     if(config && config.wrap){
29250         config.resizeChild = this.el;
29251         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29252         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29253         this.el.setStyle("overflow", "hidden");
29254         this.el.setPositioning(config.resizeChild.getPositioning());
29255         config.resizeChild.clearPositioning();
29256         if(!config.width || !config.height){
29257             var csize = config.resizeChild.getSize();
29258             this.el.setSize(csize.width, csize.height);
29259         }
29260         if(config.pinned && !config.adjustments){
29261             config.adjustments = "auto";
29262         }
29263     }
29264
29265     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29266     this.proxy.unselectable();
29267     this.proxy.enableDisplayMode('block');
29268
29269     Roo.apply(this, config);
29270
29271     if(this.pinned){
29272         this.disableTrackOver = true;
29273         this.el.addClass("x-resizable-pinned");
29274     }
29275     // if the element isn't positioned, make it relative
29276     var position = this.el.getStyle("position");
29277     if(position != "absolute" && position != "fixed"){
29278         this.el.setStyle("position", "relative");
29279     }
29280     if(!this.handles){ // no handles passed, must be legacy style
29281         this.handles = 's,e,se';
29282         if(this.multiDirectional){
29283             this.handles += ',n,w';
29284         }
29285     }
29286     if(this.handles == "all"){
29287         this.handles = "n s e w ne nw se sw";
29288     }
29289     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29290     var ps = Roo.Resizable.positions;
29291     for(var i = 0, len = hs.length; i < len; i++){
29292         if(hs[i] && ps[hs[i]]){
29293             var pos = ps[hs[i]];
29294             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29295         }
29296     }
29297     // legacy
29298     this.corner = this.southeast;
29299     
29300     // updateBox = the box can move..
29301     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29302         this.updateBox = true;
29303     }
29304
29305     this.activeHandle = null;
29306
29307     if(this.resizeChild){
29308         if(typeof this.resizeChild == "boolean"){
29309             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29310         }else{
29311             this.resizeChild = Roo.get(this.resizeChild, true);
29312         }
29313     }
29314     
29315     if(this.adjustments == "auto"){
29316         var rc = this.resizeChild;
29317         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29318         if(rc && (hw || hn)){
29319             rc.position("relative");
29320             rc.setLeft(hw ? hw.el.getWidth() : 0);
29321             rc.setTop(hn ? hn.el.getHeight() : 0);
29322         }
29323         this.adjustments = [
29324             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29325             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29326         ];
29327     }
29328
29329     if(this.draggable){
29330         this.dd = this.dynamic ?
29331             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29332         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29333     }
29334
29335     // public events
29336     this.addEvents({
29337         /**
29338          * @event beforeresize
29339          * Fired before resize is allowed. Set enabled to false to cancel resize.
29340          * @param {Roo.Resizable} this
29341          * @param {Roo.EventObject} e The mousedown event
29342          */
29343         "beforeresize" : true,
29344         /**
29345          * @event resizing
29346          * Fired a resizing.
29347          * @param {Roo.Resizable} this
29348          * @param {Number} x The new x position
29349          * @param {Number} y The new y position
29350          * @param {Number} w The new w width
29351          * @param {Number} h The new h hight
29352          * @param {Roo.EventObject} e The mouseup event
29353          */
29354         "resizing" : true,
29355         /**
29356          * @event resize
29357          * Fired after a resize.
29358          * @param {Roo.Resizable} this
29359          * @param {Number} width The new width
29360          * @param {Number} height The new height
29361          * @param {Roo.EventObject} e The mouseup event
29362          */
29363         "resize" : true
29364     });
29365
29366     if(this.width !== null && this.height !== null){
29367         this.resizeTo(this.width, this.height);
29368     }else{
29369         this.updateChildSize();
29370     }
29371     if(Roo.isIE){
29372         this.el.dom.style.zoom = 1;
29373     }
29374     Roo.Resizable.superclass.constructor.call(this);
29375 };
29376
29377 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29378         resizeChild : false,
29379         adjustments : [0, 0],
29380         minWidth : 5,
29381         minHeight : 5,
29382         maxWidth : 10000,
29383         maxHeight : 10000,
29384         enabled : true,
29385         animate : false,
29386         duration : .35,
29387         dynamic : false,
29388         handles : false,
29389         multiDirectional : false,
29390         disableTrackOver : false,
29391         easing : 'easeOutStrong',
29392         widthIncrement : 0,
29393         heightIncrement : 0,
29394         pinned : false,
29395         width : null,
29396         height : null,
29397         preserveRatio : false,
29398         transparent: false,
29399         minX: 0,
29400         minY: 0,
29401         draggable: false,
29402
29403         /**
29404          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29405          */
29406         constrainTo: undefined,
29407         /**
29408          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29409          */
29410         resizeRegion: undefined,
29411
29412
29413     /**
29414      * Perform a manual resize
29415      * @param {Number} width
29416      * @param {Number} height
29417      */
29418     resizeTo : function(width, height){
29419         this.el.setSize(width, height);
29420         this.updateChildSize();
29421         this.fireEvent("resize", this, width, height, null);
29422     },
29423
29424     // private
29425     startSizing : function(e, handle){
29426         this.fireEvent("beforeresize", this, e);
29427         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29428
29429             if(!this.overlay){
29430                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29431                 this.overlay.unselectable();
29432                 this.overlay.enableDisplayMode("block");
29433                 this.overlay.on("mousemove", this.onMouseMove, this);
29434                 this.overlay.on("mouseup", this.onMouseUp, this);
29435             }
29436             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29437
29438             this.resizing = true;
29439             this.startBox = this.el.getBox();
29440             this.startPoint = e.getXY();
29441             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29442                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29443
29444             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29445             this.overlay.show();
29446
29447             if(this.constrainTo) {
29448                 var ct = Roo.get(this.constrainTo);
29449                 this.resizeRegion = ct.getRegion().adjust(
29450                     ct.getFrameWidth('t'),
29451                     ct.getFrameWidth('l'),
29452                     -ct.getFrameWidth('b'),
29453                     -ct.getFrameWidth('r')
29454                 );
29455             }
29456
29457             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29458             this.proxy.show();
29459             this.proxy.setBox(this.startBox);
29460             if(!this.dynamic){
29461                 this.proxy.setStyle('visibility', 'visible');
29462             }
29463         }
29464     },
29465
29466     // private
29467     onMouseDown : function(handle, e){
29468         if(this.enabled){
29469             e.stopEvent();
29470             this.activeHandle = handle;
29471             this.startSizing(e, handle);
29472         }
29473     },
29474
29475     // private
29476     onMouseUp : function(e){
29477         var size = this.resizeElement();
29478         this.resizing = false;
29479         this.handleOut();
29480         this.overlay.hide();
29481         this.proxy.hide();
29482         this.fireEvent("resize", this, size.width, size.height, e);
29483     },
29484
29485     // private
29486     updateChildSize : function(){
29487         
29488         if(this.resizeChild){
29489             var el = this.el;
29490             var child = this.resizeChild;
29491             var adj = this.adjustments;
29492             if(el.dom.offsetWidth){
29493                 var b = el.getSize(true);
29494                 child.setSize(b.width+adj[0], b.height+adj[1]);
29495             }
29496             // Second call here for IE
29497             // The first call enables instant resizing and
29498             // the second call corrects scroll bars if they
29499             // exist
29500             if(Roo.isIE){
29501                 setTimeout(function(){
29502                     if(el.dom.offsetWidth){
29503                         var b = el.getSize(true);
29504                         child.setSize(b.width+adj[0], b.height+adj[1]);
29505                     }
29506                 }, 10);
29507             }
29508         }
29509     },
29510
29511     // private
29512     snap : function(value, inc, min){
29513         if(!inc || !value) {
29514             return value;
29515         }
29516         var newValue = value;
29517         var m = value % inc;
29518         if(m > 0){
29519             if(m > (inc/2)){
29520                 newValue = value + (inc-m);
29521             }else{
29522                 newValue = value - m;
29523             }
29524         }
29525         return Math.max(min, newValue);
29526     },
29527
29528     // private
29529     resizeElement : function(){
29530         var box = this.proxy.getBox();
29531         if(this.updateBox){
29532             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29533         }else{
29534             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29535         }
29536         this.updateChildSize();
29537         if(!this.dynamic){
29538             this.proxy.hide();
29539         }
29540         return box;
29541     },
29542
29543     // private
29544     constrain : function(v, diff, m, mx){
29545         if(v - diff < m){
29546             diff = v - m;
29547         }else if(v - diff > mx){
29548             diff = mx - v;
29549         }
29550         return diff;
29551     },
29552
29553     // private
29554     onMouseMove : function(e){
29555         
29556         if(this.enabled){
29557             try{// try catch so if something goes wrong the user doesn't get hung
29558
29559             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29560                 return;
29561             }
29562
29563             //var curXY = this.startPoint;
29564             var curSize = this.curSize || this.startBox;
29565             var x = this.startBox.x, y = this.startBox.y;
29566             var ox = x, oy = y;
29567             var w = curSize.width, h = curSize.height;
29568             var ow = w, oh = h;
29569             var mw = this.minWidth, mh = this.minHeight;
29570             var mxw = this.maxWidth, mxh = this.maxHeight;
29571             var wi = this.widthIncrement;
29572             var hi = this.heightIncrement;
29573
29574             var eventXY = e.getXY();
29575             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29576             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29577
29578             var pos = this.activeHandle.position;
29579
29580             switch(pos){
29581                 case "east":
29582                     w += diffX;
29583                     w = Math.min(Math.max(mw, w), mxw);
29584                     break;
29585              
29586                 case "south":
29587                     h += diffY;
29588                     h = Math.min(Math.max(mh, h), mxh);
29589                     break;
29590                 case "southeast":
29591                     w += diffX;
29592                     h += diffY;
29593                     w = Math.min(Math.max(mw, w), mxw);
29594                     h = Math.min(Math.max(mh, h), mxh);
29595                     break;
29596                 case "north":
29597                     diffY = this.constrain(h, diffY, mh, mxh);
29598                     y += diffY;
29599                     h -= diffY;
29600                     break;
29601                 case "hdrag":
29602                     
29603                     if (wi) {
29604                         var adiffX = Math.abs(diffX);
29605                         var sub = (adiffX % wi); // how much 
29606                         if (sub > (wi/2)) { // far enough to snap
29607                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29608                         } else {
29609                             // remove difference.. 
29610                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29611                         }
29612                     }
29613                     x += diffX;
29614                     x = Math.max(this.minX, x);
29615                     break;
29616                 case "west":
29617                     diffX = this.constrain(w, diffX, mw, mxw);
29618                     x += diffX;
29619                     w -= diffX;
29620                     break;
29621                 case "northeast":
29622                     w += diffX;
29623                     w = Math.min(Math.max(mw, w), mxw);
29624                     diffY = this.constrain(h, diffY, mh, mxh);
29625                     y += diffY;
29626                     h -= diffY;
29627                     break;
29628                 case "northwest":
29629                     diffX = this.constrain(w, diffX, mw, mxw);
29630                     diffY = this.constrain(h, diffY, mh, mxh);
29631                     y += diffY;
29632                     h -= diffY;
29633                     x += diffX;
29634                     w -= diffX;
29635                     break;
29636                case "southwest":
29637                     diffX = this.constrain(w, diffX, mw, mxw);
29638                     h += diffY;
29639                     h = Math.min(Math.max(mh, h), mxh);
29640                     x += diffX;
29641                     w -= diffX;
29642                     break;
29643             }
29644
29645             var sw = this.snap(w, wi, mw);
29646             var sh = this.snap(h, hi, mh);
29647             if(sw != w || sh != h){
29648                 switch(pos){
29649                     case "northeast":
29650                         y -= sh - h;
29651                     break;
29652                     case "north":
29653                         y -= sh - h;
29654                         break;
29655                     case "southwest":
29656                         x -= sw - w;
29657                     break;
29658                     case "west":
29659                         x -= sw - w;
29660                         break;
29661                     case "northwest":
29662                         x -= sw - w;
29663                         y -= sh - h;
29664                     break;
29665                 }
29666                 w = sw;
29667                 h = sh;
29668             }
29669
29670             if(this.preserveRatio){
29671                 switch(pos){
29672                     case "southeast":
29673                     case "east":
29674                         h = oh * (w/ow);
29675                         h = Math.min(Math.max(mh, h), mxh);
29676                         w = ow * (h/oh);
29677                        break;
29678                     case "south":
29679                         w = ow * (h/oh);
29680                         w = Math.min(Math.max(mw, w), mxw);
29681                         h = oh * (w/ow);
29682                         break;
29683                     case "northeast":
29684                         w = ow * (h/oh);
29685                         w = Math.min(Math.max(mw, w), mxw);
29686                         h = oh * (w/ow);
29687                     break;
29688                     case "north":
29689                         var tw = w;
29690                         w = ow * (h/oh);
29691                         w = Math.min(Math.max(mw, w), mxw);
29692                         h = oh * (w/ow);
29693                         x += (tw - w) / 2;
29694                         break;
29695                     case "southwest":
29696                         h = oh * (w/ow);
29697                         h = Math.min(Math.max(mh, h), mxh);
29698                         var tw = w;
29699                         w = ow * (h/oh);
29700                         x += tw - w;
29701                         break;
29702                     case "west":
29703                         var th = h;
29704                         h = oh * (w/ow);
29705                         h = Math.min(Math.max(mh, h), mxh);
29706                         y += (th - h) / 2;
29707                         var tw = w;
29708                         w = ow * (h/oh);
29709                         x += tw - w;
29710                        break;
29711                     case "northwest":
29712                         var tw = w;
29713                         var th = h;
29714                         h = oh * (w/ow);
29715                         h = Math.min(Math.max(mh, h), mxh);
29716                         w = ow * (h/oh);
29717                         y += th - h;
29718                         x += tw - w;
29719                        break;
29720
29721                 }
29722             }
29723             if (pos == 'hdrag') {
29724                 w = ow;
29725             }
29726             this.proxy.setBounds(x, y, w, h);
29727             if(this.dynamic){
29728                 this.resizeElement();
29729             }
29730             }catch(e){}
29731         }
29732         this.fireEvent("resizing", this, x, y, w, h, e);
29733     },
29734
29735     // private
29736     handleOver : function(){
29737         if(this.enabled){
29738             this.el.addClass("x-resizable-over");
29739         }
29740     },
29741
29742     // private
29743     handleOut : function(){
29744         if(!this.resizing){
29745             this.el.removeClass("x-resizable-over");
29746         }
29747     },
29748
29749     /**
29750      * Returns the element this component is bound to.
29751      * @return {Roo.Element}
29752      */
29753     getEl : function(){
29754         return this.el;
29755     },
29756
29757     /**
29758      * Returns the resizeChild element (or null).
29759      * @return {Roo.Element}
29760      */
29761     getResizeChild : function(){
29762         return this.resizeChild;
29763     },
29764     groupHandler : function()
29765     {
29766         
29767     },
29768     /**
29769      * Destroys this resizable. If the element was wrapped and
29770      * removeEl is not true then the element remains.
29771      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29772      */
29773     destroy : function(removeEl){
29774         this.proxy.remove();
29775         if(this.overlay){
29776             this.overlay.removeAllListeners();
29777             this.overlay.remove();
29778         }
29779         var ps = Roo.Resizable.positions;
29780         for(var k in ps){
29781             if(typeof ps[k] != "function" && this[ps[k]]){
29782                 var h = this[ps[k]];
29783                 h.el.removeAllListeners();
29784                 h.el.remove();
29785             }
29786         }
29787         if(removeEl){
29788             this.el.update("");
29789             this.el.remove();
29790         }
29791     }
29792 });
29793
29794 // private
29795 // hash to map config positions to true positions
29796 Roo.Resizable.positions = {
29797     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29798     hd: "hdrag"
29799 };
29800
29801 // private
29802 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29803     if(!this.tpl){
29804         // only initialize the template if resizable is used
29805         var tpl = Roo.DomHelper.createTemplate(
29806             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29807         );
29808         tpl.compile();
29809         Roo.Resizable.Handle.prototype.tpl = tpl;
29810     }
29811     this.position = pos;
29812     this.rz = rz;
29813     // show north drag fro topdra
29814     var handlepos = pos == 'hdrag' ? 'north' : pos;
29815     
29816     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29817     if (pos == 'hdrag') {
29818         this.el.setStyle('cursor', 'pointer');
29819     }
29820     this.el.unselectable();
29821     if(transparent){
29822         this.el.setOpacity(0);
29823     }
29824     this.el.on("mousedown", this.onMouseDown, this);
29825     if(!disableTrackOver){
29826         this.el.on("mouseover", this.onMouseOver, this);
29827         this.el.on("mouseout", this.onMouseOut, this);
29828     }
29829 };
29830
29831 // private
29832 Roo.Resizable.Handle.prototype = {
29833     afterResize : function(rz){
29834         Roo.log('after?');
29835         // do nothing
29836     },
29837     // private
29838     onMouseDown : function(e){
29839         this.rz.onMouseDown(this, e);
29840     },
29841     // private
29842     onMouseOver : function(e){
29843         this.rz.handleOver(this, e);
29844     },
29845     // private
29846     onMouseOut : function(e){
29847         this.rz.handleOut(this, e);
29848     }
29849 };/*
29850  * Based on:
29851  * Ext JS Library 1.1.1
29852  * Copyright(c) 2006-2007, Ext JS, LLC.
29853  *
29854  * Originally Released Under LGPL - original licence link has changed is not relivant.
29855  *
29856  * Fork - LGPL
29857  * <script type="text/javascript">
29858  */
29859
29860 /**
29861  * @class Roo.Editor
29862  * @extends Roo.Component
29863  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29864  * @constructor
29865  * Create a new Editor
29866  * @param {Roo.form.Field} field The Field object (or descendant)
29867  * @param {Object} config The config object
29868  */
29869 Roo.Editor = function(field, config){
29870     Roo.Editor.superclass.constructor.call(this, config);
29871     this.field = field;
29872     this.addEvents({
29873         /**
29874              * @event beforestartedit
29875              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29876              * false from the handler of this event.
29877              * @param {Editor} this
29878              * @param {Roo.Element} boundEl The underlying element bound to this editor
29879              * @param {Mixed} value The field value being set
29880              */
29881         "beforestartedit" : true,
29882         /**
29883              * @event startedit
29884              * Fires when this editor is displayed
29885              * @param {Roo.Element} boundEl The underlying element bound to this editor
29886              * @param {Mixed} value The starting field value
29887              */
29888         "startedit" : true,
29889         /**
29890              * @event beforecomplete
29891              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29892              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29893              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29894              * event will not fire since no edit actually occurred.
29895              * @param {Editor} this
29896              * @param {Mixed} value The current field value
29897              * @param {Mixed} startValue The original field value
29898              */
29899         "beforecomplete" : true,
29900         /**
29901              * @event complete
29902              * Fires after editing is complete and any changed value has been written to the underlying field.
29903              * @param {Editor} this
29904              * @param {Mixed} value The current field value
29905              * @param {Mixed} startValue The original field value
29906              */
29907         "complete" : true,
29908         /**
29909          * @event specialkey
29910          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29911          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29912          * @param {Roo.form.Field} this
29913          * @param {Roo.EventObject} e The event object
29914          */
29915         "specialkey" : true
29916     });
29917 };
29918
29919 Roo.extend(Roo.Editor, Roo.Component, {
29920     /**
29921      * @cfg {Boolean/String} autosize
29922      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29923      * or "height" to adopt the height only (defaults to false)
29924      */
29925     /**
29926      * @cfg {Boolean} revertInvalid
29927      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29928      * validation fails (defaults to true)
29929      */
29930     /**
29931      * @cfg {Boolean} ignoreNoChange
29932      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29933      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29934      * will never be ignored.
29935      */
29936     /**
29937      * @cfg {Boolean} hideEl
29938      * False to keep the bound element visible while the editor is displayed (defaults to true)
29939      */
29940     /**
29941      * @cfg {Mixed} value
29942      * The data value of the underlying field (defaults to "")
29943      */
29944     value : "",
29945     /**
29946      * @cfg {String} alignment
29947      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29948      */
29949     alignment: "c-c?",
29950     /**
29951      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29952      * for bottom-right shadow (defaults to "frame")
29953      */
29954     shadow : "frame",
29955     /**
29956      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29957      */
29958     constrain : false,
29959     /**
29960      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29961      */
29962     completeOnEnter : false,
29963     /**
29964      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29965      */
29966     cancelOnEsc : false,
29967     /**
29968      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29969      */
29970     updateEl : false,
29971
29972     // private
29973     onRender : function(ct, position){
29974         this.el = new Roo.Layer({
29975             shadow: this.shadow,
29976             cls: "x-editor",
29977             parentEl : ct,
29978             shim : this.shim,
29979             shadowOffset:4,
29980             id: this.id,
29981             constrain: this.constrain
29982         });
29983         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29984         if(this.field.msgTarget != 'title'){
29985             this.field.msgTarget = 'qtip';
29986         }
29987         this.field.render(this.el);
29988         if(Roo.isGecko){
29989             this.field.el.dom.setAttribute('autocomplete', 'off');
29990         }
29991         this.field.on("specialkey", this.onSpecialKey, this);
29992         if(this.swallowKeys){
29993             this.field.el.swallowEvent(['keydown','keypress']);
29994         }
29995         this.field.show();
29996         this.field.on("blur", this.onBlur, this);
29997         if(this.field.grow){
29998             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29999         }
30000     },
30001
30002     onSpecialKey : function(field, e)
30003     {
30004         //Roo.log('editor onSpecialKey');
30005         if(this.completeOnEnter && e.getKey() == e.ENTER){
30006             e.stopEvent();
30007             this.completeEdit();
30008             return;
30009         }
30010         // do not fire special key otherwise it might hide close the editor...
30011         if(e.getKey() == e.ENTER){    
30012             return;
30013         }
30014         if(this.cancelOnEsc && e.getKey() == e.ESC){
30015             this.cancelEdit();
30016             return;
30017         } 
30018         this.fireEvent('specialkey', field, e);
30019     
30020     },
30021
30022     /**
30023      * Starts the editing process and shows the editor.
30024      * @param {String/HTMLElement/Element} el The element to edit
30025      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
30026       * to the innerHTML of el.
30027      */
30028     startEdit : function(el, value){
30029         if(this.editing){
30030             this.completeEdit();
30031         }
30032         this.boundEl = Roo.get(el);
30033         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
30034         if(!this.rendered){
30035             this.render(this.parentEl || document.body);
30036         }
30037         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
30038             return;
30039         }
30040         this.startValue = v;
30041         this.field.setValue(v);
30042         if(this.autoSize){
30043             var sz = this.boundEl.getSize();
30044             switch(this.autoSize){
30045                 case "width":
30046                 this.setSize(sz.width,  "");
30047                 break;
30048                 case "height":
30049                 this.setSize("",  sz.height);
30050                 break;
30051                 default:
30052                 this.setSize(sz.width,  sz.height);
30053             }
30054         }
30055         this.el.alignTo(this.boundEl, this.alignment);
30056         this.editing = true;
30057         if(Roo.QuickTips){
30058             Roo.QuickTips.disable();
30059         }
30060         this.show();
30061     },
30062
30063     /**
30064      * Sets the height and width of this editor.
30065      * @param {Number} width The new width
30066      * @param {Number} height The new height
30067      */
30068     setSize : function(w, h){
30069         this.field.setSize(w, h);
30070         if(this.el){
30071             this.el.sync();
30072         }
30073     },
30074
30075     /**
30076      * Realigns the editor to the bound field based on the current alignment config value.
30077      */
30078     realign : function(){
30079         this.el.alignTo(this.boundEl, this.alignment);
30080     },
30081
30082     /**
30083      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
30084      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
30085      */
30086     completeEdit : function(remainVisible){
30087         if(!this.editing){
30088             return;
30089         }
30090         var v = this.getValue();
30091         if(this.revertInvalid !== false && !this.field.isValid()){
30092             v = this.startValue;
30093             this.cancelEdit(true);
30094         }
30095         if(String(v) === String(this.startValue) && this.ignoreNoChange){
30096             this.editing = false;
30097             this.hide();
30098             return;
30099         }
30100         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
30101             this.editing = false;
30102             if(this.updateEl && this.boundEl){
30103                 this.boundEl.update(v);
30104             }
30105             if(remainVisible !== true){
30106                 this.hide();
30107             }
30108             this.fireEvent("complete", this, v, this.startValue);
30109         }
30110     },
30111
30112     // private
30113     onShow : function(){
30114         this.el.show();
30115         if(this.hideEl !== false){
30116             this.boundEl.hide();
30117         }
30118         this.field.show();
30119         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
30120             this.fixIEFocus = true;
30121             this.deferredFocus.defer(50, this);
30122         }else{
30123             this.field.focus();
30124         }
30125         this.fireEvent("startedit", this.boundEl, this.startValue);
30126     },
30127
30128     deferredFocus : function(){
30129         if(this.editing){
30130             this.field.focus();
30131         }
30132     },
30133
30134     /**
30135      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
30136      * reverted to the original starting value.
30137      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
30138      * cancel (defaults to false)
30139      */
30140     cancelEdit : function(remainVisible){
30141         if(this.editing){
30142             this.setValue(this.startValue);
30143             if(remainVisible !== true){
30144                 this.hide();
30145             }
30146         }
30147     },
30148
30149     // private
30150     onBlur : function(){
30151         if(this.allowBlur !== true && this.editing){
30152             this.completeEdit();
30153         }
30154     },
30155
30156     // private
30157     onHide : function(){
30158         if(this.editing){
30159             this.completeEdit();
30160             return;
30161         }
30162         this.field.blur();
30163         if(this.field.collapse){
30164             this.field.collapse();
30165         }
30166         this.el.hide();
30167         if(this.hideEl !== false){
30168             this.boundEl.show();
30169         }
30170         if(Roo.QuickTips){
30171             Roo.QuickTips.enable();
30172         }
30173     },
30174
30175     /**
30176      * Sets the data value of the editor
30177      * @param {Mixed} value Any valid value supported by the underlying field
30178      */
30179     setValue : function(v){
30180         this.field.setValue(v);
30181     },
30182
30183     /**
30184      * Gets the data value of the editor
30185      * @return {Mixed} The data value
30186      */
30187     getValue : function(){
30188         return this.field.getValue();
30189     }
30190 });/*
30191  * Based on:
30192  * Ext JS Library 1.1.1
30193  * Copyright(c) 2006-2007, Ext JS, LLC.
30194  *
30195  * Originally Released Under LGPL - original licence link has changed is not relivant.
30196  *
30197  * Fork - LGPL
30198  * <script type="text/javascript">
30199  */
30200  
30201 /**
30202  * @class Roo.BasicDialog
30203  * @extends Roo.util.Observable
30204  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
30205  * <pre><code>
30206 var dlg = new Roo.BasicDialog("my-dlg", {
30207     height: 200,
30208     width: 300,
30209     minHeight: 100,
30210     minWidth: 150,
30211     modal: true,
30212     proxyDrag: true,
30213     shadow: true
30214 });
30215 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
30216 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
30217 dlg.addButton('Cancel', dlg.hide, dlg);
30218 dlg.show();
30219 </code></pre>
30220   <b>A Dialog should always be a direct child of the body element.</b>
30221  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30222  * @cfg {String} title Default text to display in the title bar (defaults to null)
30223  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30224  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30225  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30226  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30227  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30228  * (defaults to null with no animation)
30229  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30230  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30231  * property for valid values (defaults to 'all')
30232  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30233  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30234  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30235  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30236  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30237  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30238  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30239  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30240  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30241  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30242  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30243  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30244  * draggable = true (defaults to false)
30245  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30246  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30247  * shadow (defaults to false)
30248  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30249  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30250  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30251  * @cfg {Array} buttons Array of buttons
30252  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30253  * @constructor
30254  * Create a new BasicDialog.
30255  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30256  * @param {Object} config Configuration options
30257  */
30258 Roo.BasicDialog = function(el, config){
30259     this.el = Roo.get(el);
30260     var dh = Roo.DomHelper;
30261     if(!this.el && config && config.autoCreate){
30262         if(typeof config.autoCreate == "object"){
30263             if(!config.autoCreate.id){
30264                 config.autoCreate.id = el;
30265             }
30266             this.el = dh.append(document.body,
30267                         config.autoCreate, true);
30268         }else{
30269             this.el = dh.append(document.body,
30270                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30271         }
30272     }
30273     el = this.el;
30274     el.setDisplayed(true);
30275     el.hide = this.hideAction;
30276     this.id = el.id;
30277     el.addClass("x-dlg");
30278
30279     Roo.apply(this, config);
30280
30281     this.proxy = el.createProxy("x-dlg-proxy");
30282     this.proxy.hide = this.hideAction;
30283     this.proxy.setOpacity(.5);
30284     this.proxy.hide();
30285
30286     if(config.width){
30287         el.setWidth(config.width);
30288     }
30289     if(config.height){
30290         el.setHeight(config.height);
30291     }
30292     this.size = el.getSize();
30293     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30294         this.xy = [config.x,config.y];
30295     }else{
30296         this.xy = el.getCenterXY(true);
30297     }
30298     /** The header element @type Roo.Element */
30299     this.header = el.child("> .x-dlg-hd");
30300     /** The body element @type Roo.Element */
30301     this.body = el.child("> .x-dlg-bd");
30302     /** The footer element @type Roo.Element */
30303     this.footer = el.child("> .x-dlg-ft");
30304
30305     if(!this.header){
30306         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30307     }
30308     if(!this.body){
30309         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30310     }
30311
30312     this.header.unselectable();
30313     if(this.title){
30314         this.header.update(this.title);
30315     }
30316     // this element allows the dialog to be focused for keyboard event
30317     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30318     this.focusEl.swallowEvent("click", true);
30319
30320     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30321
30322     // wrap the body and footer for special rendering
30323     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30324     if(this.footer){
30325         this.bwrap.dom.appendChild(this.footer.dom);
30326     }
30327
30328     this.bg = this.el.createChild({
30329         tag: "div", cls:"x-dlg-bg",
30330         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30331     });
30332     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30333
30334
30335     if(this.autoScroll !== false && !this.autoTabs){
30336         this.body.setStyle("overflow", "auto");
30337     }
30338
30339     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30340
30341     if(this.closable !== false){
30342         this.el.addClass("x-dlg-closable");
30343         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30344         this.close.on("click", this.closeClick, this);
30345         this.close.addClassOnOver("x-dlg-close-over");
30346     }
30347     if(this.collapsible !== false){
30348         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30349         this.collapseBtn.on("click", this.collapseClick, this);
30350         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30351         this.header.on("dblclick", this.collapseClick, this);
30352     }
30353     if(this.resizable !== false){
30354         this.el.addClass("x-dlg-resizable");
30355         this.resizer = new Roo.Resizable(el, {
30356             minWidth: this.minWidth || 80,
30357             minHeight:this.minHeight || 80,
30358             handles: this.resizeHandles || "all",
30359             pinned: true
30360         });
30361         this.resizer.on("beforeresize", this.beforeResize, this);
30362         this.resizer.on("resize", this.onResize, this);
30363     }
30364     if(this.draggable !== false){
30365         el.addClass("x-dlg-draggable");
30366         if (!this.proxyDrag) {
30367             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30368         }
30369         else {
30370             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30371         }
30372         dd.setHandleElId(this.header.id);
30373         dd.endDrag = this.endMove.createDelegate(this);
30374         dd.startDrag = this.startMove.createDelegate(this);
30375         dd.onDrag = this.onDrag.createDelegate(this);
30376         dd.scroll = false;
30377         this.dd = dd;
30378     }
30379     if(this.modal){
30380         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30381         this.mask.enableDisplayMode("block");
30382         this.mask.hide();
30383         this.el.addClass("x-dlg-modal");
30384     }
30385     if(this.shadow){
30386         this.shadow = new Roo.Shadow({
30387             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30388             offset : this.shadowOffset
30389         });
30390     }else{
30391         this.shadowOffset = 0;
30392     }
30393     if(Roo.useShims && this.shim !== false){
30394         this.shim = this.el.createShim();
30395         this.shim.hide = this.hideAction;
30396         this.shim.hide();
30397     }else{
30398         this.shim = false;
30399     }
30400     if(this.autoTabs){
30401         this.initTabs();
30402     }
30403     if (this.buttons) { 
30404         var bts= this.buttons;
30405         this.buttons = [];
30406         Roo.each(bts, function(b) {
30407             this.addButton(b);
30408         }, this);
30409     }
30410     
30411     
30412     this.addEvents({
30413         /**
30414          * @event keydown
30415          * Fires when a key is pressed
30416          * @param {Roo.BasicDialog} this
30417          * @param {Roo.EventObject} e
30418          */
30419         "keydown" : true,
30420         /**
30421          * @event move
30422          * Fires when this dialog is moved by the user.
30423          * @param {Roo.BasicDialog} this
30424          * @param {Number} x The new page X
30425          * @param {Number} y The new page Y
30426          */
30427         "move" : true,
30428         /**
30429          * @event resize
30430          * Fires when this dialog is resized by the user.
30431          * @param {Roo.BasicDialog} this
30432          * @param {Number} width The new width
30433          * @param {Number} height The new height
30434          */
30435         "resize" : true,
30436         /**
30437          * @event beforehide
30438          * Fires before this dialog is hidden.
30439          * @param {Roo.BasicDialog} this
30440          */
30441         "beforehide" : true,
30442         /**
30443          * @event hide
30444          * Fires when this dialog is hidden.
30445          * @param {Roo.BasicDialog} this
30446          */
30447         "hide" : true,
30448         /**
30449          * @event beforeshow
30450          * Fires before this dialog is shown.
30451          * @param {Roo.BasicDialog} this
30452          */
30453         "beforeshow" : true,
30454         /**
30455          * @event show
30456          * Fires when this dialog is shown.
30457          * @param {Roo.BasicDialog} this
30458          */
30459         "show" : true
30460     });
30461     el.on("keydown", this.onKeyDown, this);
30462     el.on("mousedown", this.toFront, this);
30463     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30464     this.el.hide();
30465     Roo.DialogManager.register(this);
30466     Roo.BasicDialog.superclass.constructor.call(this);
30467 };
30468
30469 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30470     shadowOffset: Roo.isIE ? 6 : 5,
30471     minHeight: 80,
30472     minWidth: 200,
30473     minButtonWidth: 75,
30474     defaultButton: null,
30475     buttonAlign: "right",
30476     tabTag: 'div',
30477     firstShow: true,
30478
30479     /**
30480      * Sets the dialog title text
30481      * @param {String} text The title text to display
30482      * @return {Roo.BasicDialog} this
30483      */
30484     setTitle : function(text){
30485         this.header.update(text);
30486         return this;
30487     },
30488
30489     // private
30490     closeClick : function(){
30491         this.hide();
30492     },
30493
30494     // private
30495     collapseClick : function(){
30496         this[this.collapsed ? "expand" : "collapse"]();
30497     },
30498
30499     /**
30500      * Collapses the dialog to its minimized state (only the title bar is visible).
30501      * Equivalent to the user clicking the collapse dialog button.
30502      */
30503     collapse : function(){
30504         if(!this.collapsed){
30505             this.collapsed = true;
30506             this.el.addClass("x-dlg-collapsed");
30507             this.restoreHeight = this.el.getHeight();
30508             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30509         }
30510     },
30511
30512     /**
30513      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30514      * clicking the expand dialog button.
30515      */
30516     expand : function(){
30517         if(this.collapsed){
30518             this.collapsed = false;
30519             this.el.removeClass("x-dlg-collapsed");
30520             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30521         }
30522     },
30523
30524     /**
30525      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30526      * @return {Roo.TabPanel} The tabs component
30527      */
30528     initTabs : function(){
30529         var tabs = this.getTabs();
30530         while(tabs.getTab(0)){
30531             tabs.removeTab(0);
30532         }
30533         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30534             var dom = el.dom;
30535             tabs.addTab(Roo.id(dom), dom.title);
30536             dom.title = "";
30537         });
30538         tabs.activate(0);
30539         return tabs;
30540     },
30541
30542     // private
30543     beforeResize : function(){
30544         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30545     },
30546
30547     // private
30548     onResize : function(){
30549         this.refreshSize();
30550         this.syncBodyHeight();
30551         this.adjustAssets();
30552         this.focus();
30553         this.fireEvent("resize", this, this.size.width, this.size.height);
30554     },
30555
30556     // private
30557     onKeyDown : function(e){
30558         if(this.isVisible()){
30559             this.fireEvent("keydown", this, e);
30560         }
30561     },
30562
30563     /**
30564      * Resizes the dialog.
30565      * @param {Number} width
30566      * @param {Number} height
30567      * @return {Roo.BasicDialog} this
30568      */
30569     resizeTo : function(width, height){
30570         this.el.setSize(width, height);
30571         this.size = {width: width, height: height};
30572         this.syncBodyHeight();
30573         if(this.fixedcenter){
30574             this.center();
30575         }
30576         if(this.isVisible()){
30577             this.constrainXY();
30578             this.adjustAssets();
30579         }
30580         this.fireEvent("resize", this, width, height);
30581         return this;
30582     },
30583
30584
30585     /**
30586      * Resizes the dialog to fit the specified content size.
30587      * @param {Number} width
30588      * @param {Number} height
30589      * @return {Roo.BasicDialog} this
30590      */
30591     setContentSize : function(w, h){
30592         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30593         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30594         //if(!this.el.isBorderBox()){
30595             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30596             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30597         //}
30598         if(this.tabs){
30599             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30600             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30601         }
30602         this.resizeTo(w, h);
30603         return this;
30604     },
30605
30606     /**
30607      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30608      * executed in response to a particular key being pressed while the dialog is active.
30609      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30610      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30611      * @param {Function} fn The function to call
30612      * @param {Object} scope (optional) The scope of the function
30613      * @return {Roo.BasicDialog} this
30614      */
30615     addKeyListener : function(key, fn, scope){
30616         var keyCode, shift, ctrl, alt;
30617         if(typeof key == "object" && !(key instanceof Array)){
30618             keyCode = key["key"];
30619             shift = key["shift"];
30620             ctrl = key["ctrl"];
30621             alt = key["alt"];
30622         }else{
30623             keyCode = key;
30624         }
30625         var handler = function(dlg, e){
30626             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30627                 var k = e.getKey();
30628                 if(keyCode instanceof Array){
30629                     for(var i = 0, len = keyCode.length; i < len; i++){
30630                         if(keyCode[i] == k){
30631                           fn.call(scope || window, dlg, k, e);
30632                           return;
30633                         }
30634                     }
30635                 }else{
30636                     if(k == keyCode){
30637                         fn.call(scope || window, dlg, k, e);
30638                     }
30639                 }
30640             }
30641         };
30642         this.on("keydown", handler);
30643         return this;
30644     },
30645
30646     /**
30647      * Returns the TabPanel component (creates it if it doesn't exist).
30648      * Note: If you wish to simply check for the existence of tabs without creating them,
30649      * check for a null 'tabs' property.
30650      * @return {Roo.TabPanel} The tabs component
30651      */
30652     getTabs : function(){
30653         if(!this.tabs){
30654             this.el.addClass("x-dlg-auto-tabs");
30655             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30656             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30657         }
30658         return this.tabs;
30659     },
30660
30661     /**
30662      * Adds a button to the footer section of the dialog.
30663      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30664      * object or a valid Roo.DomHelper element config
30665      * @param {Function} handler The function called when the button is clicked
30666      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30667      * @return {Roo.Button} The new button
30668      */
30669     addButton : function(config, handler, scope){
30670         var dh = Roo.DomHelper;
30671         if(!this.footer){
30672             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30673         }
30674         if(!this.btnContainer){
30675             var tb = this.footer.createChild({
30676
30677                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30678                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30679             }, null, true);
30680             this.btnContainer = tb.firstChild.firstChild.firstChild;
30681         }
30682         var bconfig = {
30683             handler: handler,
30684             scope: scope,
30685             minWidth: this.minButtonWidth,
30686             hideParent:true
30687         };
30688         if(typeof config == "string"){
30689             bconfig.text = config;
30690         }else{
30691             if(config.tag){
30692                 bconfig.dhconfig = config;
30693             }else{
30694                 Roo.apply(bconfig, config);
30695             }
30696         }
30697         var fc = false;
30698         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30699             bconfig.position = Math.max(0, bconfig.position);
30700             fc = this.btnContainer.childNodes[bconfig.position];
30701         }
30702          
30703         var btn = new Roo.Button(
30704             fc ? 
30705                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30706                 : this.btnContainer.appendChild(document.createElement("td")),
30707             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30708             bconfig
30709         );
30710         this.syncBodyHeight();
30711         if(!this.buttons){
30712             /**
30713              * Array of all the buttons that have been added to this dialog via addButton
30714              * @type Array
30715              */
30716             this.buttons = [];
30717         }
30718         this.buttons.push(btn);
30719         return btn;
30720     },
30721
30722     /**
30723      * Sets the default button to be focused when the dialog is displayed.
30724      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30725      * @return {Roo.BasicDialog} this
30726      */
30727     setDefaultButton : function(btn){
30728         this.defaultButton = btn;
30729         return this;
30730     },
30731
30732     // private
30733     getHeaderFooterHeight : function(safe){
30734         var height = 0;
30735         if(this.header){
30736            height += this.header.getHeight();
30737         }
30738         if(this.footer){
30739            var fm = this.footer.getMargins();
30740             height += (this.footer.getHeight()+fm.top+fm.bottom);
30741         }
30742         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30743         height += this.centerBg.getPadding("tb");
30744         return height;
30745     },
30746
30747     // private
30748     syncBodyHeight : function()
30749     {
30750         var bd = this.body, // the text
30751             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30752             bw = this.bwrap;
30753         var height = this.size.height - this.getHeaderFooterHeight(false);
30754         bd.setHeight(height-bd.getMargins("tb"));
30755         var hh = this.header.getHeight();
30756         var h = this.size.height-hh;
30757         cb.setHeight(h);
30758         
30759         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30760         bw.setHeight(h-cb.getPadding("tb"));
30761         
30762         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30763         bd.setWidth(bw.getWidth(true));
30764         if(this.tabs){
30765             this.tabs.syncHeight();
30766             if(Roo.isIE){
30767                 this.tabs.el.repaint();
30768             }
30769         }
30770     },
30771
30772     /**
30773      * Restores the previous state of the dialog if Roo.state is configured.
30774      * @return {Roo.BasicDialog} this
30775      */
30776     restoreState : function(){
30777         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30778         if(box && box.width){
30779             this.xy = [box.x, box.y];
30780             this.resizeTo(box.width, box.height);
30781         }
30782         return this;
30783     },
30784
30785     // private
30786     beforeShow : function(){
30787         this.expand();
30788         if(this.fixedcenter){
30789             this.xy = this.el.getCenterXY(true);
30790         }
30791         if(this.modal){
30792             Roo.get(document.body).addClass("x-body-masked");
30793             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30794             this.mask.show();
30795         }
30796         this.constrainXY();
30797     },
30798
30799     // private
30800     animShow : function(){
30801         var b = Roo.get(this.animateTarget).getBox();
30802         this.proxy.setSize(b.width, b.height);
30803         this.proxy.setLocation(b.x, b.y);
30804         this.proxy.show();
30805         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30806                     true, .35, this.showEl.createDelegate(this));
30807     },
30808
30809     /**
30810      * Shows the dialog.
30811      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30812      * @return {Roo.BasicDialog} this
30813      */
30814     show : function(animateTarget){
30815         if (this.fireEvent("beforeshow", this) === false){
30816             return;
30817         }
30818         if(this.syncHeightBeforeShow){
30819             this.syncBodyHeight();
30820         }else if(this.firstShow){
30821             this.firstShow = false;
30822             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30823         }
30824         this.animateTarget = animateTarget || this.animateTarget;
30825         if(!this.el.isVisible()){
30826             this.beforeShow();
30827             if(this.animateTarget && Roo.get(this.animateTarget)){
30828                 this.animShow();
30829             }else{
30830                 this.showEl();
30831             }
30832         }
30833         return this;
30834     },
30835
30836     // private
30837     showEl : function(){
30838         this.proxy.hide();
30839         this.el.setXY(this.xy);
30840         this.el.show();
30841         this.adjustAssets(true);
30842         this.toFront();
30843         this.focus();
30844         // IE peekaboo bug - fix found by Dave Fenwick
30845         if(Roo.isIE){
30846             this.el.repaint();
30847         }
30848         this.fireEvent("show", this);
30849     },
30850
30851     /**
30852      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30853      * dialog itself will receive focus.
30854      */
30855     focus : function(){
30856         if(this.defaultButton){
30857             this.defaultButton.focus();
30858         }else{
30859             this.focusEl.focus();
30860         }
30861     },
30862
30863     // private
30864     constrainXY : function(){
30865         if(this.constraintoviewport !== false){
30866             if(!this.viewSize){
30867                 if(this.container){
30868                     var s = this.container.getSize();
30869                     this.viewSize = [s.width, s.height];
30870                 }else{
30871                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30872                 }
30873             }
30874             var s = Roo.get(this.container||document).getScroll();
30875
30876             var x = this.xy[0], y = this.xy[1];
30877             var w = this.size.width, h = this.size.height;
30878             var vw = this.viewSize[0], vh = this.viewSize[1];
30879             // only move it if it needs it
30880             var moved = false;
30881             // first validate right/bottom
30882             if(x + w > vw+s.left){
30883                 x = vw - w;
30884                 moved = true;
30885             }
30886             if(y + h > vh+s.top){
30887                 y = vh - h;
30888                 moved = true;
30889             }
30890             // then make sure top/left isn't negative
30891             if(x < s.left){
30892                 x = s.left;
30893                 moved = true;
30894             }
30895             if(y < s.top){
30896                 y = s.top;
30897                 moved = true;
30898             }
30899             if(moved){
30900                 // cache xy
30901                 this.xy = [x, y];
30902                 if(this.isVisible()){
30903                     this.el.setLocation(x, y);
30904                     this.adjustAssets();
30905                 }
30906             }
30907         }
30908     },
30909
30910     // private
30911     onDrag : function(){
30912         if(!this.proxyDrag){
30913             this.xy = this.el.getXY();
30914             this.adjustAssets();
30915         }
30916     },
30917
30918     // private
30919     adjustAssets : function(doShow){
30920         var x = this.xy[0], y = this.xy[1];
30921         var w = this.size.width, h = this.size.height;
30922         if(doShow === true){
30923             if(this.shadow){
30924                 this.shadow.show(this.el);
30925             }
30926             if(this.shim){
30927                 this.shim.show();
30928             }
30929         }
30930         if(this.shadow && this.shadow.isVisible()){
30931             this.shadow.show(this.el);
30932         }
30933         if(this.shim && this.shim.isVisible()){
30934             this.shim.setBounds(x, y, w, h);
30935         }
30936     },
30937
30938     // private
30939     adjustViewport : function(w, h){
30940         if(!w || !h){
30941             w = Roo.lib.Dom.getViewWidth();
30942             h = Roo.lib.Dom.getViewHeight();
30943         }
30944         // cache the size
30945         this.viewSize = [w, h];
30946         if(this.modal && this.mask.isVisible()){
30947             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30948             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30949         }
30950         if(this.isVisible()){
30951             this.constrainXY();
30952         }
30953     },
30954
30955     /**
30956      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30957      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30958      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30959      */
30960     destroy : function(removeEl){
30961         if(this.isVisible()){
30962             this.animateTarget = null;
30963             this.hide();
30964         }
30965         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30966         if(this.tabs){
30967             this.tabs.destroy(removeEl);
30968         }
30969         Roo.destroy(
30970              this.shim,
30971              this.proxy,
30972              this.resizer,
30973              this.close,
30974              this.mask
30975         );
30976         if(this.dd){
30977             this.dd.unreg();
30978         }
30979         if(this.buttons){
30980            for(var i = 0, len = this.buttons.length; i < len; i++){
30981                this.buttons[i].destroy();
30982            }
30983         }
30984         this.el.removeAllListeners();
30985         if(removeEl === true){
30986             this.el.update("");
30987             this.el.remove();
30988         }
30989         Roo.DialogManager.unregister(this);
30990     },
30991
30992     // private
30993     startMove : function(){
30994         if(this.proxyDrag){
30995             this.proxy.show();
30996         }
30997         if(this.constraintoviewport !== false){
30998             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30999         }
31000     },
31001
31002     // private
31003     endMove : function(){
31004         if(!this.proxyDrag){
31005             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
31006         }else{
31007             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
31008             this.proxy.hide();
31009         }
31010         this.refreshSize();
31011         this.adjustAssets();
31012         this.focus();
31013         this.fireEvent("move", this, this.xy[0], this.xy[1]);
31014     },
31015
31016     /**
31017      * Brings this dialog to the front of any other visible dialogs
31018      * @return {Roo.BasicDialog} this
31019      */
31020     toFront : function(){
31021         Roo.DialogManager.bringToFront(this);
31022         return this;
31023     },
31024
31025     /**
31026      * Sends this dialog to the back (under) of any other visible dialogs
31027      * @return {Roo.BasicDialog} this
31028      */
31029     toBack : function(){
31030         Roo.DialogManager.sendToBack(this);
31031         return this;
31032     },
31033
31034     /**
31035      * Centers this dialog in the viewport
31036      * @return {Roo.BasicDialog} this
31037      */
31038     center : function(){
31039         var xy = this.el.getCenterXY(true);
31040         this.moveTo(xy[0], xy[1]);
31041         return this;
31042     },
31043
31044     /**
31045      * Moves the dialog's top-left corner to the specified point
31046      * @param {Number} x
31047      * @param {Number} y
31048      * @return {Roo.BasicDialog} this
31049      */
31050     moveTo : function(x, y){
31051         this.xy = [x,y];
31052         if(this.isVisible()){
31053             this.el.setXY(this.xy);
31054             this.adjustAssets();
31055         }
31056         return this;
31057     },
31058
31059     /**
31060      * Aligns the dialog to the specified element
31061      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31062      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
31063      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31064      * @return {Roo.BasicDialog} this
31065      */
31066     alignTo : function(element, position, offsets){
31067         this.xy = this.el.getAlignToXY(element, position, offsets);
31068         if(this.isVisible()){
31069             this.el.setXY(this.xy);
31070             this.adjustAssets();
31071         }
31072         return this;
31073     },
31074
31075     /**
31076      * Anchors an element to another element and realigns it when the window is resized.
31077      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31078      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
31079      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31080      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
31081      * is a number, it is used as the buffer delay (defaults to 50ms).
31082      * @return {Roo.BasicDialog} this
31083      */
31084     anchorTo : function(el, alignment, offsets, monitorScroll){
31085         var action = function(){
31086             this.alignTo(el, alignment, offsets);
31087         };
31088         Roo.EventManager.onWindowResize(action, this);
31089         var tm = typeof monitorScroll;
31090         if(tm != 'undefined'){
31091             Roo.EventManager.on(window, 'scroll', action, this,
31092                 {buffer: tm == 'number' ? monitorScroll : 50});
31093         }
31094         action.call(this);
31095         return this;
31096     },
31097
31098     /**
31099      * Returns true if the dialog is visible
31100      * @return {Boolean}
31101      */
31102     isVisible : function(){
31103         return this.el.isVisible();
31104     },
31105
31106     // private
31107     animHide : function(callback){
31108         var b = Roo.get(this.animateTarget).getBox();
31109         this.proxy.show();
31110         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
31111         this.el.hide();
31112         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
31113                     this.hideEl.createDelegate(this, [callback]));
31114     },
31115
31116     /**
31117      * Hides the dialog.
31118      * @param {Function} callback (optional) Function to call when the dialog is hidden
31119      * @return {Roo.BasicDialog} this
31120      */
31121     hide : function(callback){
31122         if (this.fireEvent("beforehide", this) === false){
31123             return;
31124         }
31125         if(this.shadow){
31126             this.shadow.hide();
31127         }
31128         if(this.shim) {
31129           this.shim.hide();
31130         }
31131         // sometimes animateTarget seems to get set.. causing problems...
31132         // this just double checks..
31133         if(this.animateTarget && Roo.get(this.animateTarget)) {
31134            this.animHide(callback);
31135         }else{
31136             this.el.hide();
31137             this.hideEl(callback);
31138         }
31139         return this;
31140     },
31141
31142     // private
31143     hideEl : function(callback){
31144         this.proxy.hide();
31145         if(this.modal){
31146             this.mask.hide();
31147             Roo.get(document.body).removeClass("x-body-masked");
31148         }
31149         this.fireEvent("hide", this);
31150         if(typeof callback == "function"){
31151             callback();
31152         }
31153     },
31154
31155     // private
31156     hideAction : function(){
31157         this.setLeft("-10000px");
31158         this.setTop("-10000px");
31159         this.setStyle("visibility", "hidden");
31160     },
31161
31162     // private
31163     refreshSize : function(){
31164         this.size = this.el.getSize();
31165         this.xy = this.el.getXY();
31166         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
31167     },
31168
31169     // private
31170     // z-index is managed by the DialogManager and may be overwritten at any time
31171     setZIndex : function(index){
31172         if(this.modal){
31173             this.mask.setStyle("z-index", index);
31174         }
31175         if(this.shim){
31176             this.shim.setStyle("z-index", ++index);
31177         }
31178         if(this.shadow){
31179             this.shadow.setZIndex(++index);
31180         }
31181         this.el.setStyle("z-index", ++index);
31182         if(this.proxy){
31183             this.proxy.setStyle("z-index", ++index);
31184         }
31185         if(this.resizer){
31186             this.resizer.proxy.setStyle("z-index", ++index);
31187         }
31188
31189         this.lastZIndex = index;
31190     },
31191
31192     /**
31193      * Returns the element for this dialog
31194      * @return {Roo.Element} The underlying dialog Element
31195      */
31196     getEl : function(){
31197         return this.el;
31198     }
31199 });
31200
31201 /**
31202  * @class Roo.DialogManager
31203  * Provides global access to BasicDialogs that have been created and
31204  * support for z-indexing (layering) multiple open dialogs.
31205  */
31206 Roo.DialogManager = function(){
31207     var list = {};
31208     var accessList = [];
31209     var front = null;
31210
31211     // private
31212     var sortDialogs = function(d1, d2){
31213         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
31214     };
31215
31216     // private
31217     var orderDialogs = function(){
31218         accessList.sort(sortDialogs);
31219         var seed = Roo.DialogManager.zseed;
31220         for(var i = 0, len = accessList.length; i < len; i++){
31221             var dlg = accessList[i];
31222             if(dlg){
31223                 dlg.setZIndex(seed + (i*10));
31224             }
31225         }
31226     };
31227
31228     return {
31229         /**
31230          * The starting z-index for BasicDialogs (defaults to 9000)
31231          * @type Number The z-index value
31232          */
31233         zseed : 9000,
31234
31235         // private
31236         register : function(dlg){
31237             list[dlg.id] = dlg;
31238             accessList.push(dlg);
31239         },
31240
31241         // private
31242         unregister : function(dlg){
31243             delete list[dlg.id];
31244             var i=0;
31245             var len=0;
31246             if(!accessList.indexOf){
31247                 for(  i = 0, len = accessList.length; i < len; i++){
31248                     if(accessList[i] == dlg){
31249                         accessList.splice(i, 1);
31250                         return;
31251                     }
31252                 }
31253             }else{
31254                  i = accessList.indexOf(dlg);
31255                 if(i != -1){
31256                     accessList.splice(i, 1);
31257                 }
31258             }
31259         },
31260
31261         /**
31262          * Gets a registered dialog by id
31263          * @param {String/Object} id The id of the dialog or a dialog
31264          * @return {Roo.BasicDialog} this
31265          */
31266         get : function(id){
31267             return typeof id == "object" ? id : list[id];
31268         },
31269
31270         /**
31271          * Brings the specified dialog to the front
31272          * @param {String/Object} dlg The id of the dialog or a dialog
31273          * @return {Roo.BasicDialog} this
31274          */
31275         bringToFront : function(dlg){
31276             dlg = this.get(dlg);
31277             if(dlg != front){
31278                 front = dlg;
31279                 dlg._lastAccess = new Date().getTime();
31280                 orderDialogs();
31281             }
31282             return dlg;
31283         },
31284
31285         /**
31286          * Sends the specified dialog to the back
31287          * @param {String/Object} dlg The id of the dialog or a dialog
31288          * @return {Roo.BasicDialog} this
31289          */
31290         sendToBack : function(dlg){
31291             dlg = this.get(dlg);
31292             dlg._lastAccess = -(new Date().getTime());
31293             orderDialogs();
31294             return dlg;
31295         },
31296
31297         /**
31298          * Hides all dialogs
31299          */
31300         hideAll : function(){
31301             for(var id in list){
31302                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31303                     list[id].hide();
31304                 }
31305             }
31306         }
31307     };
31308 }();
31309
31310 /**
31311  * @class Roo.LayoutDialog
31312  * @extends Roo.BasicDialog
31313  * Dialog which provides adjustments for working with a layout in a Dialog.
31314  * Add your necessary layout config options to the dialog's config.<br>
31315  * Example usage (including a nested layout):
31316  * <pre><code>
31317 if(!dialog){
31318     dialog = new Roo.LayoutDialog("download-dlg", {
31319         modal: true,
31320         width:600,
31321         height:450,
31322         shadow:true,
31323         minWidth:500,
31324         minHeight:350,
31325         autoTabs:true,
31326         proxyDrag:true,
31327         // layout config merges with the dialog config
31328         center:{
31329             tabPosition: "top",
31330             alwaysShowTabs: true
31331         }
31332     });
31333     dialog.addKeyListener(27, dialog.hide, dialog);
31334     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31335     dialog.addButton("Build It!", this.getDownload, this);
31336
31337     // we can even add nested layouts
31338     var innerLayout = new Roo.BorderLayout("dl-inner", {
31339         east: {
31340             initialSize: 200,
31341             autoScroll:true,
31342             split:true
31343         },
31344         center: {
31345             autoScroll:true
31346         }
31347     });
31348     innerLayout.beginUpdate();
31349     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31350     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31351     innerLayout.endUpdate(true);
31352
31353     var layout = dialog.getLayout();
31354     layout.beginUpdate();
31355     layout.add("center", new Roo.ContentPanel("standard-panel",
31356                         {title: "Download the Source", fitToFrame:true}));
31357     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31358                {title: "Build your own roo.js"}));
31359     layout.getRegion("center").showPanel(sp);
31360     layout.endUpdate();
31361 }
31362 </code></pre>
31363     * @constructor
31364     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31365     * @param {Object} config configuration options
31366   */
31367 Roo.LayoutDialog = function(el, cfg){
31368     
31369     var config=  cfg;
31370     if (typeof(cfg) == 'undefined') {
31371         config = Roo.apply({}, el);
31372         // not sure why we use documentElement here.. - it should always be body.
31373         // IE7 borks horribly if we use documentElement.
31374         // webkit also does not like documentElement - it creates a body element...
31375         el = Roo.get( document.body || document.documentElement ).createChild();
31376         //config.autoCreate = true;
31377     }
31378     
31379     
31380     config.autoTabs = false;
31381     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31382     this.body.setStyle({overflow:"hidden", position:"relative"});
31383     this.layout = new Roo.BorderLayout(this.body.dom, config);
31384     this.layout.monitorWindowResize = false;
31385     this.el.addClass("x-dlg-auto-layout");
31386     // fix case when center region overwrites center function
31387     this.center = Roo.BasicDialog.prototype.center;
31388     this.on("show", this.layout.layout, this.layout, true);
31389     if (config.items) {
31390         var xitems = config.items;
31391         delete config.items;
31392         Roo.each(xitems, this.addxtype, this);
31393     }
31394     
31395     
31396 };
31397 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31398     /**
31399      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31400      * @deprecated
31401      */
31402     endUpdate : function(){
31403         this.layout.endUpdate();
31404     },
31405
31406     /**
31407      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31408      *  @deprecated
31409      */
31410     beginUpdate : function(){
31411         this.layout.beginUpdate();
31412     },
31413
31414     /**
31415      * Get the BorderLayout for this dialog
31416      * @return {Roo.BorderLayout}
31417      */
31418     getLayout : function(){
31419         return this.layout;
31420     },
31421
31422     showEl : function(){
31423         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31424         if(Roo.isIE7){
31425             this.layout.layout();
31426         }
31427     },
31428
31429     // private
31430     // Use the syncHeightBeforeShow config option to control this automatically
31431     syncBodyHeight : function(){
31432         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31433         if(this.layout){this.layout.layout();}
31434     },
31435     
31436       /**
31437      * Add an xtype element (actually adds to the layout.)
31438      * @return {Object} xdata xtype object data.
31439      */
31440     
31441     addxtype : function(c) {
31442         return this.layout.addxtype(c);
31443     }
31444 });/*
31445  * Based on:
31446  * Ext JS Library 1.1.1
31447  * Copyright(c) 2006-2007, Ext JS, LLC.
31448  *
31449  * Originally Released Under LGPL - original licence link has changed is not relivant.
31450  *
31451  * Fork - LGPL
31452  * <script type="text/javascript">
31453  */
31454  
31455 /**
31456  * @class Roo.MessageBox
31457  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31458  * Example usage:
31459  *<pre><code>
31460 // Basic alert:
31461 Roo.Msg.alert('Status', 'Changes saved successfully.');
31462
31463 // Prompt for user data:
31464 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31465     if (btn == 'ok'){
31466         // process text value...
31467     }
31468 });
31469
31470 // Show a dialog using config options:
31471 Roo.Msg.show({
31472    title:'Save Changes?',
31473    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31474    buttons: Roo.Msg.YESNOCANCEL,
31475    fn: processResult,
31476    animEl: 'elId'
31477 });
31478 </code></pre>
31479  * @singleton
31480  */
31481 Roo.MessageBox = function(){
31482     var dlg, opt, mask, waitTimer;
31483     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31484     var buttons, activeTextEl, bwidth;
31485
31486     // private
31487     var handleButton = function(button){
31488         dlg.hide();
31489         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31490     };
31491
31492     // private
31493     var handleHide = function(){
31494         if(opt && opt.cls){
31495             dlg.el.removeClass(opt.cls);
31496         }
31497         if(waitTimer){
31498             Roo.TaskMgr.stop(waitTimer);
31499             waitTimer = null;
31500         }
31501     };
31502
31503     // private
31504     var updateButtons = function(b){
31505         var width = 0;
31506         if(!b){
31507             buttons["ok"].hide();
31508             buttons["cancel"].hide();
31509             buttons["yes"].hide();
31510             buttons["no"].hide();
31511             dlg.footer.dom.style.display = 'none';
31512             return width;
31513         }
31514         dlg.footer.dom.style.display = '';
31515         for(var k in buttons){
31516             if(typeof buttons[k] != "function"){
31517                 if(b[k]){
31518                     buttons[k].show();
31519                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31520                     width += buttons[k].el.getWidth()+15;
31521                 }else{
31522                     buttons[k].hide();
31523                 }
31524             }
31525         }
31526         return width;
31527     };
31528
31529     // private
31530     var handleEsc = function(d, k, e){
31531         if(opt && opt.closable !== false){
31532             dlg.hide();
31533         }
31534         if(e){
31535             e.stopEvent();
31536         }
31537     };
31538
31539     return {
31540         /**
31541          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31542          * @return {Roo.BasicDialog} The BasicDialog element
31543          */
31544         getDialog : function(){
31545            if(!dlg){
31546                 dlg = new Roo.BasicDialog("x-msg-box", {
31547                     autoCreate : true,
31548                     shadow: true,
31549                     draggable: true,
31550                     resizable:false,
31551                     constraintoviewport:false,
31552                     fixedcenter:true,
31553                     collapsible : false,
31554                     shim:true,
31555                     modal: true,
31556                     width:400, height:100,
31557                     buttonAlign:"center",
31558                     closeClick : function(){
31559                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31560                             handleButton("no");
31561                         }else{
31562                             handleButton("cancel");
31563                         }
31564                     }
31565                 });
31566                 dlg.on("hide", handleHide);
31567                 mask = dlg.mask;
31568                 dlg.addKeyListener(27, handleEsc);
31569                 buttons = {};
31570                 var bt = this.buttonText;
31571                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31572                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31573                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31574                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31575                 bodyEl = dlg.body.createChild({
31576
31577                     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>'
31578                 });
31579                 msgEl = bodyEl.dom.firstChild;
31580                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31581                 textboxEl.enableDisplayMode();
31582                 textboxEl.addKeyListener([10,13], function(){
31583                     if(dlg.isVisible() && opt && opt.buttons){
31584                         if(opt.buttons.ok){
31585                             handleButton("ok");
31586                         }else if(opt.buttons.yes){
31587                             handleButton("yes");
31588                         }
31589                     }
31590                 });
31591                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31592                 textareaEl.enableDisplayMode();
31593                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31594                 progressEl.enableDisplayMode();
31595                 var pf = progressEl.dom.firstChild;
31596                 if (pf) {
31597                     pp = Roo.get(pf.firstChild);
31598                     pp.setHeight(pf.offsetHeight);
31599                 }
31600                 
31601             }
31602             return dlg;
31603         },
31604
31605         /**
31606          * Updates the message box body text
31607          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31608          * the XHTML-compliant non-breaking space character '&amp;#160;')
31609          * @return {Roo.MessageBox} This message box
31610          */
31611         updateText : function(text){
31612             if(!dlg.isVisible() && !opt.width){
31613                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31614             }
31615             msgEl.innerHTML = text || '&#160;';
31616       
31617             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31618             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31619             var w = Math.max(
31620                     Math.min(opt.width || cw , this.maxWidth), 
31621                     Math.max(opt.minWidth || this.minWidth, bwidth)
31622             );
31623             if(opt.prompt){
31624                 activeTextEl.setWidth(w);
31625             }
31626             if(dlg.isVisible()){
31627                 dlg.fixedcenter = false;
31628             }
31629             // to big, make it scroll. = But as usual stupid IE does not support
31630             // !important..
31631             
31632             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31633                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31634                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31635             } else {
31636                 bodyEl.dom.style.height = '';
31637                 bodyEl.dom.style.overflowY = '';
31638             }
31639             if (cw > w) {
31640                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31641             } else {
31642                 bodyEl.dom.style.overflowX = '';
31643             }
31644             
31645             dlg.setContentSize(w, bodyEl.getHeight());
31646             if(dlg.isVisible()){
31647                 dlg.fixedcenter = true;
31648             }
31649             return this;
31650         },
31651
31652         /**
31653          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31654          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31655          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31656          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31657          * @return {Roo.MessageBox} This message box
31658          */
31659         updateProgress : function(value, text){
31660             if(text){
31661                 this.updateText(text);
31662             }
31663             if (pp) { // weird bug on my firefox - for some reason this is not defined
31664                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31665             }
31666             return this;
31667         },        
31668
31669         /**
31670          * Returns true if the message box is currently displayed
31671          * @return {Boolean} True if the message box is visible, else false
31672          */
31673         isVisible : function(){
31674             return dlg && dlg.isVisible();  
31675         },
31676
31677         /**
31678          * Hides the message box if it is displayed
31679          */
31680         hide : function(){
31681             if(this.isVisible()){
31682                 dlg.hide();
31683             }  
31684         },
31685
31686         /**
31687          * Displays a new message box, or reinitializes an existing message box, based on the config options
31688          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31689          * The following config object properties are supported:
31690          * <pre>
31691 Property    Type             Description
31692 ----------  ---------------  ------------------------------------------------------------------------------------
31693 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31694                                    closes (defaults to undefined)
31695 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31696                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31697 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31698                                    progress and wait dialogs will ignore this property and always hide the
31699                                    close button as they can only be closed programmatically.
31700 cls               String           A custom CSS class to apply to the message box element
31701 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31702                                    displayed (defaults to 75)
31703 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31704                                    function will be btn (the name of the button that was clicked, if applicable,
31705                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31706                                    Progress and wait dialogs will ignore this option since they do not respond to
31707                                    user actions and can only be closed programmatically, so any required function
31708                                    should be called by the same code after it closes the dialog.
31709 icon              String           A CSS class that provides a background image to be used as an icon for
31710                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31711 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31712 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31713 modal             Boolean          False to allow user interaction with the page while the message box is
31714                                    displayed (defaults to true)
31715 msg               String           A string that will replace the existing message box body text (defaults
31716                                    to the XHTML-compliant non-breaking space character '&#160;')
31717 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31718 progress          Boolean          True to display a progress bar (defaults to false)
31719 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31720 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31721 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31722 title             String           The title text
31723 value             String           The string value to set into the active textbox element if displayed
31724 wait              Boolean          True to display a progress bar (defaults to false)
31725 width             Number           The width of the dialog in pixels
31726 </pre>
31727          *
31728          * Example usage:
31729          * <pre><code>
31730 Roo.Msg.show({
31731    title: 'Address',
31732    msg: 'Please enter your address:',
31733    width: 300,
31734    buttons: Roo.MessageBox.OKCANCEL,
31735    multiline: true,
31736    fn: saveAddress,
31737    animEl: 'addAddressBtn'
31738 });
31739 </code></pre>
31740          * @param {Object} config Configuration options
31741          * @return {Roo.MessageBox} This message box
31742          */
31743         show : function(options)
31744         {
31745             
31746             // this causes nightmares if you show one dialog after another
31747             // especially on callbacks..
31748              
31749             if(this.isVisible()){
31750                 
31751                 this.hide();
31752                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31753                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31754                 Roo.log("New Dialog Message:" +  options.msg )
31755                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31756                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31757                 
31758             }
31759             var d = this.getDialog();
31760             opt = options;
31761             d.setTitle(opt.title || "&#160;");
31762             d.close.setDisplayed(opt.closable !== false);
31763             activeTextEl = textboxEl;
31764             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31765             if(opt.prompt){
31766                 if(opt.multiline){
31767                     textboxEl.hide();
31768                     textareaEl.show();
31769                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31770                         opt.multiline : this.defaultTextHeight);
31771                     activeTextEl = textareaEl;
31772                 }else{
31773                     textboxEl.show();
31774                     textareaEl.hide();
31775                 }
31776             }else{
31777                 textboxEl.hide();
31778                 textareaEl.hide();
31779             }
31780             progressEl.setDisplayed(opt.progress === true);
31781             this.updateProgress(0);
31782             activeTextEl.dom.value = opt.value || "";
31783             if(opt.prompt){
31784                 dlg.setDefaultButton(activeTextEl);
31785             }else{
31786                 var bs = opt.buttons;
31787                 var db = null;
31788                 if(bs && bs.ok){
31789                     db = buttons["ok"];
31790                 }else if(bs && bs.yes){
31791                     db = buttons["yes"];
31792                 }
31793                 dlg.setDefaultButton(db);
31794             }
31795             bwidth = updateButtons(opt.buttons);
31796             this.updateText(opt.msg);
31797             if(opt.cls){
31798                 d.el.addClass(opt.cls);
31799             }
31800             d.proxyDrag = opt.proxyDrag === true;
31801             d.modal = opt.modal !== false;
31802             d.mask = opt.modal !== false ? mask : false;
31803             if(!d.isVisible()){
31804                 // force it to the end of the z-index stack so it gets a cursor in FF
31805                 document.body.appendChild(dlg.el.dom);
31806                 d.animateTarget = null;
31807                 d.show(options.animEl);
31808             }
31809             return this;
31810         },
31811
31812         /**
31813          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31814          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31815          * and closing the message box when the process is complete.
31816          * @param {String} title The title bar text
31817          * @param {String} msg The message box body text
31818          * @return {Roo.MessageBox} This message box
31819          */
31820         progress : function(title, msg){
31821             this.show({
31822                 title : title,
31823                 msg : msg,
31824                 buttons: false,
31825                 progress:true,
31826                 closable:false,
31827                 minWidth: this.minProgressWidth,
31828                 modal : true
31829             });
31830             return this;
31831         },
31832
31833         /**
31834          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31835          * If a callback function is passed it will be called after the user clicks the button, and the
31836          * id of the button that was clicked will be passed as the only parameter to the callback
31837          * (could also be the top-right close button).
31838          * @param {String} title The title bar text
31839          * @param {String} msg The message box body text
31840          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31841          * @param {Object} scope (optional) The scope of the callback function
31842          * @return {Roo.MessageBox} This message box
31843          */
31844         alert : function(title, msg, fn, scope){
31845             this.show({
31846                 title : title,
31847                 msg : msg,
31848                 buttons: this.OK,
31849                 fn: fn,
31850                 scope : scope,
31851                 modal : true
31852             });
31853             return this;
31854         },
31855
31856         /**
31857          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31858          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31859          * You are responsible for closing the message box when the process is complete.
31860          * @param {String} msg The message box body text
31861          * @param {String} title (optional) The title bar text
31862          * @return {Roo.MessageBox} This message box
31863          */
31864         wait : function(msg, title){
31865             this.show({
31866                 title : title,
31867                 msg : msg,
31868                 buttons: false,
31869                 closable:false,
31870                 progress:true,
31871                 modal:true,
31872                 width:300,
31873                 wait:true
31874             });
31875             waitTimer = Roo.TaskMgr.start({
31876                 run: function(i){
31877                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31878                 },
31879                 interval: 1000
31880             });
31881             return this;
31882         },
31883
31884         /**
31885          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31886          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31887          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31888          * @param {String} title The title bar text
31889          * @param {String} msg The message box body text
31890          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31891          * @param {Object} scope (optional) The scope of the callback function
31892          * @return {Roo.MessageBox} This message box
31893          */
31894         confirm : function(title, msg, fn, scope){
31895             this.show({
31896                 title : title,
31897                 msg : msg,
31898                 buttons: this.YESNO,
31899                 fn: fn,
31900                 scope : scope,
31901                 modal : true
31902             });
31903             return this;
31904         },
31905
31906         /**
31907          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31908          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31909          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31910          * (could also be the top-right close button) and the text that was entered will be passed as the two
31911          * parameters to the callback.
31912          * @param {String} title The title bar text
31913          * @param {String} msg The message box body text
31914          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31915          * @param {Object} scope (optional) The scope of the callback function
31916          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31917          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31918          * @return {Roo.MessageBox} This message box
31919          */
31920         prompt : function(title, msg, fn, scope, multiline){
31921             this.show({
31922                 title : title,
31923                 msg : msg,
31924                 buttons: this.OKCANCEL,
31925                 fn: fn,
31926                 minWidth:250,
31927                 scope : scope,
31928                 prompt:true,
31929                 multiline: multiline,
31930                 modal : true
31931             });
31932             return this;
31933         },
31934
31935         /**
31936          * Button config that displays a single OK button
31937          * @type Object
31938          */
31939         OK : {ok:true},
31940         /**
31941          * Button config that displays Yes and No buttons
31942          * @type Object
31943          */
31944         YESNO : {yes:true, no:true},
31945         /**
31946          * Button config that displays OK and Cancel buttons
31947          * @type Object
31948          */
31949         OKCANCEL : {ok:true, cancel:true},
31950         /**
31951          * Button config that displays Yes, No and Cancel buttons
31952          * @type Object
31953          */
31954         YESNOCANCEL : {yes:true, no:true, cancel:true},
31955
31956         /**
31957          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31958          * @type Number
31959          */
31960         defaultTextHeight : 75,
31961         /**
31962          * The maximum width in pixels of the message box (defaults to 600)
31963          * @type Number
31964          */
31965         maxWidth : 600,
31966         /**
31967          * The minimum width in pixels of the message box (defaults to 100)
31968          * @type Number
31969          */
31970         minWidth : 100,
31971         /**
31972          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31973          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31974          * @type Number
31975          */
31976         minProgressWidth : 250,
31977         /**
31978          * An object containing the default button text strings that can be overriden for localized language support.
31979          * Supported properties are: ok, cancel, yes and no.
31980          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31981          * @type Object
31982          */
31983         buttonText : {
31984             ok : "OK",
31985             cancel : "Cancel",
31986             yes : "Yes",
31987             no : "No"
31988         }
31989     };
31990 }();
31991
31992 /**
31993  * Shorthand for {@link Roo.MessageBox}
31994  */
31995 Roo.Msg = Roo.MessageBox;/*
31996  * Based on:
31997  * Ext JS Library 1.1.1
31998  * Copyright(c) 2006-2007, Ext JS, LLC.
31999  *
32000  * Originally Released Under LGPL - original licence link has changed is not relivant.
32001  *
32002  * Fork - LGPL
32003  * <script type="text/javascript">
32004  */
32005 /**
32006  * @class Roo.QuickTips
32007  * Provides attractive and customizable tooltips for any element.
32008  * @singleton
32009  */
32010 Roo.QuickTips = function(){
32011     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
32012     var ce, bd, xy, dd;
32013     var visible = false, disabled = true, inited = false;
32014     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
32015     
32016     var onOver = function(e){
32017         if(disabled){
32018             return;
32019         }
32020         var t = e.getTarget();
32021         if(!t || t.nodeType !== 1 || t == document || t == document.body){
32022             return;
32023         }
32024         if(ce && t == ce.el){
32025             clearTimeout(hideProc);
32026             return;
32027         }
32028         if(t && tagEls[t.id]){
32029             tagEls[t.id].el = t;
32030             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
32031             return;
32032         }
32033         var ttp, et = Roo.fly(t);
32034         var ns = cfg.namespace;
32035         if(tm.interceptTitles && t.title){
32036             ttp = t.title;
32037             t.qtip = ttp;
32038             t.removeAttribute("title");
32039             e.preventDefault();
32040         }else{
32041             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
32042         }
32043         if(ttp){
32044             showProc = show.defer(tm.showDelay, tm, [{
32045                 el: t, 
32046                 text: ttp, 
32047                 width: et.getAttributeNS(ns, cfg.width),
32048                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
32049                 title: et.getAttributeNS(ns, cfg.title),
32050                     cls: et.getAttributeNS(ns, cfg.cls)
32051             }]);
32052         }
32053     };
32054     
32055     var onOut = function(e){
32056         clearTimeout(showProc);
32057         var t = e.getTarget();
32058         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
32059             hideProc = setTimeout(hide, tm.hideDelay);
32060         }
32061     };
32062     
32063     var onMove = function(e){
32064         if(disabled){
32065             return;
32066         }
32067         xy = e.getXY();
32068         xy[1] += 18;
32069         if(tm.trackMouse && ce){
32070             el.setXY(xy);
32071         }
32072     };
32073     
32074     var onDown = function(e){
32075         clearTimeout(showProc);
32076         clearTimeout(hideProc);
32077         if(!e.within(el)){
32078             if(tm.hideOnClick){
32079                 hide();
32080                 tm.disable();
32081                 tm.enable.defer(100, tm);
32082             }
32083         }
32084     };
32085     
32086     var getPad = function(){
32087         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
32088     };
32089
32090     var show = function(o){
32091         if(disabled){
32092             return;
32093         }
32094         clearTimeout(dismissProc);
32095         ce = o;
32096         if(removeCls){ // in case manually hidden
32097             el.removeClass(removeCls);
32098             removeCls = null;
32099         }
32100         if(ce.cls){
32101             el.addClass(ce.cls);
32102             removeCls = ce.cls;
32103         }
32104         if(ce.title){
32105             tipTitle.update(ce.title);
32106             tipTitle.show();
32107         }else{
32108             tipTitle.update('');
32109             tipTitle.hide();
32110         }
32111         el.dom.style.width  = tm.maxWidth+'px';
32112         //tipBody.dom.style.width = '';
32113         tipBodyText.update(o.text);
32114         var p = getPad(), w = ce.width;
32115         if(!w){
32116             var td = tipBodyText.dom;
32117             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
32118             if(aw > tm.maxWidth){
32119                 w = tm.maxWidth;
32120             }else if(aw < tm.minWidth){
32121                 w = tm.minWidth;
32122             }else{
32123                 w = aw;
32124             }
32125         }
32126         //tipBody.setWidth(w);
32127         el.setWidth(parseInt(w, 10) + p);
32128         if(ce.autoHide === false){
32129             close.setDisplayed(true);
32130             if(dd){
32131                 dd.unlock();
32132             }
32133         }else{
32134             close.setDisplayed(false);
32135             if(dd){
32136                 dd.lock();
32137             }
32138         }
32139         if(xy){
32140             el.avoidY = xy[1]-18;
32141             el.setXY(xy);
32142         }
32143         if(tm.animate){
32144             el.setOpacity(.1);
32145             el.setStyle("visibility", "visible");
32146             el.fadeIn({callback: afterShow});
32147         }else{
32148             afterShow();
32149         }
32150     };
32151     
32152     var afterShow = function(){
32153         if(ce){
32154             el.show();
32155             esc.enable();
32156             if(tm.autoDismiss && ce.autoHide !== false){
32157                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
32158             }
32159         }
32160     };
32161     
32162     var hide = function(noanim){
32163         clearTimeout(dismissProc);
32164         clearTimeout(hideProc);
32165         ce = null;
32166         if(el.isVisible()){
32167             esc.disable();
32168             if(noanim !== true && tm.animate){
32169                 el.fadeOut({callback: afterHide});
32170             }else{
32171                 afterHide();
32172             } 
32173         }
32174     };
32175     
32176     var afterHide = function(){
32177         el.hide();
32178         if(removeCls){
32179             el.removeClass(removeCls);
32180             removeCls = null;
32181         }
32182     };
32183     
32184     return {
32185         /**
32186         * @cfg {Number} minWidth
32187         * The minimum width of the quick tip (defaults to 40)
32188         */
32189        minWidth : 40,
32190         /**
32191         * @cfg {Number} maxWidth
32192         * The maximum width of the quick tip (defaults to 300)
32193         */
32194        maxWidth : 300,
32195         /**
32196         * @cfg {Boolean} interceptTitles
32197         * True to automatically use the element's DOM title value if available (defaults to false)
32198         */
32199        interceptTitles : false,
32200         /**
32201         * @cfg {Boolean} trackMouse
32202         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
32203         */
32204        trackMouse : false,
32205         /**
32206         * @cfg {Boolean} hideOnClick
32207         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
32208         */
32209        hideOnClick : true,
32210         /**
32211         * @cfg {Number} showDelay
32212         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
32213         */
32214        showDelay : 500,
32215         /**
32216         * @cfg {Number} hideDelay
32217         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
32218         */
32219        hideDelay : 200,
32220         /**
32221         * @cfg {Boolean} autoHide
32222         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32223         * Used in conjunction with hideDelay.
32224         */
32225        autoHide : true,
32226         /**
32227         * @cfg {Boolean}
32228         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32229         * (defaults to true).  Used in conjunction with autoDismissDelay.
32230         */
32231        autoDismiss : true,
32232         /**
32233         * @cfg {Number}
32234         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32235         */
32236        autoDismissDelay : 5000,
32237        /**
32238         * @cfg {Boolean} animate
32239         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32240         */
32241        animate : false,
32242
32243        /**
32244         * @cfg {String} title
32245         * Title text to display (defaults to '').  This can be any valid HTML markup.
32246         */
32247         title: '',
32248        /**
32249         * @cfg {String} text
32250         * Body text to display (defaults to '').  This can be any valid HTML markup.
32251         */
32252         text : '',
32253        /**
32254         * @cfg {String} cls
32255         * A CSS class to apply to the base quick tip element (defaults to '').
32256         */
32257         cls : '',
32258        /**
32259         * @cfg {Number} width
32260         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32261         * minWidth or maxWidth.
32262         */
32263         width : null,
32264
32265     /**
32266      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32267      * or display QuickTips in a page.
32268      */
32269        init : function(){
32270           tm = Roo.QuickTips;
32271           cfg = tm.tagConfig;
32272           if(!inited){
32273               if(!Roo.isReady){ // allow calling of init() before onReady
32274                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32275                   return;
32276               }
32277               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32278               el.fxDefaults = {stopFx: true};
32279               // maximum custom styling
32280               //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>');
32281               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>');              
32282               tipTitle = el.child('h3');
32283               tipTitle.enableDisplayMode("block");
32284               tipBody = el.child('div.x-tip-bd');
32285               tipBodyText = el.child('div.x-tip-bd-inner');
32286               //bdLeft = el.child('div.x-tip-bd-left');
32287               //bdRight = el.child('div.x-tip-bd-right');
32288               close = el.child('div.x-tip-close');
32289               close.enableDisplayMode("block");
32290               close.on("click", hide);
32291               var d = Roo.get(document);
32292               d.on("mousedown", onDown);
32293               d.on("mouseover", onOver);
32294               d.on("mouseout", onOut);
32295               d.on("mousemove", onMove);
32296               esc = d.addKeyListener(27, hide);
32297               esc.disable();
32298               if(Roo.dd.DD){
32299                   dd = el.initDD("default", null, {
32300                       onDrag : function(){
32301                           el.sync();  
32302                       }
32303                   });
32304                   dd.setHandleElId(tipTitle.id);
32305                   dd.lock();
32306               }
32307               inited = true;
32308           }
32309           this.enable(); 
32310        },
32311
32312     /**
32313      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32314      * are supported:
32315      * <pre>
32316 Property    Type                   Description
32317 ----------  ---------------------  ------------------------------------------------------------------------
32318 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32319      * </ul>
32320      * @param {Object} config The config object
32321      */
32322        register : function(config){
32323            var cs = config instanceof Array ? config : arguments;
32324            for(var i = 0, len = cs.length; i < len; i++) {
32325                var c = cs[i];
32326                var target = c.target;
32327                if(target){
32328                    if(target instanceof Array){
32329                        for(var j = 0, jlen = target.length; j < jlen; j++){
32330                            tagEls[target[j]] = c;
32331                        }
32332                    }else{
32333                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32334                    }
32335                }
32336            }
32337        },
32338
32339     /**
32340      * Removes this quick tip from its element and destroys it.
32341      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32342      */
32343        unregister : function(el){
32344            delete tagEls[Roo.id(el)];
32345        },
32346
32347     /**
32348      * Enable this quick tip.
32349      */
32350        enable : function(){
32351            if(inited && disabled){
32352                locks.pop();
32353                if(locks.length < 1){
32354                    disabled = false;
32355                }
32356            }
32357        },
32358
32359     /**
32360      * Disable this quick tip.
32361      */
32362        disable : function(){
32363           disabled = true;
32364           clearTimeout(showProc);
32365           clearTimeout(hideProc);
32366           clearTimeout(dismissProc);
32367           if(ce){
32368               hide(true);
32369           }
32370           locks.push(1);
32371        },
32372
32373     /**
32374      * Returns true if the quick tip is enabled, else false.
32375      */
32376        isEnabled : function(){
32377             return !disabled;
32378        },
32379
32380         // private
32381        tagConfig : {
32382            namespace : "ext",
32383            attribute : "qtip",
32384            width : "width",
32385            target : "target",
32386            title : "qtitle",
32387            hide : "hide",
32388            cls : "qclass"
32389        }
32390    };
32391 }();
32392
32393 // backwards compat
32394 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32395  * Based on:
32396  * Ext JS Library 1.1.1
32397  * Copyright(c) 2006-2007, Ext JS, LLC.
32398  *
32399  * Originally Released Under LGPL - original licence link has changed is not relivant.
32400  *
32401  * Fork - LGPL
32402  * <script type="text/javascript">
32403  */
32404  
32405
32406 /**
32407  * @class Roo.tree.TreePanel
32408  * @extends Roo.data.Tree
32409
32410  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32411  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32412  * @cfg {Boolean} enableDD true to enable drag and drop
32413  * @cfg {Boolean} enableDrag true to enable just drag
32414  * @cfg {Boolean} enableDrop true to enable just drop
32415  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32416  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32417  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32418  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32419  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32420  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32421  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32422  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32423  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32424  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32425  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32426  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32427  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32428  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32429  * @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>
32430  * @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>
32431  * 
32432  * @constructor
32433  * @param {String/HTMLElement/Element} el The container element
32434  * @param {Object} config
32435  */
32436 Roo.tree.TreePanel = function(el, config){
32437     var root = false;
32438     var loader = false;
32439     if (config.root) {
32440         root = config.root;
32441         delete config.root;
32442     }
32443     if (config.loader) {
32444         loader = config.loader;
32445         delete config.loader;
32446     }
32447     
32448     Roo.apply(this, config);
32449     Roo.tree.TreePanel.superclass.constructor.call(this);
32450     this.el = Roo.get(el);
32451     this.el.addClass('x-tree');
32452     //console.log(root);
32453     if (root) {
32454         this.setRootNode( Roo.factory(root, Roo.tree));
32455     }
32456     if (loader) {
32457         this.loader = Roo.factory(loader, Roo.tree);
32458     }
32459    /**
32460     * Read-only. The id of the container element becomes this TreePanel's id.
32461     */
32462     this.id = this.el.id;
32463     this.addEvents({
32464         /**
32465         * @event beforeload
32466         * Fires before a node is loaded, return false to cancel
32467         * @param {Node} node The node being loaded
32468         */
32469         "beforeload" : true,
32470         /**
32471         * @event load
32472         * Fires when a node is loaded
32473         * @param {Node} node The node that was loaded
32474         */
32475         "load" : true,
32476         /**
32477         * @event textchange
32478         * Fires when the text for a node is changed
32479         * @param {Node} node The node
32480         * @param {String} text The new text
32481         * @param {String} oldText The old text
32482         */
32483         "textchange" : true,
32484         /**
32485         * @event beforeexpand
32486         * Fires before a node is expanded, return false to cancel.
32487         * @param {Node} node The node
32488         * @param {Boolean} deep
32489         * @param {Boolean} anim
32490         */
32491         "beforeexpand" : true,
32492         /**
32493         * @event beforecollapse
32494         * Fires before a node is collapsed, return false to cancel.
32495         * @param {Node} node The node
32496         * @param {Boolean} deep
32497         * @param {Boolean} anim
32498         */
32499         "beforecollapse" : true,
32500         /**
32501         * @event expand
32502         * Fires when a node is expanded
32503         * @param {Node} node The node
32504         */
32505         "expand" : true,
32506         /**
32507         * @event disabledchange
32508         * Fires when the disabled status of a node changes
32509         * @param {Node} node The node
32510         * @param {Boolean} disabled
32511         */
32512         "disabledchange" : true,
32513         /**
32514         * @event collapse
32515         * Fires when a node is collapsed
32516         * @param {Node} node The node
32517         */
32518         "collapse" : true,
32519         /**
32520         * @event beforeclick
32521         * Fires before click processing on a node. Return false to cancel the default action.
32522         * @param {Node} node The node
32523         * @param {Roo.EventObject} e The event object
32524         */
32525         "beforeclick":true,
32526         /**
32527         * @event checkchange
32528         * Fires when a node with a checkbox's checked property changes
32529         * @param {Node} this This node
32530         * @param {Boolean} checked
32531         */
32532         "checkchange":true,
32533         /**
32534         * @event click
32535         * Fires when a node is clicked
32536         * @param {Node} node The node
32537         * @param {Roo.EventObject} e The event object
32538         */
32539         "click":true,
32540         /**
32541         * @event dblclick
32542         * Fires when a node is double clicked
32543         * @param {Node} node The node
32544         * @param {Roo.EventObject} e The event object
32545         */
32546         "dblclick":true,
32547         /**
32548         * @event contextmenu
32549         * Fires when a node is right clicked
32550         * @param {Node} node The node
32551         * @param {Roo.EventObject} e The event object
32552         */
32553         "contextmenu":true,
32554         /**
32555         * @event beforechildrenrendered
32556         * Fires right before the child nodes for a node are rendered
32557         * @param {Node} node The node
32558         */
32559         "beforechildrenrendered":true,
32560         /**
32561         * @event startdrag
32562         * Fires when a node starts being dragged
32563         * @param {Roo.tree.TreePanel} this
32564         * @param {Roo.tree.TreeNode} node
32565         * @param {event} e The raw browser event
32566         */ 
32567        "startdrag" : true,
32568        /**
32569         * @event enddrag
32570         * Fires when a drag operation is complete
32571         * @param {Roo.tree.TreePanel} this
32572         * @param {Roo.tree.TreeNode} node
32573         * @param {event} e The raw browser event
32574         */
32575        "enddrag" : true,
32576        /**
32577         * @event dragdrop
32578         * Fires when a dragged node is dropped on a valid DD target
32579         * @param {Roo.tree.TreePanel} this
32580         * @param {Roo.tree.TreeNode} node
32581         * @param {DD} dd The dd it was dropped on
32582         * @param {event} e The raw browser event
32583         */
32584        "dragdrop" : true,
32585        /**
32586         * @event beforenodedrop
32587         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32588         * passed to handlers has the following properties:<br />
32589         * <ul style="padding:5px;padding-left:16px;">
32590         * <li>tree - The TreePanel</li>
32591         * <li>target - The node being targeted for the drop</li>
32592         * <li>data - The drag data from the drag source</li>
32593         * <li>point - The point of the drop - append, above or below</li>
32594         * <li>source - The drag source</li>
32595         * <li>rawEvent - Raw mouse event</li>
32596         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32597         * to be inserted by setting them on this object.</li>
32598         * <li>cancel - Set this to true to cancel the drop.</li>
32599         * </ul>
32600         * @param {Object} dropEvent
32601         */
32602        "beforenodedrop" : true,
32603        /**
32604         * @event nodedrop
32605         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32606         * passed to handlers has the following properties:<br />
32607         * <ul style="padding:5px;padding-left:16px;">
32608         * <li>tree - The TreePanel</li>
32609         * <li>target - The node being targeted for the drop</li>
32610         * <li>data - The drag data from the drag source</li>
32611         * <li>point - The point of the drop - append, above or below</li>
32612         * <li>source - The drag source</li>
32613         * <li>rawEvent - Raw mouse event</li>
32614         * <li>dropNode - Dropped node(s).</li>
32615         * </ul>
32616         * @param {Object} dropEvent
32617         */
32618        "nodedrop" : true,
32619         /**
32620         * @event nodedragover
32621         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32622         * passed to handlers has the following properties:<br />
32623         * <ul style="padding:5px;padding-left:16px;">
32624         * <li>tree - The TreePanel</li>
32625         * <li>target - The node being targeted for the drop</li>
32626         * <li>data - The drag data from the drag source</li>
32627         * <li>point - The point of the drop - append, above or below</li>
32628         * <li>source - The drag source</li>
32629         * <li>rawEvent - Raw mouse event</li>
32630         * <li>dropNode - Drop node(s) provided by the source.</li>
32631         * <li>cancel - Set this to true to signal drop not allowed.</li>
32632         * </ul>
32633         * @param {Object} dragOverEvent
32634         */
32635        "nodedragover" : true
32636         
32637     });
32638     if(this.singleExpand){
32639        this.on("beforeexpand", this.restrictExpand, this);
32640     }
32641     if (this.editor) {
32642         this.editor.tree = this;
32643         this.editor = Roo.factory(this.editor, Roo.tree);
32644     }
32645     
32646     if (this.selModel) {
32647         this.selModel = Roo.factory(this.selModel, Roo.tree);
32648     }
32649    
32650 };
32651 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32652     rootVisible : true,
32653     animate: Roo.enableFx,
32654     lines : true,
32655     enableDD : false,
32656     hlDrop : Roo.enableFx,
32657   
32658     renderer: false,
32659     
32660     rendererTip: false,
32661     // private
32662     restrictExpand : function(node){
32663         var p = node.parentNode;
32664         if(p){
32665             if(p.expandedChild && p.expandedChild.parentNode == p){
32666                 p.expandedChild.collapse();
32667             }
32668             p.expandedChild = node;
32669         }
32670     },
32671
32672     // private override
32673     setRootNode : function(node){
32674         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32675         if(!this.rootVisible){
32676             node.ui = new Roo.tree.RootTreeNodeUI(node);
32677         }
32678         return node;
32679     },
32680
32681     /**
32682      * Returns the container element for this TreePanel
32683      */
32684     getEl : function(){
32685         return this.el;
32686     },
32687
32688     /**
32689      * Returns the default TreeLoader for this TreePanel
32690      */
32691     getLoader : function(){
32692         return this.loader;
32693     },
32694
32695     /**
32696      * Expand all nodes
32697      */
32698     expandAll : function(){
32699         this.root.expand(true);
32700     },
32701
32702     /**
32703      * Collapse all nodes
32704      */
32705     collapseAll : function(){
32706         this.root.collapse(true);
32707     },
32708
32709     /**
32710      * Returns the selection model used by this TreePanel
32711      */
32712     getSelectionModel : function(){
32713         if(!this.selModel){
32714             this.selModel = new Roo.tree.DefaultSelectionModel();
32715         }
32716         return this.selModel;
32717     },
32718
32719     /**
32720      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32721      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32722      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32723      * @return {Array}
32724      */
32725     getChecked : function(a, startNode){
32726         startNode = startNode || this.root;
32727         var r = [];
32728         var f = function(){
32729             if(this.attributes.checked){
32730                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32731             }
32732         }
32733         startNode.cascade(f);
32734         return r;
32735     },
32736
32737     /**
32738      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32739      * @param {String} path
32740      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32741      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32742      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32743      */
32744     expandPath : function(path, attr, callback){
32745         attr = attr || "id";
32746         var keys = path.split(this.pathSeparator);
32747         var curNode = this.root;
32748         if(curNode.attributes[attr] != keys[1]){ // invalid root
32749             if(callback){
32750                 callback(false, null);
32751             }
32752             return;
32753         }
32754         var index = 1;
32755         var f = function(){
32756             if(++index == keys.length){
32757                 if(callback){
32758                     callback(true, curNode);
32759                 }
32760                 return;
32761             }
32762             var c = curNode.findChild(attr, keys[index]);
32763             if(!c){
32764                 if(callback){
32765                     callback(false, curNode);
32766                 }
32767                 return;
32768             }
32769             curNode = c;
32770             c.expand(false, false, f);
32771         };
32772         curNode.expand(false, false, f);
32773     },
32774
32775     /**
32776      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32777      * @param {String} path
32778      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32779      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32780      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32781      */
32782     selectPath : function(path, attr, callback){
32783         attr = attr || "id";
32784         var keys = path.split(this.pathSeparator);
32785         var v = keys.pop();
32786         if(keys.length > 0){
32787             var f = function(success, node){
32788                 if(success && node){
32789                     var n = node.findChild(attr, v);
32790                     if(n){
32791                         n.select();
32792                         if(callback){
32793                             callback(true, n);
32794                         }
32795                     }else if(callback){
32796                         callback(false, n);
32797                     }
32798                 }else{
32799                     if(callback){
32800                         callback(false, n);
32801                     }
32802                 }
32803             };
32804             this.expandPath(keys.join(this.pathSeparator), attr, f);
32805         }else{
32806             this.root.select();
32807             if(callback){
32808                 callback(true, this.root);
32809             }
32810         }
32811     },
32812
32813     getTreeEl : function(){
32814         return this.el;
32815     },
32816
32817     /**
32818      * Trigger rendering of this TreePanel
32819      */
32820     render : function(){
32821         if (this.innerCt) {
32822             return this; // stop it rendering more than once!!
32823         }
32824         
32825         this.innerCt = this.el.createChild({tag:"ul",
32826                cls:"x-tree-root-ct " +
32827                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32828
32829         if(this.containerScroll){
32830             Roo.dd.ScrollManager.register(this.el);
32831         }
32832         if((this.enableDD || this.enableDrop) && !this.dropZone){
32833            /**
32834             * The dropZone used by this tree if drop is enabled
32835             * @type Roo.tree.TreeDropZone
32836             */
32837              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32838                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32839            });
32840         }
32841         if((this.enableDD || this.enableDrag) && !this.dragZone){
32842            /**
32843             * The dragZone used by this tree if drag is enabled
32844             * @type Roo.tree.TreeDragZone
32845             */
32846             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32847                ddGroup: this.ddGroup || "TreeDD",
32848                scroll: this.ddScroll
32849            });
32850         }
32851         this.getSelectionModel().init(this);
32852         if (!this.root) {
32853             Roo.log("ROOT not set in tree");
32854             return this;
32855         }
32856         this.root.render();
32857         if(!this.rootVisible){
32858             this.root.renderChildren();
32859         }
32860         return this;
32861     }
32862 });/*
32863  * Based on:
32864  * Ext JS Library 1.1.1
32865  * Copyright(c) 2006-2007, Ext JS, LLC.
32866  *
32867  * Originally Released Under LGPL - original licence link has changed is not relivant.
32868  *
32869  * Fork - LGPL
32870  * <script type="text/javascript">
32871  */
32872  
32873
32874 /**
32875  * @class Roo.tree.DefaultSelectionModel
32876  * @extends Roo.util.Observable
32877  * The default single selection for a TreePanel.
32878  * @param {Object} cfg Configuration
32879  */
32880 Roo.tree.DefaultSelectionModel = function(cfg){
32881    this.selNode = null;
32882    
32883    
32884    
32885    this.addEvents({
32886        /**
32887         * @event selectionchange
32888         * Fires when the selected node changes
32889         * @param {DefaultSelectionModel} this
32890         * @param {TreeNode} node the new selection
32891         */
32892        "selectionchange" : true,
32893
32894        /**
32895         * @event beforeselect
32896         * Fires before the selected node changes, return false to cancel the change
32897         * @param {DefaultSelectionModel} this
32898         * @param {TreeNode} node the new selection
32899         * @param {TreeNode} node the old selection
32900         */
32901        "beforeselect" : true
32902    });
32903    
32904     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32905 };
32906
32907 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32908     init : function(tree){
32909         this.tree = tree;
32910         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32911         tree.on("click", this.onNodeClick, this);
32912     },
32913     
32914     onNodeClick : function(node, e){
32915         if (e.ctrlKey && this.selNode == node)  {
32916             this.unselect(node);
32917             return;
32918         }
32919         this.select(node);
32920     },
32921     
32922     /**
32923      * Select a node.
32924      * @param {TreeNode} node The node to select
32925      * @return {TreeNode} The selected node
32926      */
32927     select : function(node){
32928         var last = this.selNode;
32929         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32930             if(last){
32931                 last.ui.onSelectedChange(false);
32932             }
32933             this.selNode = node;
32934             node.ui.onSelectedChange(true);
32935             this.fireEvent("selectionchange", this, node, last);
32936         }
32937         return node;
32938     },
32939     
32940     /**
32941      * Deselect a node.
32942      * @param {TreeNode} node The node to unselect
32943      */
32944     unselect : function(node){
32945         if(this.selNode == node){
32946             this.clearSelections();
32947         }    
32948     },
32949     
32950     /**
32951      * Clear all selections
32952      */
32953     clearSelections : function(){
32954         var n = this.selNode;
32955         if(n){
32956             n.ui.onSelectedChange(false);
32957             this.selNode = null;
32958             this.fireEvent("selectionchange", this, null);
32959         }
32960         return n;
32961     },
32962     
32963     /**
32964      * Get the selected node
32965      * @return {TreeNode} The selected node
32966      */
32967     getSelectedNode : function(){
32968         return this.selNode;    
32969     },
32970     
32971     /**
32972      * Returns true if the node is selected
32973      * @param {TreeNode} node The node to check
32974      * @return {Boolean}
32975      */
32976     isSelected : function(node){
32977         return this.selNode == node;  
32978     },
32979
32980     /**
32981      * Selects the node above the selected node in the tree, intelligently walking the nodes
32982      * @return TreeNode The new selection
32983      */
32984     selectPrevious : function(){
32985         var s = this.selNode || this.lastSelNode;
32986         if(!s){
32987             return null;
32988         }
32989         var ps = s.previousSibling;
32990         if(ps){
32991             if(!ps.isExpanded() || ps.childNodes.length < 1){
32992                 return this.select(ps);
32993             } else{
32994                 var lc = ps.lastChild;
32995                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32996                     lc = lc.lastChild;
32997                 }
32998                 return this.select(lc);
32999             }
33000         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
33001             return this.select(s.parentNode);
33002         }
33003         return null;
33004     },
33005
33006     /**
33007      * Selects the node above the selected node in the tree, intelligently walking the nodes
33008      * @return TreeNode The new selection
33009      */
33010     selectNext : function(){
33011         var s = this.selNode || this.lastSelNode;
33012         if(!s){
33013             return null;
33014         }
33015         if(s.firstChild && s.isExpanded()){
33016              return this.select(s.firstChild);
33017          }else if(s.nextSibling){
33018              return this.select(s.nextSibling);
33019          }else if(s.parentNode){
33020             var newS = null;
33021             s.parentNode.bubble(function(){
33022                 if(this.nextSibling){
33023                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
33024                     return false;
33025                 }
33026             });
33027             return newS;
33028          }
33029         return null;
33030     },
33031
33032     onKeyDown : function(e){
33033         var s = this.selNode || this.lastSelNode;
33034         // undesirable, but required
33035         var sm = this;
33036         if(!s){
33037             return;
33038         }
33039         var k = e.getKey();
33040         switch(k){
33041              case e.DOWN:
33042                  e.stopEvent();
33043                  this.selectNext();
33044              break;
33045              case e.UP:
33046                  e.stopEvent();
33047                  this.selectPrevious();
33048              break;
33049              case e.RIGHT:
33050                  e.preventDefault();
33051                  if(s.hasChildNodes()){
33052                      if(!s.isExpanded()){
33053                          s.expand();
33054                      }else if(s.firstChild){
33055                          this.select(s.firstChild, e);
33056                      }
33057                  }
33058              break;
33059              case e.LEFT:
33060                  e.preventDefault();
33061                  if(s.hasChildNodes() && s.isExpanded()){
33062                      s.collapse();
33063                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
33064                      this.select(s.parentNode, e);
33065                  }
33066              break;
33067         };
33068     }
33069 });
33070
33071 /**
33072  * @class Roo.tree.MultiSelectionModel
33073  * @extends Roo.util.Observable
33074  * Multi selection for a TreePanel.
33075  * @param {Object} cfg Configuration
33076  */
33077 Roo.tree.MultiSelectionModel = function(){
33078    this.selNodes = [];
33079    this.selMap = {};
33080    this.addEvents({
33081        /**
33082         * @event selectionchange
33083         * Fires when the selected nodes change
33084         * @param {MultiSelectionModel} this
33085         * @param {Array} nodes Array of the selected nodes
33086         */
33087        "selectionchange" : true
33088    });
33089    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
33090    
33091 };
33092
33093 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
33094     init : function(tree){
33095         this.tree = tree;
33096         tree.getTreeEl().on("keydown", this.onKeyDown, this);
33097         tree.on("click", this.onNodeClick, this);
33098     },
33099     
33100     onNodeClick : function(node, e){
33101         this.select(node, e, e.ctrlKey);
33102     },
33103     
33104     /**
33105      * Select a node.
33106      * @param {TreeNode} node The node to select
33107      * @param {EventObject} e (optional) An event associated with the selection
33108      * @param {Boolean} keepExisting True to retain existing selections
33109      * @return {TreeNode} The selected node
33110      */
33111     select : function(node, e, keepExisting){
33112         if(keepExisting !== true){
33113             this.clearSelections(true);
33114         }
33115         if(this.isSelected(node)){
33116             this.lastSelNode = node;
33117             return node;
33118         }
33119         this.selNodes.push(node);
33120         this.selMap[node.id] = node;
33121         this.lastSelNode = node;
33122         node.ui.onSelectedChange(true);
33123         this.fireEvent("selectionchange", this, this.selNodes);
33124         return node;
33125     },
33126     
33127     /**
33128      * Deselect a node.
33129      * @param {TreeNode} node The node to unselect
33130      */
33131     unselect : function(node){
33132         if(this.selMap[node.id]){
33133             node.ui.onSelectedChange(false);
33134             var sn = this.selNodes;
33135             var index = -1;
33136             if(sn.indexOf){
33137                 index = sn.indexOf(node);
33138             }else{
33139                 for(var i = 0, len = sn.length; i < len; i++){
33140                     if(sn[i] == node){
33141                         index = i;
33142                         break;
33143                     }
33144                 }
33145             }
33146             if(index != -1){
33147                 this.selNodes.splice(index, 1);
33148             }
33149             delete this.selMap[node.id];
33150             this.fireEvent("selectionchange", this, this.selNodes);
33151         }
33152     },
33153     
33154     /**
33155      * Clear all selections
33156      */
33157     clearSelections : function(suppressEvent){
33158         var sn = this.selNodes;
33159         if(sn.length > 0){
33160             for(var i = 0, len = sn.length; i < len; i++){
33161                 sn[i].ui.onSelectedChange(false);
33162             }
33163             this.selNodes = [];
33164             this.selMap = {};
33165             if(suppressEvent !== true){
33166                 this.fireEvent("selectionchange", this, this.selNodes);
33167             }
33168         }
33169     },
33170     
33171     /**
33172      * Returns true if the node is selected
33173      * @param {TreeNode} node The node to check
33174      * @return {Boolean}
33175      */
33176     isSelected : function(node){
33177         return this.selMap[node.id] ? true : false;  
33178     },
33179     
33180     /**
33181      * Returns an array of the selected nodes
33182      * @return {Array}
33183      */
33184     getSelectedNodes : function(){
33185         return this.selNodes;    
33186     },
33187
33188     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
33189
33190     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
33191
33192     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
33193 });/*
33194  * Based on:
33195  * Ext JS Library 1.1.1
33196  * Copyright(c) 2006-2007, Ext JS, LLC.
33197  *
33198  * Originally Released Under LGPL - original licence link has changed is not relivant.
33199  *
33200  * Fork - LGPL
33201  * <script type="text/javascript">
33202  */
33203  
33204 /**
33205  * @class Roo.tree.TreeNode
33206  * @extends Roo.data.Node
33207  * @cfg {String} text The text for this node
33208  * @cfg {Boolean} expanded true to start the node expanded
33209  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
33210  * @cfg {Boolean} allowDrop false if this node cannot be drop on
33211  * @cfg {Boolean} disabled true to start the node disabled
33212  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
33213  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
33214  * @cfg {String} cls A css class to be added to the node
33215  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
33216  * @cfg {String} href URL of the link used for the node (defaults to #)
33217  * @cfg {String} hrefTarget target frame for the link
33218  * @cfg {String} qtip An Ext QuickTip for the node
33219  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33220  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33221  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33222  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33223  * (defaults to undefined with no checkbox rendered)
33224  * @constructor
33225  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33226  */
33227 Roo.tree.TreeNode = function(attributes){
33228     attributes = attributes || {};
33229     if(typeof attributes == "string"){
33230         attributes = {text: attributes};
33231     }
33232     this.childrenRendered = false;
33233     this.rendered = false;
33234     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33235     this.expanded = attributes.expanded === true;
33236     this.isTarget = attributes.isTarget !== false;
33237     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33238     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33239
33240     /**
33241      * Read-only. The text for this node. To change it use setText().
33242      * @type String
33243      */
33244     this.text = attributes.text;
33245     /**
33246      * True if this node is disabled.
33247      * @type Boolean
33248      */
33249     this.disabled = attributes.disabled === true;
33250
33251     this.addEvents({
33252         /**
33253         * @event textchange
33254         * Fires when the text for this node is changed
33255         * @param {Node} this This node
33256         * @param {String} text The new text
33257         * @param {String} oldText The old text
33258         */
33259         "textchange" : true,
33260         /**
33261         * @event beforeexpand
33262         * Fires before this node is expanded, return false to cancel.
33263         * @param {Node} this This node
33264         * @param {Boolean} deep
33265         * @param {Boolean} anim
33266         */
33267         "beforeexpand" : true,
33268         /**
33269         * @event beforecollapse
33270         * Fires before this node is collapsed, return false to cancel.
33271         * @param {Node} this This node
33272         * @param {Boolean} deep
33273         * @param {Boolean} anim
33274         */
33275         "beforecollapse" : true,
33276         /**
33277         * @event expand
33278         * Fires when this node is expanded
33279         * @param {Node} this This node
33280         */
33281         "expand" : true,
33282         /**
33283         * @event disabledchange
33284         * Fires when the disabled status of this node changes
33285         * @param {Node} this This node
33286         * @param {Boolean} disabled
33287         */
33288         "disabledchange" : true,
33289         /**
33290         * @event collapse
33291         * Fires when this node is collapsed
33292         * @param {Node} this This node
33293         */
33294         "collapse" : true,
33295         /**
33296         * @event beforeclick
33297         * Fires before click processing. Return false to cancel the default action.
33298         * @param {Node} this This node
33299         * @param {Roo.EventObject} e The event object
33300         */
33301         "beforeclick":true,
33302         /**
33303         * @event checkchange
33304         * Fires when a node with a checkbox's checked property changes
33305         * @param {Node} this This node
33306         * @param {Boolean} checked
33307         */
33308         "checkchange":true,
33309         /**
33310         * @event click
33311         * Fires when this node is clicked
33312         * @param {Node} this This node
33313         * @param {Roo.EventObject} e The event object
33314         */
33315         "click":true,
33316         /**
33317         * @event dblclick
33318         * Fires when this node is double clicked
33319         * @param {Node} this This node
33320         * @param {Roo.EventObject} e The event object
33321         */
33322         "dblclick":true,
33323         /**
33324         * @event contextmenu
33325         * Fires when this node is right clicked
33326         * @param {Node} this This node
33327         * @param {Roo.EventObject} e The event object
33328         */
33329         "contextmenu":true,
33330         /**
33331         * @event beforechildrenrendered
33332         * Fires right before the child nodes for this node are rendered
33333         * @param {Node} this This node
33334         */
33335         "beforechildrenrendered":true
33336     });
33337
33338     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33339
33340     /**
33341      * Read-only. The UI for this node
33342      * @type TreeNodeUI
33343      */
33344     this.ui = new uiClass(this);
33345     
33346     // finally support items[]
33347     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33348         return;
33349     }
33350     
33351     
33352     Roo.each(this.attributes.items, function(c) {
33353         this.appendChild(Roo.factory(c,Roo.Tree));
33354     }, this);
33355     delete this.attributes.items;
33356     
33357     
33358     
33359 };
33360 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33361     preventHScroll: true,
33362     /**
33363      * Returns true if this node is expanded
33364      * @return {Boolean}
33365      */
33366     isExpanded : function(){
33367         return this.expanded;
33368     },
33369
33370     /**
33371      * Returns the UI object for this node
33372      * @return {TreeNodeUI}
33373      */
33374     getUI : function(){
33375         return this.ui;
33376     },
33377
33378     // private override
33379     setFirstChild : function(node){
33380         var of = this.firstChild;
33381         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33382         if(this.childrenRendered && of && node != of){
33383             of.renderIndent(true, true);
33384         }
33385         if(this.rendered){
33386             this.renderIndent(true, true);
33387         }
33388     },
33389
33390     // private override
33391     setLastChild : function(node){
33392         var ol = this.lastChild;
33393         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33394         if(this.childrenRendered && ol && node != ol){
33395             ol.renderIndent(true, true);
33396         }
33397         if(this.rendered){
33398             this.renderIndent(true, true);
33399         }
33400     },
33401
33402     // these methods are overridden to provide lazy rendering support
33403     // private override
33404     appendChild : function()
33405     {
33406         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33407         if(node && this.childrenRendered){
33408             node.render();
33409         }
33410         this.ui.updateExpandIcon();
33411         return node;
33412     },
33413
33414     // private override
33415     removeChild : function(node){
33416         this.ownerTree.getSelectionModel().unselect(node);
33417         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33418         // if it's been rendered remove dom node
33419         if(this.childrenRendered){
33420             node.ui.remove();
33421         }
33422         if(this.childNodes.length < 1){
33423             this.collapse(false, false);
33424         }else{
33425             this.ui.updateExpandIcon();
33426         }
33427         if(!this.firstChild) {
33428             this.childrenRendered = false;
33429         }
33430         return node;
33431     },
33432
33433     // private override
33434     insertBefore : function(node, refNode){
33435         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33436         if(newNode && refNode && this.childrenRendered){
33437             node.render();
33438         }
33439         this.ui.updateExpandIcon();
33440         return newNode;
33441     },
33442
33443     /**
33444      * Sets the text for this node
33445      * @param {String} text
33446      */
33447     setText : function(text){
33448         var oldText = this.text;
33449         this.text = text;
33450         this.attributes.text = text;
33451         if(this.rendered){ // event without subscribing
33452             this.ui.onTextChange(this, text, oldText);
33453         }
33454         this.fireEvent("textchange", this, text, oldText);
33455     },
33456
33457     /**
33458      * Triggers selection of this node
33459      */
33460     select : function(){
33461         this.getOwnerTree().getSelectionModel().select(this);
33462     },
33463
33464     /**
33465      * Triggers deselection of this node
33466      */
33467     unselect : function(){
33468         this.getOwnerTree().getSelectionModel().unselect(this);
33469     },
33470
33471     /**
33472      * Returns true if this node is selected
33473      * @return {Boolean}
33474      */
33475     isSelected : function(){
33476         return this.getOwnerTree().getSelectionModel().isSelected(this);
33477     },
33478
33479     /**
33480      * Expand this node.
33481      * @param {Boolean} deep (optional) True to expand all children as well
33482      * @param {Boolean} anim (optional) false to cancel the default animation
33483      * @param {Function} callback (optional) A callback to be called when
33484      * expanding this node completes (does not wait for deep expand to complete).
33485      * Called with 1 parameter, this node.
33486      */
33487     expand : function(deep, anim, callback){
33488         if(!this.expanded){
33489             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33490                 return;
33491             }
33492             if(!this.childrenRendered){
33493                 this.renderChildren();
33494             }
33495             this.expanded = true;
33496             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33497                 this.ui.animExpand(function(){
33498                     this.fireEvent("expand", this);
33499                     if(typeof callback == "function"){
33500                         callback(this);
33501                     }
33502                     if(deep === true){
33503                         this.expandChildNodes(true);
33504                     }
33505                 }.createDelegate(this));
33506                 return;
33507             }else{
33508                 this.ui.expand();
33509                 this.fireEvent("expand", this);
33510                 if(typeof callback == "function"){
33511                     callback(this);
33512                 }
33513             }
33514         }else{
33515            if(typeof callback == "function"){
33516                callback(this);
33517            }
33518         }
33519         if(deep === true){
33520             this.expandChildNodes(true);
33521         }
33522     },
33523
33524     isHiddenRoot : function(){
33525         return this.isRoot && !this.getOwnerTree().rootVisible;
33526     },
33527
33528     /**
33529      * Collapse this node.
33530      * @param {Boolean} deep (optional) True to collapse all children as well
33531      * @param {Boolean} anim (optional) false to cancel the default animation
33532      */
33533     collapse : function(deep, anim){
33534         if(this.expanded && !this.isHiddenRoot()){
33535             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33536                 return;
33537             }
33538             this.expanded = false;
33539             if((this.getOwnerTree().animate && anim !== false) || anim){
33540                 this.ui.animCollapse(function(){
33541                     this.fireEvent("collapse", this);
33542                     if(deep === true){
33543                         this.collapseChildNodes(true);
33544                     }
33545                 }.createDelegate(this));
33546                 return;
33547             }else{
33548                 this.ui.collapse();
33549                 this.fireEvent("collapse", this);
33550             }
33551         }
33552         if(deep === true){
33553             var cs = this.childNodes;
33554             for(var i = 0, len = cs.length; i < len; i++) {
33555                 cs[i].collapse(true, false);
33556             }
33557         }
33558     },
33559
33560     // private
33561     delayedExpand : function(delay){
33562         if(!this.expandProcId){
33563             this.expandProcId = this.expand.defer(delay, this);
33564         }
33565     },
33566
33567     // private
33568     cancelExpand : function(){
33569         if(this.expandProcId){
33570             clearTimeout(this.expandProcId);
33571         }
33572         this.expandProcId = false;
33573     },
33574
33575     /**
33576      * Toggles expanded/collapsed state of the node
33577      */
33578     toggle : function(){
33579         if(this.expanded){
33580             this.collapse();
33581         }else{
33582             this.expand();
33583         }
33584     },
33585
33586     /**
33587      * Ensures all parent nodes are expanded
33588      */
33589     ensureVisible : function(callback){
33590         var tree = this.getOwnerTree();
33591         tree.expandPath(this.parentNode.getPath(), false, function(){
33592             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33593             Roo.callback(callback);
33594         }.createDelegate(this));
33595     },
33596
33597     /**
33598      * Expand all child nodes
33599      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33600      */
33601     expandChildNodes : function(deep){
33602         var cs = this.childNodes;
33603         for(var i = 0, len = cs.length; i < len; i++) {
33604                 cs[i].expand(deep);
33605         }
33606     },
33607
33608     /**
33609      * Collapse all child nodes
33610      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33611      */
33612     collapseChildNodes : function(deep){
33613         var cs = this.childNodes;
33614         for(var i = 0, len = cs.length; i < len; i++) {
33615                 cs[i].collapse(deep);
33616         }
33617     },
33618
33619     /**
33620      * Disables this node
33621      */
33622     disable : function(){
33623         this.disabled = true;
33624         this.unselect();
33625         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33626             this.ui.onDisableChange(this, true);
33627         }
33628         this.fireEvent("disabledchange", this, true);
33629     },
33630
33631     /**
33632      * Enables this node
33633      */
33634     enable : function(){
33635         this.disabled = false;
33636         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33637             this.ui.onDisableChange(this, false);
33638         }
33639         this.fireEvent("disabledchange", this, false);
33640     },
33641
33642     // private
33643     renderChildren : function(suppressEvent){
33644         if(suppressEvent !== false){
33645             this.fireEvent("beforechildrenrendered", this);
33646         }
33647         var cs = this.childNodes;
33648         for(var i = 0, len = cs.length; i < len; i++){
33649             cs[i].render(true);
33650         }
33651         this.childrenRendered = true;
33652     },
33653
33654     // private
33655     sort : function(fn, scope){
33656         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33657         if(this.childrenRendered){
33658             var cs = this.childNodes;
33659             for(var i = 0, len = cs.length; i < len; i++){
33660                 cs[i].render(true);
33661             }
33662         }
33663     },
33664
33665     // private
33666     render : function(bulkRender){
33667         this.ui.render(bulkRender);
33668         if(!this.rendered){
33669             this.rendered = true;
33670             if(this.expanded){
33671                 this.expanded = false;
33672                 this.expand(false, false);
33673             }
33674         }
33675     },
33676
33677     // private
33678     renderIndent : function(deep, refresh){
33679         if(refresh){
33680             this.ui.childIndent = null;
33681         }
33682         this.ui.renderIndent();
33683         if(deep === true && this.childrenRendered){
33684             var cs = this.childNodes;
33685             for(var i = 0, len = cs.length; i < len; i++){
33686                 cs[i].renderIndent(true, refresh);
33687             }
33688         }
33689     }
33690 });/*
33691  * Based on:
33692  * Ext JS Library 1.1.1
33693  * Copyright(c) 2006-2007, Ext JS, LLC.
33694  *
33695  * Originally Released Under LGPL - original licence link has changed is not relivant.
33696  *
33697  * Fork - LGPL
33698  * <script type="text/javascript">
33699  */
33700  
33701 /**
33702  * @class Roo.tree.AsyncTreeNode
33703  * @extends Roo.tree.TreeNode
33704  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33705  * @constructor
33706  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33707  */
33708  Roo.tree.AsyncTreeNode = function(config){
33709     this.loaded = false;
33710     this.loading = false;
33711     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33712     /**
33713     * @event beforeload
33714     * Fires before this node is loaded, return false to cancel
33715     * @param {Node} this This node
33716     */
33717     this.addEvents({'beforeload':true, 'load': true});
33718     /**
33719     * @event load
33720     * Fires when this node is loaded
33721     * @param {Node} this This node
33722     */
33723     /**
33724      * The loader used by this node (defaults to using the tree's defined loader)
33725      * @type TreeLoader
33726      * @property loader
33727      */
33728 };
33729 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33730     expand : function(deep, anim, callback){
33731         if(this.loading){ // if an async load is already running, waiting til it's done
33732             var timer;
33733             var f = function(){
33734                 if(!this.loading){ // done loading
33735                     clearInterval(timer);
33736                     this.expand(deep, anim, callback);
33737                 }
33738             }.createDelegate(this);
33739             timer = setInterval(f, 200);
33740             return;
33741         }
33742         if(!this.loaded){
33743             if(this.fireEvent("beforeload", this) === false){
33744                 return;
33745             }
33746             this.loading = true;
33747             this.ui.beforeLoad(this);
33748             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33749             if(loader){
33750                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33751                 return;
33752             }
33753         }
33754         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33755     },
33756     
33757     /**
33758      * Returns true if this node is currently loading
33759      * @return {Boolean}
33760      */
33761     isLoading : function(){
33762         return this.loading;  
33763     },
33764     
33765     loadComplete : function(deep, anim, callback){
33766         this.loading = false;
33767         this.loaded = true;
33768         this.ui.afterLoad(this);
33769         this.fireEvent("load", this);
33770         this.expand(deep, anim, callback);
33771     },
33772     
33773     /**
33774      * Returns true if this node has been loaded
33775      * @return {Boolean}
33776      */
33777     isLoaded : function(){
33778         return this.loaded;
33779     },
33780     
33781     hasChildNodes : function(){
33782         if(!this.isLeaf() && !this.loaded){
33783             return true;
33784         }else{
33785             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33786         }
33787     },
33788
33789     /**
33790      * Trigger a reload for this node
33791      * @param {Function} callback
33792      */
33793     reload : function(callback){
33794         this.collapse(false, false);
33795         while(this.firstChild){
33796             this.removeChild(this.firstChild);
33797         }
33798         this.childrenRendered = false;
33799         this.loaded = false;
33800         if(this.isHiddenRoot()){
33801             this.expanded = false;
33802         }
33803         this.expand(false, false, callback);
33804     }
33805 });/*
33806  * Based on:
33807  * Ext JS Library 1.1.1
33808  * Copyright(c) 2006-2007, Ext JS, LLC.
33809  *
33810  * Originally Released Under LGPL - original licence link has changed is not relivant.
33811  *
33812  * Fork - LGPL
33813  * <script type="text/javascript">
33814  */
33815  
33816 /**
33817  * @class Roo.tree.TreeNodeUI
33818  * @constructor
33819  * @param {Object} node The node to render
33820  * The TreeNode UI implementation is separate from the
33821  * tree implementation. Unless you are customizing the tree UI,
33822  * you should never have to use this directly.
33823  */
33824 Roo.tree.TreeNodeUI = function(node){
33825     this.node = node;
33826     this.rendered = false;
33827     this.animating = false;
33828     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33829 };
33830
33831 Roo.tree.TreeNodeUI.prototype = {
33832     removeChild : function(node){
33833         if(this.rendered){
33834             this.ctNode.removeChild(node.ui.getEl());
33835         }
33836     },
33837
33838     beforeLoad : function(){
33839          this.addClass("x-tree-node-loading");
33840     },
33841
33842     afterLoad : function(){
33843          this.removeClass("x-tree-node-loading");
33844     },
33845
33846     onTextChange : function(node, text, oldText){
33847         if(this.rendered){
33848             this.textNode.innerHTML = text;
33849         }
33850     },
33851
33852     onDisableChange : function(node, state){
33853         this.disabled = state;
33854         if(state){
33855             this.addClass("x-tree-node-disabled");
33856         }else{
33857             this.removeClass("x-tree-node-disabled");
33858         }
33859     },
33860
33861     onSelectedChange : function(state){
33862         if(state){
33863             this.focus();
33864             this.addClass("x-tree-selected");
33865         }else{
33866             //this.blur();
33867             this.removeClass("x-tree-selected");
33868         }
33869     },
33870
33871     onMove : function(tree, node, oldParent, newParent, index, refNode){
33872         this.childIndent = null;
33873         if(this.rendered){
33874             var targetNode = newParent.ui.getContainer();
33875             if(!targetNode){//target not rendered
33876                 this.holder = document.createElement("div");
33877                 this.holder.appendChild(this.wrap);
33878                 return;
33879             }
33880             var insertBefore = refNode ? refNode.ui.getEl() : null;
33881             if(insertBefore){
33882                 targetNode.insertBefore(this.wrap, insertBefore);
33883             }else{
33884                 targetNode.appendChild(this.wrap);
33885             }
33886             this.node.renderIndent(true);
33887         }
33888     },
33889
33890     addClass : function(cls){
33891         if(this.elNode){
33892             Roo.fly(this.elNode).addClass(cls);
33893         }
33894     },
33895
33896     removeClass : function(cls){
33897         if(this.elNode){
33898             Roo.fly(this.elNode).removeClass(cls);
33899         }
33900     },
33901
33902     remove : function(){
33903         if(this.rendered){
33904             this.holder = document.createElement("div");
33905             this.holder.appendChild(this.wrap);
33906         }
33907     },
33908
33909     fireEvent : function(){
33910         return this.node.fireEvent.apply(this.node, arguments);
33911     },
33912
33913     initEvents : function(){
33914         this.node.on("move", this.onMove, this);
33915         var E = Roo.EventManager;
33916         var a = this.anchor;
33917
33918         var el = Roo.fly(a, '_treeui');
33919
33920         if(Roo.isOpera){ // opera render bug ignores the CSS
33921             el.setStyle("text-decoration", "none");
33922         }
33923
33924         el.on("click", this.onClick, this);
33925         el.on("dblclick", this.onDblClick, this);
33926
33927         if(this.checkbox){
33928             Roo.EventManager.on(this.checkbox,
33929                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33930         }
33931
33932         el.on("contextmenu", this.onContextMenu, this);
33933
33934         var icon = Roo.fly(this.iconNode);
33935         icon.on("click", this.onClick, this);
33936         icon.on("dblclick", this.onDblClick, this);
33937         icon.on("contextmenu", this.onContextMenu, this);
33938         E.on(this.ecNode, "click", this.ecClick, this, true);
33939
33940         if(this.node.disabled){
33941             this.addClass("x-tree-node-disabled");
33942         }
33943         if(this.node.hidden){
33944             this.addClass("x-tree-node-disabled");
33945         }
33946         var ot = this.node.getOwnerTree();
33947         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33948         if(dd && (!this.node.isRoot || ot.rootVisible)){
33949             Roo.dd.Registry.register(this.elNode, {
33950                 node: this.node,
33951                 handles: this.getDDHandles(),
33952                 isHandle: false
33953             });
33954         }
33955     },
33956
33957     getDDHandles : function(){
33958         return [this.iconNode, this.textNode];
33959     },
33960
33961     hide : function(){
33962         if(this.rendered){
33963             this.wrap.style.display = "none";
33964         }
33965     },
33966
33967     show : function(){
33968         if(this.rendered){
33969             this.wrap.style.display = "";
33970         }
33971     },
33972
33973     onContextMenu : function(e){
33974         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33975             e.preventDefault();
33976             this.focus();
33977             this.fireEvent("contextmenu", this.node, e);
33978         }
33979     },
33980
33981     onClick : function(e){
33982         if(this.dropping){
33983             e.stopEvent();
33984             return;
33985         }
33986         if(this.fireEvent("beforeclick", this.node, e) !== false){
33987             if(!this.disabled && this.node.attributes.href){
33988                 this.fireEvent("click", this.node, e);
33989                 return;
33990             }
33991             e.preventDefault();
33992             if(this.disabled){
33993                 return;
33994             }
33995
33996             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33997                 this.node.toggle();
33998             }
33999
34000             this.fireEvent("click", this.node, e);
34001         }else{
34002             e.stopEvent();
34003         }
34004     },
34005
34006     onDblClick : function(e){
34007         e.preventDefault();
34008         if(this.disabled){
34009             return;
34010         }
34011         if(this.checkbox){
34012             this.toggleCheck();
34013         }
34014         if(!this.animating && this.node.hasChildNodes()){
34015             this.node.toggle();
34016         }
34017         this.fireEvent("dblclick", this.node, e);
34018     },
34019
34020     onCheckChange : function(){
34021         var checked = this.checkbox.checked;
34022         this.node.attributes.checked = checked;
34023         this.fireEvent('checkchange', this.node, checked);
34024     },
34025
34026     ecClick : function(e){
34027         if(!this.animating && this.node.hasChildNodes()){
34028             this.node.toggle();
34029         }
34030     },
34031
34032     startDrop : function(){
34033         this.dropping = true;
34034     },
34035
34036     // delayed drop so the click event doesn't get fired on a drop
34037     endDrop : function(){
34038        setTimeout(function(){
34039            this.dropping = false;
34040        }.createDelegate(this), 50);
34041     },
34042
34043     expand : function(){
34044         this.updateExpandIcon();
34045         this.ctNode.style.display = "";
34046     },
34047
34048     focus : function(){
34049         if(!this.node.preventHScroll){
34050             try{this.anchor.focus();
34051             }catch(e){}
34052         }else if(!Roo.isIE){
34053             try{
34054                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
34055                 var l = noscroll.scrollLeft;
34056                 this.anchor.focus();
34057                 noscroll.scrollLeft = l;
34058             }catch(e){}
34059         }
34060     },
34061
34062     toggleCheck : function(value){
34063         var cb = this.checkbox;
34064         if(cb){
34065             cb.checked = (value === undefined ? !cb.checked : value);
34066         }
34067     },
34068
34069     blur : function(){
34070         try{
34071             this.anchor.blur();
34072         }catch(e){}
34073     },
34074
34075     animExpand : function(callback){
34076         var ct = Roo.get(this.ctNode);
34077         ct.stopFx();
34078         if(!this.node.hasChildNodes()){
34079             this.updateExpandIcon();
34080             this.ctNode.style.display = "";
34081             Roo.callback(callback);
34082             return;
34083         }
34084         this.animating = true;
34085         this.updateExpandIcon();
34086
34087         ct.slideIn('t', {
34088            callback : function(){
34089                this.animating = false;
34090                Roo.callback(callback);
34091             },
34092             scope: this,
34093             duration: this.node.ownerTree.duration || .25
34094         });
34095     },
34096
34097     highlight : function(){
34098         var tree = this.node.getOwnerTree();
34099         Roo.fly(this.wrap).highlight(
34100             tree.hlColor || "C3DAF9",
34101             {endColor: tree.hlBaseColor}
34102         );
34103     },
34104
34105     collapse : function(){
34106         this.updateExpandIcon();
34107         this.ctNode.style.display = "none";
34108     },
34109
34110     animCollapse : function(callback){
34111         var ct = Roo.get(this.ctNode);
34112         ct.enableDisplayMode('block');
34113         ct.stopFx();
34114
34115         this.animating = true;
34116         this.updateExpandIcon();
34117
34118         ct.slideOut('t', {
34119             callback : function(){
34120                this.animating = false;
34121                Roo.callback(callback);
34122             },
34123             scope: this,
34124             duration: this.node.ownerTree.duration || .25
34125         });
34126     },
34127
34128     getContainer : function(){
34129         return this.ctNode;
34130     },
34131
34132     getEl : function(){
34133         return this.wrap;
34134     },
34135
34136     appendDDGhost : function(ghostNode){
34137         ghostNode.appendChild(this.elNode.cloneNode(true));
34138     },
34139
34140     getDDRepairXY : function(){
34141         return Roo.lib.Dom.getXY(this.iconNode);
34142     },
34143
34144     onRender : function(){
34145         this.render();
34146     },
34147
34148     render : function(bulkRender){
34149         var n = this.node, a = n.attributes;
34150         var targetNode = n.parentNode ?
34151               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
34152
34153         if(!this.rendered){
34154             this.rendered = true;
34155
34156             this.renderElements(n, a, targetNode, bulkRender);
34157
34158             if(a.qtip){
34159                if(this.textNode.setAttributeNS){
34160                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
34161                    if(a.qtipTitle){
34162                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
34163                    }
34164                }else{
34165                    this.textNode.setAttribute("ext:qtip", a.qtip);
34166                    if(a.qtipTitle){
34167                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
34168                    }
34169                }
34170             }else if(a.qtipCfg){
34171                 a.qtipCfg.target = Roo.id(this.textNode);
34172                 Roo.QuickTips.register(a.qtipCfg);
34173             }
34174             this.initEvents();
34175             if(!this.node.expanded){
34176                 this.updateExpandIcon();
34177             }
34178         }else{
34179             if(bulkRender === true) {
34180                 targetNode.appendChild(this.wrap);
34181             }
34182         }
34183     },
34184
34185     renderElements : function(n, a, targetNode, bulkRender)
34186     {
34187         // add some indent caching, this helps performance when rendering a large tree
34188         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34189         var t = n.getOwnerTree();
34190         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
34191         if (typeof(n.attributes.html) != 'undefined') {
34192             txt = n.attributes.html;
34193         }
34194         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
34195         var cb = typeof a.checked == 'boolean';
34196         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34197         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
34198             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
34199             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
34200             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
34201             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
34202             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
34203              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
34204                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
34205             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34206             "</li>"];
34207
34208         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34209             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34210                                 n.nextSibling.ui.getEl(), buf.join(""));
34211         }else{
34212             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34213         }
34214
34215         this.elNode = this.wrap.childNodes[0];
34216         this.ctNode = this.wrap.childNodes[1];
34217         var cs = this.elNode.childNodes;
34218         this.indentNode = cs[0];
34219         this.ecNode = cs[1];
34220         this.iconNode = cs[2];
34221         var index = 3;
34222         if(cb){
34223             this.checkbox = cs[3];
34224             index++;
34225         }
34226         this.anchor = cs[index];
34227         this.textNode = cs[index].firstChild;
34228     },
34229
34230     getAnchor : function(){
34231         return this.anchor;
34232     },
34233
34234     getTextEl : function(){
34235         return this.textNode;
34236     },
34237
34238     getIconEl : function(){
34239         return this.iconNode;
34240     },
34241
34242     isChecked : function(){
34243         return this.checkbox ? this.checkbox.checked : false;
34244     },
34245
34246     updateExpandIcon : function(){
34247         if(this.rendered){
34248             var n = this.node, c1, c2;
34249             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34250             var hasChild = n.hasChildNodes();
34251             if(hasChild){
34252                 if(n.expanded){
34253                     cls += "-minus";
34254                     c1 = "x-tree-node-collapsed";
34255                     c2 = "x-tree-node-expanded";
34256                 }else{
34257                     cls += "-plus";
34258                     c1 = "x-tree-node-expanded";
34259                     c2 = "x-tree-node-collapsed";
34260                 }
34261                 if(this.wasLeaf){
34262                     this.removeClass("x-tree-node-leaf");
34263                     this.wasLeaf = false;
34264                 }
34265                 if(this.c1 != c1 || this.c2 != c2){
34266                     Roo.fly(this.elNode).replaceClass(c1, c2);
34267                     this.c1 = c1; this.c2 = c2;
34268                 }
34269             }else{
34270                 // this changes non-leafs into leafs if they have no children.
34271                 // it's not very rational behaviour..
34272                 
34273                 if(!this.wasLeaf && this.node.leaf){
34274                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34275                     delete this.c1;
34276                     delete this.c2;
34277                     this.wasLeaf = true;
34278                 }
34279             }
34280             var ecc = "x-tree-ec-icon "+cls;
34281             if(this.ecc != ecc){
34282                 this.ecNode.className = ecc;
34283                 this.ecc = ecc;
34284             }
34285         }
34286     },
34287
34288     getChildIndent : function(){
34289         if(!this.childIndent){
34290             var buf = [];
34291             var p = this.node;
34292             while(p){
34293                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34294                     if(!p.isLast()) {
34295                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34296                     } else {
34297                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34298                     }
34299                 }
34300                 p = p.parentNode;
34301             }
34302             this.childIndent = buf.join("");
34303         }
34304         return this.childIndent;
34305     },
34306
34307     renderIndent : function(){
34308         if(this.rendered){
34309             var indent = "";
34310             var p = this.node.parentNode;
34311             if(p){
34312                 indent = p.ui.getChildIndent();
34313             }
34314             if(this.indentMarkup != indent){ // don't rerender if not required
34315                 this.indentNode.innerHTML = indent;
34316                 this.indentMarkup = indent;
34317             }
34318             this.updateExpandIcon();
34319         }
34320     }
34321 };
34322
34323 Roo.tree.RootTreeNodeUI = function(){
34324     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34325 };
34326 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34327     render : function(){
34328         if(!this.rendered){
34329             var targetNode = this.node.ownerTree.innerCt.dom;
34330             this.node.expanded = true;
34331             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34332             this.wrap = this.ctNode = targetNode.firstChild;
34333         }
34334     },
34335     collapse : function(){
34336     },
34337     expand : function(){
34338     }
34339 });/*
34340  * Based on:
34341  * Ext JS Library 1.1.1
34342  * Copyright(c) 2006-2007, Ext JS, LLC.
34343  *
34344  * Originally Released Under LGPL - original licence link has changed is not relivant.
34345  *
34346  * Fork - LGPL
34347  * <script type="text/javascript">
34348  */
34349 /**
34350  * @class Roo.tree.TreeLoader
34351  * @extends Roo.util.Observable
34352  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34353  * nodes from a specified URL. The response must be a javascript Array definition
34354  * who's elements are node definition objects. eg:
34355  * <pre><code>
34356 {  success : true,
34357    data :      [
34358    
34359     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34360     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34361     ]
34362 }
34363
34364
34365 </code></pre>
34366  * <br><br>
34367  * The old style respose with just an array is still supported, but not recommended.
34368  * <br><br>
34369  *
34370  * A server request is sent, and child nodes are loaded only when a node is expanded.
34371  * The loading node's id is passed to the server under the parameter name "node" to
34372  * enable the server to produce the correct child nodes.
34373  * <br><br>
34374  * To pass extra parameters, an event handler may be attached to the "beforeload"
34375  * event, and the parameters specified in the TreeLoader's baseParams property:
34376  * <pre><code>
34377     myTreeLoader.on("beforeload", function(treeLoader, node) {
34378         this.baseParams.category = node.attributes.category;
34379     }, this);
34380 </code></pre><
34381  * This would pass an HTTP parameter called "category" to the server containing
34382  * the value of the Node's "category" attribute.
34383  * @constructor
34384  * Creates a new Treeloader.
34385  * @param {Object} config A config object containing config properties.
34386  */
34387 Roo.tree.TreeLoader = function(config){
34388     this.baseParams = {};
34389     this.requestMethod = "POST";
34390     Roo.apply(this, config);
34391
34392     this.addEvents({
34393     
34394         /**
34395          * @event beforeload
34396          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34397          * @param {Object} This TreeLoader object.
34398          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34399          * @param {Object} callback The callback function specified in the {@link #load} call.
34400          */
34401         beforeload : true,
34402         /**
34403          * @event load
34404          * Fires when the node has been successfuly loaded.
34405          * @param {Object} This TreeLoader object.
34406          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34407          * @param {Object} response The response object containing the data from the server.
34408          */
34409         load : true,
34410         /**
34411          * @event loadexception
34412          * Fires if the network request failed.
34413          * @param {Object} This TreeLoader object.
34414          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34415          * @param {Object} response The response object containing the data from the server.
34416          */
34417         loadexception : true,
34418         /**
34419          * @event create
34420          * Fires before a node is created, enabling you to return custom Node types 
34421          * @param {Object} This TreeLoader object.
34422          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34423          */
34424         create : true
34425     });
34426
34427     Roo.tree.TreeLoader.superclass.constructor.call(this);
34428 };
34429
34430 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34431     /**
34432     * @cfg {String} dataUrl The URL from which to request a Json string which
34433     * specifies an array of node definition object representing the child nodes
34434     * to be loaded.
34435     */
34436     /**
34437     * @cfg {String} requestMethod either GET or POST
34438     * defaults to POST (due to BC)
34439     * to be loaded.
34440     */
34441     /**
34442     * @cfg {Object} baseParams (optional) An object containing properties which
34443     * specify HTTP parameters to be passed to each request for child nodes.
34444     */
34445     /**
34446     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34447     * created by this loader. If the attributes sent by the server have an attribute in this object,
34448     * they take priority.
34449     */
34450     /**
34451     * @cfg {Object} uiProviders (optional) An object containing properties which
34452     * 
34453     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34454     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34455     * <i>uiProvider</i> attribute of a returned child node is a string rather
34456     * than a reference to a TreeNodeUI implementation, this that string value
34457     * is used as a property name in the uiProviders object. You can define the provider named
34458     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34459     */
34460     uiProviders : {},
34461
34462     /**
34463     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34464     * child nodes before loading.
34465     */
34466     clearOnLoad : true,
34467
34468     /**
34469     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34470     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34471     * Grid query { data : [ .....] }
34472     */
34473     
34474     root : false,
34475      /**
34476     * @cfg {String} queryParam (optional) 
34477     * Name of the query as it will be passed on the querystring (defaults to 'node')
34478     * eg. the request will be ?node=[id]
34479     */
34480     
34481     
34482     queryParam: false,
34483     
34484     /**
34485      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34486      * This is called automatically when a node is expanded, but may be used to reload
34487      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34488      * @param {Roo.tree.TreeNode} node
34489      * @param {Function} callback
34490      */
34491     load : function(node, callback){
34492         if(this.clearOnLoad){
34493             while(node.firstChild){
34494                 node.removeChild(node.firstChild);
34495             }
34496         }
34497         if(node.attributes.children){ // preloaded json children
34498             var cs = node.attributes.children;
34499             for(var i = 0, len = cs.length; i < len; i++){
34500                 node.appendChild(this.createNode(cs[i]));
34501             }
34502             if(typeof callback == "function"){
34503                 callback();
34504             }
34505         }else if(this.dataUrl){
34506             this.requestData(node, callback);
34507         }
34508     },
34509
34510     getParams: function(node){
34511         var buf = [], bp = this.baseParams;
34512         for(var key in bp){
34513             if(typeof bp[key] != "function"){
34514                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34515             }
34516         }
34517         var n = this.queryParam === false ? 'node' : this.queryParam;
34518         buf.push(n + "=", encodeURIComponent(node.id));
34519         return buf.join("");
34520     },
34521
34522     requestData : function(node, callback){
34523         if(this.fireEvent("beforeload", this, node, callback) !== false){
34524             this.transId = Roo.Ajax.request({
34525                 method:this.requestMethod,
34526                 url: this.dataUrl||this.url,
34527                 success: this.handleResponse,
34528                 failure: this.handleFailure,
34529                 scope: this,
34530                 argument: {callback: callback, node: node},
34531                 params: this.getParams(node)
34532             });
34533         }else{
34534             // if the load is cancelled, make sure we notify
34535             // the node that we are done
34536             if(typeof callback == "function"){
34537                 callback();
34538             }
34539         }
34540     },
34541
34542     isLoading : function(){
34543         return this.transId ? true : false;
34544     },
34545
34546     abort : function(){
34547         if(this.isLoading()){
34548             Roo.Ajax.abort(this.transId);
34549         }
34550     },
34551
34552     // private
34553     createNode : function(attr)
34554     {
34555         // apply baseAttrs, nice idea Corey!
34556         if(this.baseAttrs){
34557             Roo.applyIf(attr, this.baseAttrs);
34558         }
34559         if(this.applyLoader !== false){
34560             attr.loader = this;
34561         }
34562         // uiProvider = depreciated..
34563         
34564         if(typeof(attr.uiProvider) == 'string'){
34565            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34566                 /**  eval:var:attr */ eval(attr.uiProvider);
34567         }
34568         if(typeof(this.uiProviders['default']) != 'undefined') {
34569             attr.uiProvider = this.uiProviders['default'];
34570         }
34571         
34572         this.fireEvent('create', this, attr);
34573         
34574         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34575         return(attr.leaf ?
34576                         new Roo.tree.TreeNode(attr) :
34577                         new Roo.tree.AsyncTreeNode(attr));
34578     },
34579
34580     processResponse : function(response, node, callback)
34581     {
34582         var json = response.responseText;
34583         try {
34584             
34585             var o = Roo.decode(json);
34586             
34587             if (this.root === false && typeof(o.success) != undefined) {
34588                 this.root = 'data'; // the default behaviour for list like data..
34589                 }
34590                 
34591             if (this.root !== false &&  !o.success) {
34592                 // it's a failure condition.
34593                 var a = response.argument;
34594                 this.fireEvent("loadexception", this, a.node, response);
34595                 Roo.log("Load failed - should have a handler really");
34596                 return;
34597             }
34598             
34599             
34600             
34601             if (this.root !== false) {
34602                  o = o[this.root];
34603             }
34604             
34605             for(var i = 0, len = o.length; i < len; i++){
34606                 var n = this.createNode(o[i]);
34607                 if(n){
34608                     node.appendChild(n);
34609                 }
34610             }
34611             if(typeof callback == "function"){
34612                 callback(this, node);
34613             }
34614         }catch(e){
34615             this.handleFailure(response);
34616         }
34617     },
34618
34619     handleResponse : function(response){
34620         this.transId = false;
34621         var a = response.argument;
34622         this.processResponse(response, a.node, a.callback);
34623         this.fireEvent("load", this, a.node, response);
34624     },
34625
34626     handleFailure : function(response)
34627     {
34628         // should handle failure better..
34629         this.transId = false;
34630         var a = response.argument;
34631         this.fireEvent("loadexception", this, a.node, response);
34632         if(typeof a.callback == "function"){
34633             a.callback(this, a.node);
34634         }
34635     }
34636 });/*
34637  * Based on:
34638  * Ext JS Library 1.1.1
34639  * Copyright(c) 2006-2007, Ext JS, LLC.
34640  *
34641  * Originally Released Under LGPL - original licence link has changed is not relivant.
34642  *
34643  * Fork - LGPL
34644  * <script type="text/javascript">
34645  */
34646
34647 /**
34648 * @class Roo.tree.TreeFilter
34649 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34650 * @param {TreePanel} tree
34651 * @param {Object} config (optional)
34652  */
34653 Roo.tree.TreeFilter = function(tree, config){
34654     this.tree = tree;
34655     this.filtered = {};
34656     Roo.apply(this, config);
34657 };
34658
34659 Roo.tree.TreeFilter.prototype = {
34660     clearBlank:false,
34661     reverse:false,
34662     autoClear:false,
34663     remove:false,
34664
34665      /**
34666      * Filter the data by a specific attribute.
34667      * @param {String/RegExp} value Either string that the attribute value
34668      * should start with or a RegExp to test against the attribute
34669      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34670      * @param {TreeNode} startNode (optional) The node to start the filter at.
34671      */
34672     filter : function(value, attr, startNode){
34673         attr = attr || "text";
34674         var f;
34675         if(typeof value == "string"){
34676             var vlen = value.length;
34677             // auto clear empty filter
34678             if(vlen == 0 && this.clearBlank){
34679                 this.clear();
34680                 return;
34681             }
34682             value = value.toLowerCase();
34683             f = function(n){
34684                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34685             };
34686         }else if(value.exec){ // regex?
34687             f = function(n){
34688                 return value.test(n.attributes[attr]);
34689             };
34690         }else{
34691             throw 'Illegal filter type, must be string or regex';
34692         }
34693         this.filterBy(f, null, startNode);
34694         },
34695
34696     /**
34697      * Filter by a function. The passed function will be called with each
34698      * node in the tree (or from the startNode). If the function returns true, the node is kept
34699      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34700      * @param {Function} fn The filter function
34701      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34702      */
34703     filterBy : function(fn, scope, startNode){
34704         startNode = startNode || this.tree.root;
34705         if(this.autoClear){
34706             this.clear();
34707         }
34708         var af = this.filtered, rv = this.reverse;
34709         var f = function(n){
34710             if(n == startNode){
34711                 return true;
34712             }
34713             if(af[n.id]){
34714                 return false;
34715             }
34716             var m = fn.call(scope || n, n);
34717             if(!m || rv){
34718                 af[n.id] = n;
34719                 n.ui.hide();
34720                 return false;
34721             }
34722             return true;
34723         };
34724         startNode.cascade(f);
34725         if(this.remove){
34726            for(var id in af){
34727                if(typeof id != "function"){
34728                    var n = af[id];
34729                    if(n && n.parentNode){
34730                        n.parentNode.removeChild(n);
34731                    }
34732                }
34733            }
34734         }
34735     },
34736
34737     /**
34738      * Clears the current filter. Note: with the "remove" option
34739      * set a filter cannot be cleared.
34740      */
34741     clear : function(){
34742         var t = this.tree;
34743         var af = this.filtered;
34744         for(var id in af){
34745             if(typeof id != "function"){
34746                 var n = af[id];
34747                 if(n){
34748                     n.ui.show();
34749                 }
34750             }
34751         }
34752         this.filtered = {};
34753     }
34754 };
34755 /*
34756  * Based on:
34757  * Ext JS Library 1.1.1
34758  * Copyright(c) 2006-2007, Ext JS, LLC.
34759  *
34760  * Originally Released Under LGPL - original licence link has changed is not relivant.
34761  *
34762  * Fork - LGPL
34763  * <script type="text/javascript">
34764  */
34765  
34766
34767 /**
34768  * @class Roo.tree.TreeSorter
34769  * Provides sorting of nodes in a TreePanel
34770  * 
34771  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34772  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34773  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34774  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34775  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34776  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34777  * @constructor
34778  * @param {TreePanel} tree
34779  * @param {Object} config
34780  */
34781 Roo.tree.TreeSorter = function(tree, config){
34782     Roo.apply(this, config);
34783     tree.on("beforechildrenrendered", this.doSort, this);
34784     tree.on("append", this.updateSort, this);
34785     tree.on("insert", this.updateSort, this);
34786     
34787     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34788     var p = this.property || "text";
34789     var sortType = this.sortType;
34790     var fs = this.folderSort;
34791     var cs = this.caseSensitive === true;
34792     var leafAttr = this.leafAttr || 'leaf';
34793
34794     this.sortFn = function(n1, n2){
34795         if(fs){
34796             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34797                 return 1;
34798             }
34799             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34800                 return -1;
34801             }
34802         }
34803         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34804         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34805         if(v1 < v2){
34806                         return dsc ? +1 : -1;
34807                 }else if(v1 > v2){
34808                         return dsc ? -1 : +1;
34809         }else{
34810                 return 0;
34811         }
34812     };
34813 };
34814
34815 Roo.tree.TreeSorter.prototype = {
34816     doSort : function(node){
34817         node.sort(this.sortFn);
34818     },
34819     
34820     compareNodes : function(n1, n2){
34821         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34822     },
34823     
34824     updateSort : function(tree, node){
34825         if(node.childrenRendered){
34826             this.doSort.defer(1, this, [node]);
34827         }
34828     }
34829 };/*
34830  * Based on:
34831  * Ext JS Library 1.1.1
34832  * Copyright(c) 2006-2007, Ext JS, LLC.
34833  *
34834  * Originally Released Under LGPL - original licence link has changed is not relivant.
34835  *
34836  * Fork - LGPL
34837  * <script type="text/javascript">
34838  */
34839
34840 if(Roo.dd.DropZone){
34841     
34842 Roo.tree.TreeDropZone = function(tree, config){
34843     this.allowParentInsert = false;
34844     this.allowContainerDrop = false;
34845     this.appendOnly = false;
34846     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34847     this.tree = tree;
34848     this.lastInsertClass = "x-tree-no-status";
34849     this.dragOverData = {};
34850 };
34851
34852 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34853     ddGroup : "TreeDD",
34854     scroll:  true,
34855     
34856     expandDelay : 1000,
34857     
34858     expandNode : function(node){
34859         if(node.hasChildNodes() && !node.isExpanded()){
34860             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34861         }
34862     },
34863     
34864     queueExpand : function(node){
34865         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34866     },
34867     
34868     cancelExpand : function(){
34869         if(this.expandProcId){
34870             clearTimeout(this.expandProcId);
34871             this.expandProcId = false;
34872         }
34873     },
34874     
34875     isValidDropPoint : function(n, pt, dd, e, data){
34876         if(!n || !data){ return false; }
34877         var targetNode = n.node;
34878         var dropNode = data.node;
34879         // default drop rules
34880         if(!(targetNode && targetNode.isTarget && pt)){
34881             return false;
34882         }
34883         if(pt == "append" && targetNode.allowChildren === false){
34884             return false;
34885         }
34886         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34887             return false;
34888         }
34889         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34890             return false;
34891         }
34892         // reuse the object
34893         var overEvent = this.dragOverData;
34894         overEvent.tree = this.tree;
34895         overEvent.target = targetNode;
34896         overEvent.data = data;
34897         overEvent.point = pt;
34898         overEvent.source = dd;
34899         overEvent.rawEvent = e;
34900         overEvent.dropNode = dropNode;
34901         overEvent.cancel = false;  
34902         var result = this.tree.fireEvent("nodedragover", overEvent);
34903         return overEvent.cancel === false && result !== false;
34904     },
34905     
34906     getDropPoint : function(e, n, dd)
34907     {
34908         var tn = n.node;
34909         if(tn.isRoot){
34910             return tn.allowChildren !== false ? "append" : false; // always append for root
34911         }
34912         var dragEl = n.ddel;
34913         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34914         var y = Roo.lib.Event.getPageY(e);
34915         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34916         
34917         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34918         var noAppend = tn.allowChildren === false;
34919         if(this.appendOnly || tn.parentNode.allowChildren === false){
34920             return noAppend ? false : "append";
34921         }
34922         var noBelow = false;
34923         if(!this.allowParentInsert){
34924             noBelow = tn.hasChildNodes() && tn.isExpanded();
34925         }
34926         var q = (b - t) / (noAppend ? 2 : 3);
34927         if(y >= t && y < (t + q)){
34928             return "above";
34929         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34930             return "below";
34931         }else{
34932             return "append";
34933         }
34934     },
34935     
34936     onNodeEnter : function(n, dd, e, data)
34937     {
34938         this.cancelExpand();
34939     },
34940     
34941     onNodeOver : function(n, dd, e, data)
34942     {
34943        
34944         var pt = this.getDropPoint(e, n, dd);
34945         var node = n.node;
34946         
34947         // auto node expand check
34948         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34949             this.queueExpand(node);
34950         }else if(pt != "append"){
34951             this.cancelExpand();
34952         }
34953         
34954         // set the insert point style on the target node
34955         var returnCls = this.dropNotAllowed;
34956         if(this.isValidDropPoint(n, pt, dd, e, data)){
34957            if(pt){
34958                var el = n.ddel;
34959                var cls;
34960                if(pt == "above"){
34961                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34962                    cls = "x-tree-drag-insert-above";
34963                }else if(pt == "below"){
34964                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34965                    cls = "x-tree-drag-insert-below";
34966                }else{
34967                    returnCls = "x-tree-drop-ok-append";
34968                    cls = "x-tree-drag-append";
34969                }
34970                if(this.lastInsertClass != cls){
34971                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34972                    this.lastInsertClass = cls;
34973                }
34974            }
34975        }
34976        return returnCls;
34977     },
34978     
34979     onNodeOut : function(n, dd, e, data){
34980         
34981         this.cancelExpand();
34982         this.removeDropIndicators(n);
34983     },
34984     
34985     onNodeDrop : function(n, dd, e, data){
34986         var point = this.getDropPoint(e, n, dd);
34987         var targetNode = n.node;
34988         targetNode.ui.startDrop();
34989         if(!this.isValidDropPoint(n, point, dd, e, data)){
34990             targetNode.ui.endDrop();
34991             return false;
34992         }
34993         // first try to find the drop node
34994         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34995         var dropEvent = {
34996             tree : this.tree,
34997             target: targetNode,
34998             data: data,
34999             point: point,
35000             source: dd,
35001             rawEvent: e,
35002             dropNode: dropNode,
35003             cancel: !dropNode   
35004         };
35005         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
35006         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
35007             targetNode.ui.endDrop();
35008             return false;
35009         }
35010         // allow target changing
35011         targetNode = dropEvent.target;
35012         if(point == "append" && !targetNode.isExpanded()){
35013             targetNode.expand(false, null, function(){
35014                 this.completeDrop(dropEvent);
35015             }.createDelegate(this));
35016         }else{
35017             this.completeDrop(dropEvent);
35018         }
35019         return true;
35020     },
35021     
35022     completeDrop : function(de){
35023         var ns = de.dropNode, p = de.point, t = de.target;
35024         if(!(ns instanceof Array)){
35025             ns = [ns];
35026         }
35027         var n;
35028         for(var i = 0, len = ns.length; i < len; i++){
35029             n = ns[i];
35030             if(p == "above"){
35031                 t.parentNode.insertBefore(n, t);
35032             }else if(p == "below"){
35033                 t.parentNode.insertBefore(n, t.nextSibling);
35034             }else{
35035                 t.appendChild(n);
35036             }
35037         }
35038         n.ui.focus();
35039         if(this.tree.hlDrop){
35040             n.ui.highlight();
35041         }
35042         t.ui.endDrop();
35043         this.tree.fireEvent("nodedrop", de);
35044     },
35045     
35046     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
35047         if(this.tree.hlDrop){
35048             dropNode.ui.focus();
35049             dropNode.ui.highlight();
35050         }
35051         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
35052     },
35053     
35054     getTree : function(){
35055         return this.tree;
35056     },
35057     
35058     removeDropIndicators : function(n){
35059         if(n && n.ddel){
35060             var el = n.ddel;
35061             Roo.fly(el).removeClass([
35062                     "x-tree-drag-insert-above",
35063                     "x-tree-drag-insert-below",
35064                     "x-tree-drag-append"]);
35065             this.lastInsertClass = "_noclass";
35066         }
35067     },
35068     
35069     beforeDragDrop : function(target, e, id){
35070         this.cancelExpand();
35071         return true;
35072     },
35073     
35074     afterRepair : function(data){
35075         if(data && Roo.enableFx){
35076             data.node.ui.highlight();
35077         }
35078         this.hideProxy();
35079     } 
35080     
35081 });
35082
35083 }
35084 /*
35085  * Based on:
35086  * Ext JS Library 1.1.1
35087  * Copyright(c) 2006-2007, Ext JS, LLC.
35088  *
35089  * Originally Released Under LGPL - original licence link has changed is not relivant.
35090  *
35091  * Fork - LGPL
35092  * <script type="text/javascript">
35093  */
35094  
35095
35096 if(Roo.dd.DragZone){
35097 Roo.tree.TreeDragZone = function(tree, config){
35098     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
35099     this.tree = tree;
35100 };
35101
35102 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
35103     ddGroup : "TreeDD",
35104    
35105     onBeforeDrag : function(data, e){
35106         var n = data.node;
35107         return n && n.draggable && !n.disabled;
35108     },
35109      
35110     
35111     onInitDrag : function(e){
35112         var data = this.dragData;
35113         this.tree.getSelectionModel().select(data.node);
35114         this.proxy.update("");
35115         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
35116         this.tree.fireEvent("startdrag", this.tree, data.node, e);
35117     },
35118     
35119     getRepairXY : function(e, data){
35120         return data.node.ui.getDDRepairXY();
35121     },
35122     
35123     onEndDrag : function(data, e){
35124         this.tree.fireEvent("enddrag", this.tree, data.node, e);
35125         
35126         
35127     },
35128     
35129     onValidDrop : function(dd, e, id){
35130         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
35131         this.hideProxy();
35132     },
35133     
35134     beforeInvalidDrop : function(e, id){
35135         // this scrolls the original position back into view
35136         var sm = this.tree.getSelectionModel();
35137         sm.clearSelections();
35138         sm.select(this.dragData.node);
35139     }
35140 });
35141 }/*
35142  * Based on:
35143  * Ext JS Library 1.1.1
35144  * Copyright(c) 2006-2007, Ext JS, LLC.
35145  *
35146  * Originally Released Under LGPL - original licence link has changed is not relivant.
35147  *
35148  * Fork - LGPL
35149  * <script type="text/javascript">
35150  */
35151 /**
35152  * @class Roo.tree.TreeEditor
35153  * @extends Roo.Editor
35154  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
35155  * as the editor field.
35156  * @constructor
35157  * @param {Object} config (used to be the tree panel.)
35158  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
35159  * 
35160  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
35161  * @cfg {Roo.form.TextField|Object} field The field configuration
35162  *
35163  * 
35164  */
35165 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
35166     var tree = config;
35167     var field;
35168     if (oldconfig) { // old style..
35169         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
35170     } else {
35171         // new style..
35172         tree = config.tree;
35173         config.field = config.field  || {};
35174         config.field.xtype = 'TextField';
35175         field = Roo.factory(config.field, Roo.form);
35176     }
35177     config = config || {};
35178     
35179     
35180     this.addEvents({
35181         /**
35182          * @event beforenodeedit
35183          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35184          * false from the handler of this event.
35185          * @param {Editor} this
35186          * @param {Roo.tree.Node} node 
35187          */
35188         "beforenodeedit" : true
35189     });
35190     
35191     //Roo.log(config);
35192     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
35193
35194     this.tree = tree;
35195
35196     tree.on('beforeclick', this.beforeNodeClick, this);
35197     tree.getTreeEl().on('mousedown', this.hide, this);
35198     this.on('complete', this.updateNode, this);
35199     this.on('beforestartedit', this.fitToTree, this);
35200     this.on('startedit', this.bindScroll, this, {delay:10});
35201     this.on('specialkey', this.onSpecialKey, this);
35202 };
35203
35204 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
35205     /**
35206      * @cfg {String} alignment
35207      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
35208      */
35209     alignment: "l-l",
35210     // inherit
35211     autoSize: false,
35212     /**
35213      * @cfg {Boolean} hideEl
35214      * True to hide the bound element while the editor is displayed (defaults to false)
35215      */
35216     hideEl : false,
35217     /**
35218      * @cfg {String} cls
35219      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35220      */
35221     cls: "x-small-editor x-tree-editor",
35222     /**
35223      * @cfg {Boolean} shim
35224      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35225      */
35226     shim:false,
35227     // inherit
35228     shadow:"frame",
35229     /**
35230      * @cfg {Number} maxWidth
35231      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35232      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35233      * scroll and client offsets into account prior to each edit.
35234      */
35235     maxWidth: 250,
35236
35237     editDelay : 350,
35238
35239     // private
35240     fitToTree : function(ed, el){
35241         var td = this.tree.getTreeEl().dom, nd = el.dom;
35242         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35243             td.scrollLeft = nd.offsetLeft;
35244         }
35245         var w = Math.min(
35246                 this.maxWidth,
35247                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35248         this.setSize(w, '');
35249         
35250         return this.fireEvent('beforenodeedit', this, this.editNode);
35251         
35252     },
35253
35254     // private
35255     triggerEdit : function(node){
35256         this.completeEdit();
35257         this.editNode = node;
35258         this.startEdit(node.ui.textNode, node.text);
35259     },
35260
35261     // private
35262     bindScroll : function(){
35263         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35264     },
35265
35266     // private
35267     beforeNodeClick : function(node, e){
35268         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35269         this.lastClick = new Date();
35270         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35271             e.stopEvent();
35272             this.triggerEdit(node);
35273             return false;
35274         }
35275         return true;
35276     },
35277
35278     // private
35279     updateNode : function(ed, value){
35280         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35281         this.editNode.setText(value);
35282     },
35283
35284     // private
35285     onHide : function(){
35286         Roo.tree.TreeEditor.superclass.onHide.call(this);
35287         if(this.editNode){
35288             this.editNode.ui.focus();
35289         }
35290     },
35291
35292     // private
35293     onSpecialKey : function(field, e){
35294         var k = e.getKey();
35295         if(k == e.ESC){
35296             e.stopEvent();
35297             this.cancelEdit();
35298         }else if(k == e.ENTER && !e.hasModifier()){
35299             e.stopEvent();
35300             this.completeEdit();
35301         }
35302     }
35303 });//<Script type="text/javascript">
35304 /*
35305  * Based on:
35306  * Ext JS Library 1.1.1
35307  * Copyright(c) 2006-2007, Ext JS, LLC.
35308  *
35309  * Originally Released Under LGPL - original licence link has changed is not relivant.
35310  *
35311  * Fork - LGPL
35312  * <script type="text/javascript">
35313  */
35314  
35315 /**
35316  * Not documented??? - probably should be...
35317  */
35318
35319 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35320     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35321     
35322     renderElements : function(n, a, targetNode, bulkRender){
35323         //consel.log("renderElements?");
35324         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35325
35326         var t = n.getOwnerTree();
35327         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35328         
35329         var cols = t.columns;
35330         var bw = t.borderWidth;
35331         var c = cols[0];
35332         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35333          var cb = typeof a.checked == "boolean";
35334         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35335         var colcls = 'x-t-' + tid + '-c0';
35336         var buf = [
35337             '<li class="x-tree-node">',
35338             
35339                 
35340                 '<div class="x-tree-node-el ', a.cls,'">',
35341                     // extran...
35342                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35343                 
35344                 
35345                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35346                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35347                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35348                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35349                            (a.iconCls ? ' '+a.iconCls : ''),
35350                            '" unselectable="on" />',
35351                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35352                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35353                              
35354                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35355                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35356                             '<span unselectable="on" qtip="' + tx + '">',
35357                              tx,
35358                              '</span></a>' ,
35359                     '</div>',
35360                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35361                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35362                  ];
35363         for(var i = 1, len = cols.length; i < len; i++){
35364             c = cols[i];
35365             colcls = 'x-t-' + tid + '-c' +i;
35366             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35367             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35368                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35369                       "</div>");
35370          }
35371          
35372          buf.push(
35373             '</a>',
35374             '<div class="x-clear"></div></div>',
35375             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35376             "</li>");
35377         
35378         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35379             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35380                                 n.nextSibling.ui.getEl(), buf.join(""));
35381         }else{
35382             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35383         }
35384         var el = this.wrap.firstChild;
35385         this.elRow = el;
35386         this.elNode = el.firstChild;
35387         this.ranchor = el.childNodes[1];
35388         this.ctNode = this.wrap.childNodes[1];
35389         var cs = el.firstChild.childNodes;
35390         this.indentNode = cs[0];
35391         this.ecNode = cs[1];
35392         this.iconNode = cs[2];
35393         var index = 3;
35394         if(cb){
35395             this.checkbox = cs[3];
35396             index++;
35397         }
35398         this.anchor = cs[index];
35399         
35400         this.textNode = cs[index].firstChild;
35401         
35402         //el.on("click", this.onClick, this);
35403         //el.on("dblclick", this.onDblClick, this);
35404         
35405         
35406        // console.log(this);
35407     },
35408     initEvents : function(){
35409         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35410         
35411             
35412         var a = this.ranchor;
35413
35414         var el = Roo.get(a);
35415
35416         if(Roo.isOpera){ // opera render bug ignores the CSS
35417             el.setStyle("text-decoration", "none");
35418         }
35419
35420         el.on("click", this.onClick, this);
35421         el.on("dblclick", this.onDblClick, this);
35422         el.on("contextmenu", this.onContextMenu, this);
35423         
35424     },
35425     
35426     /*onSelectedChange : function(state){
35427         if(state){
35428             this.focus();
35429             this.addClass("x-tree-selected");
35430         }else{
35431             //this.blur();
35432             this.removeClass("x-tree-selected");
35433         }
35434     },*/
35435     addClass : function(cls){
35436         if(this.elRow){
35437             Roo.fly(this.elRow).addClass(cls);
35438         }
35439         
35440     },
35441     
35442     
35443     removeClass : function(cls){
35444         if(this.elRow){
35445             Roo.fly(this.elRow).removeClass(cls);
35446         }
35447     }
35448
35449     
35450     
35451 });//<Script type="text/javascript">
35452
35453 /*
35454  * Based on:
35455  * Ext JS Library 1.1.1
35456  * Copyright(c) 2006-2007, Ext JS, LLC.
35457  *
35458  * Originally Released Under LGPL - original licence link has changed is not relivant.
35459  *
35460  * Fork - LGPL
35461  * <script type="text/javascript">
35462  */
35463  
35464
35465 /**
35466  * @class Roo.tree.ColumnTree
35467  * @extends Roo.data.TreePanel
35468  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35469  * @cfg {int} borderWidth  compined right/left border allowance
35470  * @constructor
35471  * @param {String/HTMLElement/Element} el The container element
35472  * @param {Object} config
35473  */
35474 Roo.tree.ColumnTree =  function(el, config)
35475 {
35476    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35477    this.addEvents({
35478         /**
35479         * @event resize
35480         * Fire this event on a container when it resizes
35481         * @param {int} w Width
35482         * @param {int} h Height
35483         */
35484        "resize" : true
35485     });
35486     this.on('resize', this.onResize, this);
35487 };
35488
35489 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35490     //lines:false,
35491     
35492     
35493     borderWidth: Roo.isBorderBox ? 0 : 2, 
35494     headEls : false,
35495     
35496     render : function(){
35497         // add the header.....
35498        
35499         Roo.tree.ColumnTree.superclass.render.apply(this);
35500         
35501         this.el.addClass('x-column-tree');
35502         
35503         this.headers = this.el.createChild(
35504             {cls:'x-tree-headers'},this.innerCt.dom);
35505    
35506         var cols = this.columns, c;
35507         var totalWidth = 0;
35508         this.headEls = [];
35509         var  len = cols.length;
35510         for(var i = 0; i < len; i++){
35511              c = cols[i];
35512              totalWidth += c.width;
35513             this.headEls.push(this.headers.createChild({
35514                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35515                  cn: {
35516                      cls:'x-tree-hd-text',
35517                      html: c.header
35518                  },
35519                  style:'width:'+(c.width-this.borderWidth)+'px;'
35520              }));
35521         }
35522         this.headers.createChild({cls:'x-clear'});
35523         // prevent floats from wrapping when clipped
35524         this.headers.setWidth(totalWidth);
35525         //this.innerCt.setWidth(totalWidth);
35526         this.innerCt.setStyle({ overflow: 'auto' });
35527         this.onResize(this.width, this.height);
35528              
35529         
35530     },
35531     onResize : function(w,h)
35532     {
35533         this.height = h;
35534         this.width = w;
35535         // resize cols..
35536         this.innerCt.setWidth(this.width);
35537         this.innerCt.setHeight(this.height-20);
35538         
35539         // headers...
35540         var cols = this.columns, c;
35541         var totalWidth = 0;
35542         var expEl = false;
35543         var len = cols.length;
35544         for(var i = 0; i < len; i++){
35545             c = cols[i];
35546             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35547                 // it's the expander..
35548                 expEl  = this.headEls[i];
35549                 continue;
35550             }
35551             totalWidth += c.width;
35552             
35553         }
35554         if (expEl) {
35555             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35556         }
35557         this.headers.setWidth(w-20);
35558
35559         
35560         
35561         
35562     }
35563 });
35564 /*
35565  * Based on:
35566  * Ext JS Library 1.1.1
35567  * Copyright(c) 2006-2007, Ext JS, LLC.
35568  *
35569  * Originally Released Under LGPL - original licence link has changed is not relivant.
35570  *
35571  * Fork - LGPL
35572  * <script type="text/javascript">
35573  */
35574  
35575 /**
35576  * @class Roo.menu.Menu
35577  * @extends Roo.util.Observable
35578  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35579  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35580  * @constructor
35581  * Creates a new Menu
35582  * @param {Object} config Configuration options
35583  */
35584 Roo.menu.Menu = function(config){
35585     Roo.apply(this, config);
35586     this.id = this.id || Roo.id();
35587     this.addEvents({
35588         /**
35589          * @event beforeshow
35590          * Fires before this menu is displayed
35591          * @param {Roo.menu.Menu} this
35592          */
35593         beforeshow : true,
35594         /**
35595          * @event beforehide
35596          * Fires before this menu is hidden
35597          * @param {Roo.menu.Menu} this
35598          */
35599         beforehide : true,
35600         /**
35601          * @event show
35602          * Fires after this menu is displayed
35603          * @param {Roo.menu.Menu} this
35604          */
35605         show : true,
35606         /**
35607          * @event hide
35608          * Fires after this menu is hidden
35609          * @param {Roo.menu.Menu} this
35610          */
35611         hide : true,
35612         /**
35613          * @event click
35614          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35615          * @param {Roo.menu.Menu} this
35616          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35617          * @param {Roo.EventObject} e
35618          */
35619         click : true,
35620         /**
35621          * @event mouseover
35622          * Fires when the mouse is hovering over this menu
35623          * @param {Roo.menu.Menu} this
35624          * @param {Roo.EventObject} e
35625          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35626          */
35627         mouseover : true,
35628         /**
35629          * @event mouseout
35630          * Fires when the mouse exits this menu
35631          * @param {Roo.menu.Menu} this
35632          * @param {Roo.EventObject} e
35633          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35634          */
35635         mouseout : true,
35636         /**
35637          * @event itemclick
35638          * Fires when a menu item contained in this menu is clicked
35639          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35640          * @param {Roo.EventObject} e
35641          */
35642         itemclick: true
35643     });
35644     if (this.registerMenu) {
35645         Roo.menu.MenuMgr.register(this);
35646     }
35647     
35648     var mis = this.items;
35649     this.items = new Roo.util.MixedCollection();
35650     if(mis){
35651         this.add.apply(this, mis);
35652     }
35653 };
35654
35655 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35656     /**
35657      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35658      */
35659     minWidth : 120,
35660     /**
35661      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35662      * for bottom-right shadow (defaults to "sides")
35663      */
35664     shadow : "sides",
35665     /**
35666      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35667      * this menu (defaults to "tl-tr?")
35668      */
35669     subMenuAlign : "tl-tr?",
35670     /**
35671      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35672      * relative to its element of origin (defaults to "tl-bl?")
35673      */
35674     defaultAlign : "tl-bl?",
35675     /**
35676      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35677      */
35678     allowOtherMenus : false,
35679     /**
35680      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35681      */
35682     registerMenu : true,
35683
35684     hidden:true,
35685
35686     // private
35687     render : function(){
35688         if(this.el){
35689             return;
35690         }
35691         var el = this.el = new Roo.Layer({
35692             cls: "x-menu",
35693             shadow:this.shadow,
35694             constrain: false,
35695             parentEl: this.parentEl || document.body,
35696             zindex:15000
35697         });
35698
35699         this.keyNav = new Roo.menu.MenuNav(this);
35700
35701         if(this.plain){
35702             el.addClass("x-menu-plain");
35703         }
35704         if(this.cls){
35705             el.addClass(this.cls);
35706         }
35707         // generic focus element
35708         this.focusEl = el.createChild({
35709             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35710         });
35711         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35712         //disabling touch- as it's causing issues ..
35713         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35714         ul.on('click'   , this.onClick, this);
35715         
35716         
35717         ul.on("mouseover", this.onMouseOver, this);
35718         ul.on("mouseout", this.onMouseOut, this);
35719         this.items.each(function(item){
35720             if (item.hidden) {
35721                 return;
35722             }
35723             
35724             var li = document.createElement("li");
35725             li.className = "x-menu-list-item";
35726             ul.dom.appendChild(li);
35727             item.render(li, this);
35728         }, this);
35729         this.ul = ul;
35730         this.autoWidth();
35731     },
35732
35733     // private
35734     autoWidth : function(){
35735         var el = this.el, ul = this.ul;
35736         if(!el){
35737             return;
35738         }
35739         var w = this.width;
35740         if(w){
35741             el.setWidth(w);
35742         }else if(Roo.isIE){
35743             el.setWidth(this.minWidth);
35744             var t = el.dom.offsetWidth; // force recalc
35745             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35746         }
35747     },
35748
35749     // private
35750     delayAutoWidth : function(){
35751         if(this.rendered){
35752             if(!this.awTask){
35753                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35754             }
35755             this.awTask.delay(20);
35756         }
35757     },
35758
35759     // private
35760     findTargetItem : function(e){
35761         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35762         if(t && t.menuItemId){
35763             return this.items.get(t.menuItemId);
35764         }
35765     },
35766
35767     // private
35768     onClick : function(e){
35769         Roo.log("menu.onClick");
35770         var t = this.findTargetItem(e);
35771         if(!t){
35772             return;
35773         }
35774         Roo.log(e);
35775         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35776             if(t == this.activeItem && t.shouldDeactivate(e)){
35777                 this.activeItem.deactivate();
35778                 delete this.activeItem;
35779                 return;
35780             }
35781             if(t.canActivate){
35782                 this.setActiveItem(t, true);
35783             }
35784             return;
35785             
35786             
35787         }
35788         
35789         t.onClick(e);
35790         this.fireEvent("click", this, t, e);
35791     },
35792
35793     // private
35794     setActiveItem : function(item, autoExpand){
35795         if(item != this.activeItem){
35796             if(this.activeItem){
35797                 this.activeItem.deactivate();
35798             }
35799             this.activeItem = item;
35800             item.activate(autoExpand);
35801         }else if(autoExpand){
35802             item.expandMenu();
35803         }
35804     },
35805
35806     // private
35807     tryActivate : function(start, step){
35808         var items = this.items;
35809         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35810             var item = items.get(i);
35811             if(!item.disabled && item.canActivate){
35812                 this.setActiveItem(item, false);
35813                 return item;
35814             }
35815         }
35816         return false;
35817     },
35818
35819     // private
35820     onMouseOver : function(e){
35821         var t;
35822         if(t = this.findTargetItem(e)){
35823             if(t.canActivate && !t.disabled){
35824                 this.setActiveItem(t, true);
35825             }
35826         }
35827         this.fireEvent("mouseover", this, e, t);
35828     },
35829
35830     // private
35831     onMouseOut : function(e){
35832         var t;
35833         if(t = this.findTargetItem(e)){
35834             if(t == this.activeItem && t.shouldDeactivate(e)){
35835                 this.activeItem.deactivate();
35836                 delete this.activeItem;
35837             }
35838         }
35839         this.fireEvent("mouseout", this, e, t);
35840     },
35841
35842     /**
35843      * Read-only.  Returns true if the menu is currently displayed, else false.
35844      * @type Boolean
35845      */
35846     isVisible : function(){
35847         return this.el && !this.hidden;
35848     },
35849
35850     /**
35851      * Displays this menu relative to another element
35852      * @param {String/HTMLElement/Roo.Element} element The element to align to
35853      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35854      * the element (defaults to this.defaultAlign)
35855      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35856      */
35857     show : function(el, pos, parentMenu){
35858         this.parentMenu = parentMenu;
35859         if(!this.el){
35860             this.render();
35861         }
35862         this.fireEvent("beforeshow", this);
35863         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35864     },
35865
35866     /**
35867      * Displays this menu at a specific xy position
35868      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35869      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35870      */
35871     showAt : function(xy, parentMenu, /* private: */_e){
35872         this.parentMenu = parentMenu;
35873         if(!this.el){
35874             this.render();
35875         }
35876         if(_e !== false){
35877             this.fireEvent("beforeshow", this);
35878             xy = this.el.adjustForConstraints(xy);
35879         }
35880         this.el.setXY(xy);
35881         this.el.show();
35882         this.hidden = false;
35883         this.focus();
35884         this.fireEvent("show", this);
35885     },
35886
35887     focus : function(){
35888         if(!this.hidden){
35889             this.doFocus.defer(50, this);
35890         }
35891     },
35892
35893     doFocus : function(){
35894         if(!this.hidden){
35895             this.focusEl.focus();
35896         }
35897     },
35898
35899     /**
35900      * Hides this menu and optionally all parent menus
35901      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35902      */
35903     hide : function(deep){
35904         if(this.el && this.isVisible()){
35905             this.fireEvent("beforehide", this);
35906             if(this.activeItem){
35907                 this.activeItem.deactivate();
35908                 this.activeItem = null;
35909             }
35910             this.el.hide();
35911             this.hidden = true;
35912             this.fireEvent("hide", this);
35913         }
35914         if(deep === true && this.parentMenu){
35915             this.parentMenu.hide(true);
35916         }
35917     },
35918
35919     /**
35920      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35921      * Any of the following are valid:
35922      * <ul>
35923      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35924      * <li>An HTMLElement object which will be converted to a menu item</li>
35925      * <li>A menu item config object that will be created as a new menu item</li>
35926      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35927      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35928      * </ul>
35929      * Usage:
35930      * <pre><code>
35931 // Create the menu
35932 var menu = new Roo.menu.Menu();
35933
35934 // Create a menu item to add by reference
35935 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35936
35937 // Add a bunch of items at once using different methods.
35938 // Only the last item added will be returned.
35939 var item = menu.add(
35940     menuItem,                // add existing item by ref
35941     'Dynamic Item',          // new TextItem
35942     '-',                     // new separator
35943     { text: 'Config Item' }  // new item by config
35944 );
35945 </code></pre>
35946      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35947      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35948      */
35949     add : function(){
35950         var a = arguments, l = a.length, item;
35951         for(var i = 0; i < l; i++){
35952             var el = a[i];
35953             if ((typeof(el) == "object") && el.xtype && el.xns) {
35954                 el = Roo.factory(el, Roo.menu);
35955             }
35956             
35957             if(el.render){ // some kind of Item
35958                 item = this.addItem(el);
35959             }else if(typeof el == "string"){ // string
35960                 if(el == "separator" || el == "-"){
35961                     item = this.addSeparator();
35962                 }else{
35963                     item = this.addText(el);
35964                 }
35965             }else if(el.tagName || el.el){ // element
35966                 item = this.addElement(el);
35967             }else if(typeof el == "object"){ // must be menu item config?
35968                 item = this.addMenuItem(el);
35969             }
35970         }
35971         return item;
35972     },
35973
35974     /**
35975      * Returns this menu's underlying {@link Roo.Element} object
35976      * @return {Roo.Element} The element
35977      */
35978     getEl : function(){
35979         if(!this.el){
35980             this.render();
35981         }
35982         return this.el;
35983     },
35984
35985     /**
35986      * Adds a separator bar to the menu
35987      * @return {Roo.menu.Item} The menu item that was added
35988      */
35989     addSeparator : function(){
35990         return this.addItem(new Roo.menu.Separator());
35991     },
35992
35993     /**
35994      * Adds an {@link Roo.Element} object to the menu
35995      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35996      * @return {Roo.menu.Item} The menu item that was added
35997      */
35998     addElement : function(el){
35999         return this.addItem(new Roo.menu.BaseItem(el));
36000     },
36001
36002     /**
36003      * Adds an existing object based on {@link Roo.menu.Item} to the menu
36004      * @param {Roo.menu.Item} item The menu item to add
36005      * @return {Roo.menu.Item} The menu item that was added
36006      */
36007     addItem : function(item){
36008         this.items.add(item);
36009         if(this.ul){
36010             var li = document.createElement("li");
36011             li.className = "x-menu-list-item";
36012             this.ul.dom.appendChild(li);
36013             item.render(li, this);
36014             this.delayAutoWidth();
36015         }
36016         return item;
36017     },
36018
36019     /**
36020      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
36021      * @param {Object} config A MenuItem config object
36022      * @return {Roo.menu.Item} The menu item that was added
36023      */
36024     addMenuItem : function(config){
36025         if(!(config instanceof Roo.menu.Item)){
36026             if(typeof config.checked == "boolean"){ // must be check menu item config?
36027                 config = new Roo.menu.CheckItem(config);
36028             }else{
36029                 config = new Roo.menu.Item(config);
36030             }
36031         }
36032         return this.addItem(config);
36033     },
36034
36035     /**
36036      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
36037      * @param {String} text The text to display in the menu item
36038      * @return {Roo.menu.Item} The menu item that was added
36039      */
36040     addText : function(text){
36041         return this.addItem(new Roo.menu.TextItem({ text : text }));
36042     },
36043
36044     /**
36045      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
36046      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
36047      * @param {Roo.menu.Item} item The menu item to add
36048      * @return {Roo.menu.Item} The menu item that was added
36049      */
36050     insert : function(index, item){
36051         this.items.insert(index, item);
36052         if(this.ul){
36053             var li = document.createElement("li");
36054             li.className = "x-menu-list-item";
36055             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
36056             item.render(li, this);
36057             this.delayAutoWidth();
36058         }
36059         return item;
36060     },
36061
36062     /**
36063      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
36064      * @param {Roo.menu.Item} item The menu item to remove
36065      */
36066     remove : function(item){
36067         this.items.removeKey(item.id);
36068         item.destroy();
36069     },
36070
36071     /**
36072      * Removes and destroys all items in the menu
36073      */
36074     removeAll : function(){
36075         var f;
36076         while(f = this.items.first()){
36077             this.remove(f);
36078         }
36079     }
36080 });
36081
36082 // MenuNav is a private utility class used internally by the Menu
36083 Roo.menu.MenuNav = function(menu){
36084     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
36085     this.scope = this.menu = menu;
36086 };
36087
36088 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
36089     doRelay : function(e, h){
36090         var k = e.getKey();
36091         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
36092             this.menu.tryActivate(0, 1);
36093             return false;
36094         }
36095         return h.call(this.scope || this, e, this.menu);
36096     },
36097
36098     up : function(e, m){
36099         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
36100             m.tryActivate(m.items.length-1, -1);
36101         }
36102     },
36103
36104     down : function(e, m){
36105         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
36106             m.tryActivate(0, 1);
36107         }
36108     },
36109
36110     right : function(e, m){
36111         if(m.activeItem){
36112             m.activeItem.expandMenu(true);
36113         }
36114     },
36115
36116     left : function(e, m){
36117         m.hide();
36118         if(m.parentMenu && m.parentMenu.activeItem){
36119             m.parentMenu.activeItem.activate();
36120         }
36121     },
36122
36123     enter : function(e, m){
36124         if(m.activeItem){
36125             e.stopPropagation();
36126             m.activeItem.onClick(e);
36127             m.fireEvent("click", this, m.activeItem);
36128             return true;
36129         }
36130     }
36131 });/*
36132  * Based on:
36133  * Ext JS Library 1.1.1
36134  * Copyright(c) 2006-2007, Ext JS, LLC.
36135  *
36136  * Originally Released Under LGPL - original licence link has changed is not relivant.
36137  *
36138  * Fork - LGPL
36139  * <script type="text/javascript">
36140  */
36141  
36142 /**
36143  * @class Roo.menu.MenuMgr
36144  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
36145  * @singleton
36146  */
36147 Roo.menu.MenuMgr = function(){
36148    var menus, active, groups = {}, attached = false, lastShow = new Date();
36149
36150    // private - called when first menu is created
36151    function init(){
36152        menus = {};
36153        active = new Roo.util.MixedCollection();
36154        Roo.get(document).addKeyListener(27, function(){
36155            if(active.length > 0){
36156                hideAll();
36157            }
36158        });
36159    }
36160
36161    // private
36162    function hideAll(){
36163        if(active && active.length > 0){
36164            var c = active.clone();
36165            c.each(function(m){
36166                m.hide();
36167            });
36168        }
36169    }
36170
36171    // private
36172    function onHide(m){
36173        active.remove(m);
36174        if(active.length < 1){
36175            Roo.get(document).un("mousedown", onMouseDown);
36176            attached = false;
36177        }
36178    }
36179
36180    // private
36181    function onShow(m){
36182        var last = active.last();
36183        lastShow = new Date();
36184        active.add(m);
36185        if(!attached){
36186            Roo.get(document).on("mousedown", onMouseDown);
36187            attached = true;
36188        }
36189        if(m.parentMenu){
36190           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
36191           m.parentMenu.activeChild = m;
36192        }else if(last && last.isVisible()){
36193           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
36194        }
36195    }
36196
36197    // private
36198    function onBeforeHide(m){
36199        if(m.activeChild){
36200            m.activeChild.hide();
36201        }
36202        if(m.autoHideTimer){
36203            clearTimeout(m.autoHideTimer);
36204            delete m.autoHideTimer;
36205        }
36206    }
36207
36208    // private
36209    function onBeforeShow(m){
36210        var pm = m.parentMenu;
36211        if(!pm && !m.allowOtherMenus){
36212            hideAll();
36213        }else if(pm && pm.activeChild && active != m){
36214            pm.activeChild.hide();
36215        }
36216    }
36217
36218    // private
36219    function onMouseDown(e){
36220        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36221            hideAll();
36222        }
36223    }
36224
36225    // private
36226    function onBeforeCheck(mi, state){
36227        if(state){
36228            var g = groups[mi.group];
36229            for(var i = 0, l = g.length; i < l; i++){
36230                if(g[i] != mi){
36231                    g[i].setChecked(false);
36232                }
36233            }
36234        }
36235    }
36236
36237    return {
36238
36239        /**
36240         * Hides all menus that are currently visible
36241         */
36242        hideAll : function(){
36243             hideAll();  
36244        },
36245
36246        // private
36247        register : function(menu){
36248            if(!menus){
36249                init();
36250            }
36251            menus[menu.id] = menu;
36252            menu.on("beforehide", onBeforeHide);
36253            menu.on("hide", onHide);
36254            menu.on("beforeshow", onBeforeShow);
36255            menu.on("show", onShow);
36256            var g = menu.group;
36257            if(g && menu.events["checkchange"]){
36258                if(!groups[g]){
36259                    groups[g] = [];
36260                }
36261                groups[g].push(menu);
36262                menu.on("checkchange", onCheck);
36263            }
36264        },
36265
36266         /**
36267          * Returns a {@link Roo.menu.Menu} object
36268          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36269          * be used to generate and return a new Menu instance.
36270          */
36271        get : function(menu){
36272            if(typeof menu == "string"){ // menu id
36273                return menus[menu];
36274            }else if(menu.events){  // menu instance
36275                return menu;
36276            }else if(typeof menu.length == 'number'){ // array of menu items?
36277                return new Roo.menu.Menu({items:menu});
36278            }else{ // otherwise, must be a config
36279                return new Roo.menu.Menu(menu);
36280            }
36281        },
36282
36283        // private
36284        unregister : function(menu){
36285            delete menus[menu.id];
36286            menu.un("beforehide", onBeforeHide);
36287            menu.un("hide", onHide);
36288            menu.un("beforeshow", onBeforeShow);
36289            menu.un("show", onShow);
36290            var g = menu.group;
36291            if(g && menu.events["checkchange"]){
36292                groups[g].remove(menu);
36293                menu.un("checkchange", onCheck);
36294            }
36295        },
36296
36297        // private
36298        registerCheckable : function(menuItem){
36299            var g = menuItem.group;
36300            if(g){
36301                if(!groups[g]){
36302                    groups[g] = [];
36303                }
36304                groups[g].push(menuItem);
36305                menuItem.on("beforecheckchange", onBeforeCheck);
36306            }
36307        },
36308
36309        // private
36310        unregisterCheckable : function(menuItem){
36311            var g = menuItem.group;
36312            if(g){
36313                groups[g].remove(menuItem);
36314                menuItem.un("beforecheckchange", onBeforeCheck);
36315            }
36316        }
36317    };
36318 }();/*
36319  * Based on:
36320  * Ext JS Library 1.1.1
36321  * Copyright(c) 2006-2007, Ext JS, LLC.
36322  *
36323  * Originally Released Under LGPL - original licence link has changed is not relivant.
36324  *
36325  * Fork - LGPL
36326  * <script type="text/javascript">
36327  */
36328  
36329
36330 /**
36331  * @class Roo.menu.BaseItem
36332  * @extends Roo.Component
36333  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36334  * management and base configuration options shared by all menu components.
36335  * @constructor
36336  * Creates a new BaseItem
36337  * @param {Object} config Configuration options
36338  */
36339 Roo.menu.BaseItem = function(config){
36340     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36341
36342     this.addEvents({
36343         /**
36344          * @event click
36345          * Fires when this item is clicked
36346          * @param {Roo.menu.BaseItem} this
36347          * @param {Roo.EventObject} e
36348          */
36349         click: true,
36350         /**
36351          * @event activate
36352          * Fires when this item is activated
36353          * @param {Roo.menu.BaseItem} this
36354          */
36355         activate : true,
36356         /**
36357          * @event deactivate
36358          * Fires when this item is deactivated
36359          * @param {Roo.menu.BaseItem} this
36360          */
36361         deactivate : true
36362     });
36363
36364     if(this.handler){
36365         this.on("click", this.handler, this.scope, true);
36366     }
36367 };
36368
36369 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36370     /**
36371      * @cfg {Function} handler
36372      * A function that will handle the click event of this menu item (defaults to undefined)
36373      */
36374     /**
36375      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36376      */
36377     canActivate : false,
36378     
36379      /**
36380      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36381      */
36382     hidden: false,
36383     
36384     /**
36385      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36386      */
36387     activeClass : "x-menu-item-active",
36388     /**
36389      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36390      */
36391     hideOnClick : true,
36392     /**
36393      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36394      */
36395     hideDelay : 100,
36396
36397     // private
36398     ctype: "Roo.menu.BaseItem",
36399
36400     // private
36401     actionMode : "container",
36402
36403     // private
36404     render : function(container, parentMenu){
36405         this.parentMenu = parentMenu;
36406         Roo.menu.BaseItem.superclass.render.call(this, container);
36407         this.container.menuItemId = this.id;
36408     },
36409
36410     // private
36411     onRender : function(container, position){
36412         this.el = Roo.get(this.el);
36413         container.dom.appendChild(this.el.dom);
36414     },
36415
36416     // private
36417     onClick : function(e){
36418         if(!this.disabled && this.fireEvent("click", this, e) !== false
36419                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36420             this.handleClick(e);
36421         }else{
36422             e.stopEvent();
36423         }
36424     },
36425
36426     // private
36427     activate : function(){
36428         if(this.disabled){
36429             return false;
36430         }
36431         var li = this.container;
36432         li.addClass(this.activeClass);
36433         this.region = li.getRegion().adjust(2, 2, -2, -2);
36434         this.fireEvent("activate", this);
36435         return true;
36436     },
36437
36438     // private
36439     deactivate : function(){
36440         this.container.removeClass(this.activeClass);
36441         this.fireEvent("deactivate", this);
36442     },
36443
36444     // private
36445     shouldDeactivate : function(e){
36446         return !this.region || !this.region.contains(e.getPoint());
36447     },
36448
36449     // private
36450     handleClick : function(e){
36451         if(this.hideOnClick){
36452             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36453         }
36454     },
36455
36456     // private
36457     expandMenu : function(autoActivate){
36458         // do nothing
36459     },
36460
36461     // private
36462     hideMenu : function(){
36463         // do nothing
36464     }
36465 });/*
36466  * Based on:
36467  * Ext JS Library 1.1.1
36468  * Copyright(c) 2006-2007, Ext JS, LLC.
36469  *
36470  * Originally Released Under LGPL - original licence link has changed is not relivant.
36471  *
36472  * Fork - LGPL
36473  * <script type="text/javascript">
36474  */
36475  
36476 /**
36477  * @class Roo.menu.Adapter
36478  * @extends Roo.menu.BaseItem
36479  * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
36480  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36481  * @constructor
36482  * Creates a new Adapter
36483  * @param {Object} config Configuration options
36484  */
36485 Roo.menu.Adapter = function(component, config){
36486     Roo.menu.Adapter.superclass.constructor.call(this, config);
36487     this.component = component;
36488 };
36489 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36490     // private
36491     canActivate : true,
36492
36493     // private
36494     onRender : function(container, position){
36495         this.component.render(container);
36496         this.el = this.component.getEl();
36497     },
36498
36499     // private
36500     activate : function(){
36501         if(this.disabled){
36502             return false;
36503         }
36504         this.component.focus();
36505         this.fireEvent("activate", this);
36506         return true;
36507     },
36508
36509     // private
36510     deactivate : function(){
36511         this.fireEvent("deactivate", this);
36512     },
36513
36514     // private
36515     disable : function(){
36516         this.component.disable();
36517         Roo.menu.Adapter.superclass.disable.call(this);
36518     },
36519
36520     // private
36521     enable : function(){
36522         this.component.enable();
36523         Roo.menu.Adapter.superclass.enable.call(this);
36524     }
36525 });/*
36526  * Based on:
36527  * Ext JS Library 1.1.1
36528  * Copyright(c) 2006-2007, Ext JS, LLC.
36529  *
36530  * Originally Released Under LGPL - original licence link has changed is not relivant.
36531  *
36532  * Fork - LGPL
36533  * <script type="text/javascript">
36534  */
36535
36536 /**
36537  * @class Roo.menu.TextItem
36538  * @extends Roo.menu.BaseItem
36539  * Adds a static text string to a menu, usually used as either a heading or group separator.
36540  * Note: old style constructor with text is still supported.
36541  * 
36542  * @constructor
36543  * Creates a new TextItem
36544  * @param {Object} cfg Configuration
36545  */
36546 Roo.menu.TextItem = function(cfg){
36547     if (typeof(cfg) == 'string') {
36548         this.text = cfg;
36549     } else {
36550         Roo.apply(this,cfg);
36551     }
36552     
36553     Roo.menu.TextItem.superclass.constructor.call(this);
36554 };
36555
36556 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36557     /**
36558      * @cfg {Boolean} text Text to show on item.
36559      */
36560     text : '',
36561     
36562     /**
36563      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36564      */
36565     hideOnClick : false,
36566     /**
36567      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36568      */
36569     itemCls : "x-menu-text",
36570
36571     // private
36572     onRender : function(){
36573         var s = document.createElement("span");
36574         s.className = this.itemCls;
36575         s.innerHTML = this.text;
36576         this.el = s;
36577         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36578     }
36579 });/*
36580  * Based on:
36581  * Ext JS Library 1.1.1
36582  * Copyright(c) 2006-2007, Ext JS, LLC.
36583  *
36584  * Originally Released Under LGPL - original licence link has changed is not relivant.
36585  *
36586  * Fork - LGPL
36587  * <script type="text/javascript">
36588  */
36589
36590 /**
36591  * @class Roo.menu.Separator
36592  * @extends Roo.menu.BaseItem
36593  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36594  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36595  * @constructor
36596  * @param {Object} config Configuration options
36597  */
36598 Roo.menu.Separator = function(config){
36599     Roo.menu.Separator.superclass.constructor.call(this, config);
36600 };
36601
36602 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36603     /**
36604      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36605      */
36606     itemCls : "x-menu-sep",
36607     /**
36608      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36609      */
36610     hideOnClick : false,
36611
36612     // private
36613     onRender : function(li){
36614         var s = document.createElement("span");
36615         s.className = this.itemCls;
36616         s.innerHTML = "&#160;";
36617         this.el = s;
36618         li.addClass("x-menu-sep-li");
36619         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36620     }
36621 });/*
36622  * Based on:
36623  * Ext JS Library 1.1.1
36624  * Copyright(c) 2006-2007, Ext JS, LLC.
36625  *
36626  * Originally Released Under LGPL - original licence link has changed is not relivant.
36627  *
36628  * Fork - LGPL
36629  * <script type="text/javascript">
36630  */
36631 /**
36632  * @class Roo.menu.Item
36633  * @extends Roo.menu.BaseItem
36634  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36635  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36636  * activation and click handling.
36637  * @constructor
36638  * Creates a new Item
36639  * @param {Object} config Configuration options
36640  */
36641 Roo.menu.Item = function(config){
36642     Roo.menu.Item.superclass.constructor.call(this, config);
36643     if(this.menu){
36644         this.menu = Roo.menu.MenuMgr.get(this.menu);
36645     }
36646 };
36647 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36648     
36649     /**
36650      * @cfg {String} text
36651      * The text to show on the menu item.
36652      */
36653     text: '',
36654      /**
36655      * @cfg {String} HTML to render in menu
36656      * The text to show on the menu item (HTML version).
36657      */
36658     html: '',
36659     /**
36660      * @cfg {String} icon
36661      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36662      */
36663     icon: undefined,
36664     /**
36665      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36666      */
36667     itemCls : "x-menu-item",
36668     /**
36669      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36670      */
36671     canActivate : true,
36672     /**
36673      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36674      */
36675     showDelay: 200,
36676     // doc'd in BaseItem
36677     hideDelay: 200,
36678
36679     // private
36680     ctype: "Roo.menu.Item",
36681     
36682     // private
36683     onRender : function(container, position){
36684         var el = document.createElement("a");
36685         el.hideFocus = true;
36686         el.unselectable = "on";
36687         el.href = this.href || "#";
36688         if(this.hrefTarget){
36689             el.target = this.hrefTarget;
36690         }
36691         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36692         
36693         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36694         
36695         el.innerHTML = String.format(
36696                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36697                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36698         this.el = el;
36699         Roo.menu.Item.superclass.onRender.call(this, container, position);
36700     },
36701
36702     /**
36703      * Sets the text to display in this menu item
36704      * @param {String} text The text to display
36705      * @param {Boolean} isHTML true to indicate text is pure html.
36706      */
36707     setText : function(text, isHTML){
36708         if (isHTML) {
36709             this.html = text;
36710         } else {
36711             this.text = text;
36712             this.html = '';
36713         }
36714         if(this.rendered){
36715             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36716      
36717             this.el.update(String.format(
36718                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36719                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36720             this.parentMenu.autoWidth();
36721         }
36722     },
36723
36724     // private
36725     handleClick : function(e){
36726         if(!this.href){ // if no link defined, stop the event automatically
36727             e.stopEvent();
36728         }
36729         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36730     },
36731
36732     // private
36733     activate : function(autoExpand){
36734         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36735             this.focus();
36736             if(autoExpand){
36737                 this.expandMenu();
36738             }
36739         }
36740         return true;
36741     },
36742
36743     // private
36744     shouldDeactivate : function(e){
36745         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36746             if(this.menu && this.menu.isVisible()){
36747                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36748             }
36749             return true;
36750         }
36751         return false;
36752     },
36753
36754     // private
36755     deactivate : function(){
36756         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36757         this.hideMenu();
36758     },
36759
36760     // private
36761     expandMenu : function(autoActivate){
36762         if(!this.disabled && this.menu){
36763             clearTimeout(this.hideTimer);
36764             delete this.hideTimer;
36765             if(!this.menu.isVisible() && !this.showTimer){
36766                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36767             }else if (this.menu.isVisible() && autoActivate){
36768                 this.menu.tryActivate(0, 1);
36769             }
36770         }
36771     },
36772
36773     // private
36774     deferExpand : function(autoActivate){
36775         delete this.showTimer;
36776         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36777         if(autoActivate){
36778             this.menu.tryActivate(0, 1);
36779         }
36780     },
36781
36782     // private
36783     hideMenu : function(){
36784         clearTimeout(this.showTimer);
36785         delete this.showTimer;
36786         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36787             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36788         }
36789     },
36790
36791     // private
36792     deferHide : function(){
36793         delete this.hideTimer;
36794         this.menu.hide();
36795     }
36796 });/*
36797  * Based on:
36798  * Ext JS Library 1.1.1
36799  * Copyright(c) 2006-2007, Ext JS, LLC.
36800  *
36801  * Originally Released Under LGPL - original licence link has changed is not relivant.
36802  *
36803  * Fork - LGPL
36804  * <script type="text/javascript">
36805  */
36806  
36807 /**
36808  * @class Roo.menu.CheckItem
36809  * @extends Roo.menu.Item
36810  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36811  * @constructor
36812  * Creates a new CheckItem
36813  * @param {Object} config Configuration options
36814  */
36815 Roo.menu.CheckItem = function(config){
36816     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36817     this.addEvents({
36818         /**
36819          * @event beforecheckchange
36820          * Fires before the checked value is set, providing an opportunity to cancel if needed
36821          * @param {Roo.menu.CheckItem} this
36822          * @param {Boolean} checked The new checked value that will be set
36823          */
36824         "beforecheckchange" : true,
36825         /**
36826          * @event checkchange
36827          * Fires after the checked value has been set
36828          * @param {Roo.menu.CheckItem} this
36829          * @param {Boolean} checked The checked value that was set
36830          */
36831         "checkchange" : true
36832     });
36833     if(this.checkHandler){
36834         this.on('checkchange', this.checkHandler, this.scope);
36835     }
36836 };
36837 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36838     /**
36839      * @cfg {String} group
36840      * All check items with the same group name will automatically be grouped into a single-select
36841      * radio button group (defaults to '')
36842      */
36843     /**
36844      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36845      */
36846     itemCls : "x-menu-item x-menu-check-item",
36847     /**
36848      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36849      */
36850     groupClass : "x-menu-group-item",
36851
36852     /**
36853      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36854      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36855      * initialized with checked = true will be rendered as checked.
36856      */
36857     checked: false,
36858
36859     // private
36860     ctype: "Roo.menu.CheckItem",
36861
36862     // private
36863     onRender : function(c){
36864         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36865         if(this.group){
36866             this.el.addClass(this.groupClass);
36867         }
36868         Roo.menu.MenuMgr.registerCheckable(this);
36869         if(this.checked){
36870             this.checked = false;
36871             this.setChecked(true, true);
36872         }
36873     },
36874
36875     // private
36876     destroy : function(){
36877         if(this.rendered){
36878             Roo.menu.MenuMgr.unregisterCheckable(this);
36879         }
36880         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36881     },
36882
36883     /**
36884      * Set the checked state of this item
36885      * @param {Boolean} checked The new checked value
36886      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36887      */
36888     setChecked : function(state, suppressEvent){
36889         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36890             if(this.container){
36891                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36892             }
36893             this.checked = state;
36894             if(suppressEvent !== true){
36895                 this.fireEvent("checkchange", this, state);
36896             }
36897         }
36898     },
36899
36900     // private
36901     handleClick : function(e){
36902        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36903            this.setChecked(!this.checked);
36904        }
36905        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36906     }
36907 });/*
36908  * Based on:
36909  * Ext JS Library 1.1.1
36910  * Copyright(c) 2006-2007, Ext JS, LLC.
36911  *
36912  * Originally Released Under LGPL - original licence link has changed is not relivant.
36913  *
36914  * Fork - LGPL
36915  * <script type="text/javascript">
36916  */
36917  
36918 /**
36919  * @class Roo.menu.DateItem
36920  * @extends Roo.menu.Adapter
36921  * A menu item that wraps the {@link Roo.DatPicker} component.
36922  * @constructor
36923  * Creates a new DateItem
36924  * @param {Object} config Configuration options
36925  */
36926 Roo.menu.DateItem = function(config){
36927     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36928     /** The Roo.DatePicker object @type Roo.DatePicker */
36929     this.picker = this.component;
36930     this.addEvents({select: true});
36931     
36932     this.picker.on("render", function(picker){
36933         picker.getEl().swallowEvent("click");
36934         picker.container.addClass("x-menu-date-item");
36935     });
36936
36937     this.picker.on("select", this.onSelect, this);
36938 };
36939
36940 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36941     // private
36942     onSelect : function(picker, date){
36943         this.fireEvent("select", this, date, picker);
36944         Roo.menu.DateItem.superclass.handleClick.call(this);
36945     }
36946 });/*
36947  * Based on:
36948  * Ext JS Library 1.1.1
36949  * Copyright(c) 2006-2007, Ext JS, LLC.
36950  *
36951  * Originally Released Under LGPL - original licence link has changed is not relivant.
36952  *
36953  * Fork - LGPL
36954  * <script type="text/javascript">
36955  */
36956  
36957 /**
36958  * @class Roo.menu.ColorItem
36959  * @extends Roo.menu.Adapter
36960  * A menu item that wraps the {@link Roo.ColorPalette} component.
36961  * @constructor
36962  * Creates a new ColorItem
36963  * @param {Object} config Configuration options
36964  */
36965 Roo.menu.ColorItem = function(config){
36966     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36967     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36968     this.palette = this.component;
36969     this.relayEvents(this.palette, ["select"]);
36970     if(this.selectHandler){
36971         this.on('select', this.selectHandler, this.scope);
36972     }
36973 };
36974 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36975  * Based on:
36976  * Ext JS Library 1.1.1
36977  * Copyright(c) 2006-2007, Ext JS, LLC.
36978  *
36979  * Originally Released Under LGPL - original licence link has changed is not relivant.
36980  *
36981  * Fork - LGPL
36982  * <script type="text/javascript">
36983  */
36984  
36985
36986 /**
36987  * @class Roo.menu.DateMenu
36988  * @extends Roo.menu.Menu
36989  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36990  * @constructor
36991  * Creates a new DateMenu
36992  * @param {Object} config Configuration options
36993  */
36994 Roo.menu.DateMenu = function(config){
36995     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36996     this.plain = true;
36997     var di = new Roo.menu.DateItem(config);
36998     this.add(di);
36999     /**
37000      * The {@link Roo.DatePicker} instance for this DateMenu
37001      * @type DatePicker
37002      */
37003     this.picker = di.picker;
37004     /**
37005      * @event select
37006      * @param {DatePicker} picker
37007      * @param {Date} date
37008      */
37009     this.relayEvents(di, ["select"]);
37010     this.on('beforeshow', function(){
37011         if(this.picker){
37012             this.picker.hideMonthPicker(false);
37013         }
37014     }, this);
37015 };
37016 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
37017     cls:'x-date-menu'
37018 });/*
37019  * Based on:
37020  * Ext JS Library 1.1.1
37021  * Copyright(c) 2006-2007, Ext JS, LLC.
37022  *
37023  * Originally Released Under LGPL - original licence link has changed is not relivant.
37024  *
37025  * Fork - LGPL
37026  * <script type="text/javascript">
37027  */
37028  
37029
37030 /**
37031  * @class Roo.menu.ColorMenu
37032  * @extends Roo.menu.Menu
37033  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
37034  * @constructor
37035  * Creates a new ColorMenu
37036  * @param {Object} config Configuration options
37037  */
37038 Roo.menu.ColorMenu = function(config){
37039     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
37040     this.plain = true;
37041     var ci = new Roo.menu.ColorItem(config);
37042     this.add(ci);
37043     /**
37044      * The {@link Roo.ColorPalette} instance for this ColorMenu
37045      * @type ColorPalette
37046      */
37047     this.palette = ci.palette;
37048     /**
37049      * @event select
37050      * @param {ColorPalette} palette
37051      * @param {String} color
37052      */
37053     this.relayEvents(ci, ["select"]);
37054 };
37055 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
37056  * Based on:
37057  * Ext JS Library 1.1.1
37058  * Copyright(c) 2006-2007, Ext JS, LLC.
37059  *
37060  * Originally Released Under LGPL - original licence link has changed is not relivant.
37061  *
37062  * Fork - LGPL
37063  * <script type="text/javascript">
37064  */
37065  
37066 /**
37067  * @class Roo.form.Field
37068  * @extends Roo.BoxComponent
37069  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
37070  * @constructor
37071  * Creates a new Field
37072  * @param {Object} config Configuration options
37073  */
37074 Roo.form.Field = function(config){
37075     Roo.form.Field.superclass.constructor.call(this, config);
37076 };
37077
37078 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
37079     /**
37080      * @cfg {String} fieldLabel Label to use when rendering a form.
37081      */
37082        /**
37083      * @cfg {String} qtip Mouse over tip
37084      */
37085      
37086     /**
37087      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
37088      */
37089     invalidClass : "x-form-invalid",
37090     /**
37091      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
37092      */
37093     invalidText : "The value in this field is invalid",
37094     /**
37095      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
37096      */
37097     focusClass : "x-form-focus",
37098     /**
37099      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
37100       automatic validation (defaults to "keyup").
37101      */
37102     validationEvent : "keyup",
37103     /**
37104      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
37105      */
37106     validateOnBlur : true,
37107     /**
37108      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
37109      */
37110     validationDelay : 250,
37111     /**
37112      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37113      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
37114      */
37115     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
37116     /**
37117      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
37118      */
37119     fieldClass : "x-form-field",
37120     /**
37121      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
37122      *<pre>
37123 Value         Description
37124 -----------   ----------------------------------------------------------------------
37125 qtip          Display a quick tip when the user hovers over the field
37126 title         Display a default browser title attribute popup
37127 under         Add a block div beneath the field containing the error text
37128 side          Add an error icon to the right of the field with a popup on hover
37129 [element id]  Add the error text directly to the innerHTML of the specified element
37130 </pre>
37131      */
37132     msgTarget : 'qtip',
37133     /**
37134      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
37135      */
37136     msgFx : 'normal',
37137
37138     /**
37139      * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
37140      */
37141     readOnly : false,
37142
37143     /**
37144      * @cfg {Boolean} disabled True to disable the field (defaults to false).
37145      */
37146     disabled : false,
37147
37148     /**
37149      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
37150      */
37151     inputType : undefined,
37152     
37153     /**
37154      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
37155          */
37156         tabIndex : undefined,
37157         
37158     // private
37159     isFormField : true,
37160
37161     // private
37162     hasFocus : false,
37163     /**
37164      * @property {Roo.Element} fieldEl
37165      * Element Containing the rendered Field (with label etc.)
37166      */
37167     /**
37168      * @cfg {Mixed} value A value to initialize this field with.
37169      */
37170     value : undefined,
37171
37172     /**
37173      * @cfg {String} name The field's HTML name attribute.
37174      */
37175     /**
37176      * @cfg {String} cls A CSS class to apply to the field's underlying element.
37177      */
37178
37179         // private ??
37180         initComponent : function(){
37181         Roo.form.Field.superclass.initComponent.call(this);
37182         this.addEvents({
37183             /**
37184              * @event focus
37185              * Fires when this field receives input focus.
37186              * @param {Roo.form.Field} this
37187              */
37188             focus : true,
37189             /**
37190              * @event blur
37191              * Fires when this field loses input focus.
37192              * @param {Roo.form.Field} this
37193              */
37194             blur : true,
37195             /**
37196              * @event specialkey
37197              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
37198              * {@link Roo.EventObject#getKey} to determine which key was pressed.
37199              * @param {Roo.form.Field} this
37200              * @param {Roo.EventObject} e The event object
37201              */
37202             specialkey : true,
37203             /**
37204              * @event change
37205              * Fires just before the field blurs if the field value has changed.
37206              * @param {Roo.form.Field} this
37207              * @param {Mixed} newValue The new value
37208              * @param {Mixed} oldValue The original value
37209              */
37210             change : true,
37211             /**
37212              * @event invalid
37213              * Fires after the field has been marked as invalid.
37214              * @param {Roo.form.Field} this
37215              * @param {String} msg The validation message
37216              */
37217             invalid : true,
37218             /**
37219              * @event valid
37220              * Fires after the field has been validated with no errors.
37221              * @param {Roo.form.Field} this
37222              */
37223             valid : true,
37224              /**
37225              * @event keyup
37226              * Fires after the key up
37227              * @param {Roo.form.Field} this
37228              * @param {Roo.EventObject}  e The event Object
37229              */
37230             keyup : true
37231         });
37232     },
37233
37234     /**
37235      * Returns the name attribute of the field if available
37236      * @return {String} name The field name
37237      */
37238     getName: function(){
37239          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37240     },
37241
37242     // private
37243     onRender : function(ct, position){
37244         Roo.form.Field.superclass.onRender.call(this, ct, position);
37245         if(!this.el){
37246             var cfg = this.getAutoCreate();
37247             if(!cfg.name){
37248                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37249             }
37250             if (!cfg.name.length) {
37251                 delete cfg.name;
37252             }
37253             if(this.inputType){
37254                 cfg.type = this.inputType;
37255             }
37256             this.el = ct.createChild(cfg, position);
37257         }
37258         var type = this.el.dom.type;
37259         if(type){
37260             if(type == 'password'){
37261                 type = 'text';
37262             }
37263             this.el.addClass('x-form-'+type);
37264         }
37265         if(this.readOnly){
37266             this.el.dom.readOnly = true;
37267         }
37268         if(this.tabIndex !== undefined){
37269             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37270         }
37271
37272         this.el.addClass([this.fieldClass, this.cls]);
37273         this.initValue();
37274     },
37275
37276     /**
37277      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37278      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37279      * @return {Roo.form.Field} this
37280      */
37281     applyTo : function(target){
37282         this.allowDomMove = false;
37283         this.el = Roo.get(target);
37284         this.render(this.el.dom.parentNode);
37285         return this;
37286     },
37287
37288     // private
37289     initValue : function(){
37290         if(this.value !== undefined){
37291             this.setValue(this.value);
37292         }else if(this.el.dom.value.length > 0){
37293             this.setValue(this.el.dom.value);
37294         }
37295     },
37296
37297     /**
37298      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37299      */
37300     isDirty : function() {
37301         if(this.disabled) {
37302             return false;
37303         }
37304         return String(this.getValue()) !== String(this.originalValue);
37305     },
37306
37307     // private
37308     afterRender : function(){
37309         Roo.form.Field.superclass.afterRender.call(this);
37310         this.initEvents();
37311     },
37312
37313     // private
37314     fireKey : function(e){
37315         //Roo.log('field ' + e.getKey());
37316         if(e.isNavKeyPress()){
37317             this.fireEvent("specialkey", this, e);
37318         }
37319     },
37320
37321     /**
37322      * Resets the current field value to the originally loaded value and clears any validation messages
37323      */
37324     reset : function(){
37325         this.setValue(this.resetValue);
37326         this.clearInvalid();
37327     },
37328
37329     // private
37330     initEvents : function(){
37331         // safari killled keypress - so keydown is now used..
37332         this.el.on("keydown" , this.fireKey,  this);
37333         this.el.on("focus", this.onFocus,  this);
37334         this.el.on("blur", this.onBlur,  this);
37335         this.el.relayEvent('keyup', this);
37336
37337         // reference to original value for reset
37338         this.originalValue = this.getValue();
37339         this.resetValue =  this.getValue();
37340     },
37341
37342     // private
37343     onFocus : function(){
37344         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37345             this.el.addClass(this.focusClass);
37346         }
37347         if(!this.hasFocus){
37348             this.hasFocus = true;
37349             this.startValue = this.getValue();
37350             this.fireEvent("focus", this);
37351         }
37352     },
37353
37354     beforeBlur : Roo.emptyFn,
37355
37356     // private
37357     onBlur : function(){
37358         this.beforeBlur();
37359         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37360             this.el.removeClass(this.focusClass);
37361         }
37362         this.hasFocus = false;
37363         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37364             this.validate();
37365         }
37366         var v = this.getValue();
37367         if(String(v) !== String(this.startValue)){
37368             this.fireEvent('change', this, v, this.startValue);
37369         }
37370         this.fireEvent("blur", this);
37371     },
37372
37373     /**
37374      * Returns whether or not the field value is currently valid
37375      * @param {Boolean} preventMark True to disable marking the field invalid
37376      * @return {Boolean} True if the value is valid, else false
37377      */
37378     isValid : function(preventMark){
37379         if(this.disabled){
37380             return true;
37381         }
37382         var restore = this.preventMark;
37383         this.preventMark = preventMark === true;
37384         var v = this.validateValue(this.processValue(this.getRawValue()));
37385         this.preventMark = restore;
37386         return v;
37387     },
37388
37389     /**
37390      * Validates the field value
37391      * @return {Boolean} True if the value is valid, else false
37392      */
37393     validate : function(){
37394         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37395             this.clearInvalid();
37396             return true;
37397         }
37398         return false;
37399     },
37400
37401     processValue : function(value){
37402         return value;
37403     },
37404
37405     // private
37406     // Subclasses should provide the validation implementation by overriding this
37407     validateValue : function(value){
37408         return true;
37409     },
37410
37411     /**
37412      * Mark this field as invalid
37413      * @param {String} msg The validation message
37414      */
37415     markInvalid : function(msg){
37416         if(!this.rendered || this.preventMark){ // not rendered
37417             return;
37418         }
37419         
37420         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37421         
37422         obj.el.addClass(this.invalidClass);
37423         msg = msg || this.invalidText;
37424         switch(this.msgTarget){
37425             case 'qtip':
37426                 obj.el.dom.qtip = msg;
37427                 obj.el.dom.qclass = 'x-form-invalid-tip';
37428                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37429                     Roo.QuickTips.enable();
37430                 }
37431                 break;
37432             case 'title':
37433                 this.el.dom.title = msg;
37434                 break;
37435             case 'under':
37436                 if(!this.errorEl){
37437                     var elp = this.el.findParent('.x-form-element', 5, true);
37438                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37439                     this.errorEl.setWidth(elp.getWidth(true)-20);
37440                 }
37441                 this.errorEl.update(msg);
37442                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37443                 break;
37444             case 'side':
37445                 if(!this.errorIcon){
37446                     var elp = this.el.findParent('.x-form-element', 5, true);
37447                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37448                 }
37449                 this.alignErrorIcon();
37450                 this.errorIcon.dom.qtip = msg;
37451                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37452                 this.errorIcon.show();
37453                 this.on('resize', this.alignErrorIcon, this);
37454                 break;
37455             default:
37456                 var t = Roo.getDom(this.msgTarget);
37457                 t.innerHTML = msg;
37458                 t.style.display = this.msgDisplay;
37459                 break;
37460         }
37461         this.fireEvent('invalid', this, msg);
37462     },
37463
37464     // private
37465     alignErrorIcon : function(){
37466         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37467     },
37468
37469     /**
37470      * Clear any invalid styles/messages for this field
37471      */
37472     clearInvalid : function(){
37473         if(!this.rendered || this.preventMark){ // not rendered
37474             return;
37475         }
37476         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37477         
37478         obj.el.removeClass(this.invalidClass);
37479         switch(this.msgTarget){
37480             case 'qtip':
37481                 obj.el.dom.qtip = '';
37482                 break;
37483             case 'title':
37484                 this.el.dom.title = '';
37485                 break;
37486             case 'under':
37487                 if(this.errorEl){
37488                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37489                 }
37490                 break;
37491             case 'side':
37492                 if(this.errorIcon){
37493                     this.errorIcon.dom.qtip = '';
37494                     this.errorIcon.hide();
37495                     this.un('resize', this.alignErrorIcon, this);
37496                 }
37497                 break;
37498             default:
37499                 var t = Roo.getDom(this.msgTarget);
37500                 t.innerHTML = '';
37501                 t.style.display = 'none';
37502                 break;
37503         }
37504         this.fireEvent('valid', this);
37505     },
37506
37507     /**
37508      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37509      * @return {Mixed} value The field value
37510      */
37511     getRawValue : function(){
37512         var v = this.el.getValue();
37513         
37514         return v;
37515     },
37516
37517     /**
37518      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37519      * @return {Mixed} value The field value
37520      */
37521     getValue : function(){
37522         var v = this.el.getValue();
37523          
37524         return v;
37525     },
37526
37527     /**
37528      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37529      * @param {Mixed} value The value to set
37530      */
37531     setRawValue : function(v){
37532         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37533     },
37534
37535     /**
37536      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37537      * @param {Mixed} value The value to set
37538      */
37539     setValue : function(v){
37540         this.value = v;
37541         if(this.rendered){
37542             this.el.dom.value = (v === null || v === undefined ? '' : v);
37543              this.validate();
37544         }
37545     },
37546
37547     adjustSize : function(w, h){
37548         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37549         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37550         return s;
37551     },
37552
37553     adjustWidth : function(tag, w){
37554         tag = tag.toLowerCase();
37555         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37556             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37557                 if(tag == 'input'){
37558                     return w + 2;
37559                 }
37560                 if(tag == 'textarea'){
37561                     return w-2;
37562                 }
37563             }else if(Roo.isOpera){
37564                 if(tag == 'input'){
37565                     return w + 2;
37566                 }
37567                 if(tag == 'textarea'){
37568                     return w-2;
37569                 }
37570             }
37571         }
37572         return w;
37573     }
37574 });
37575
37576
37577 // anything other than normal should be considered experimental
37578 Roo.form.Field.msgFx = {
37579     normal : {
37580         show: function(msgEl, f){
37581             msgEl.setDisplayed('block');
37582         },
37583
37584         hide : function(msgEl, f){
37585             msgEl.setDisplayed(false).update('');
37586         }
37587     },
37588
37589     slide : {
37590         show: function(msgEl, f){
37591             msgEl.slideIn('t', {stopFx:true});
37592         },
37593
37594         hide : function(msgEl, f){
37595             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37596         }
37597     },
37598
37599     slideRight : {
37600         show: function(msgEl, f){
37601             msgEl.fixDisplay();
37602             msgEl.alignTo(f.el, 'tl-tr');
37603             msgEl.slideIn('l', {stopFx:true});
37604         },
37605
37606         hide : function(msgEl, f){
37607             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37608         }
37609     }
37610 };/*
37611  * Based on:
37612  * Ext JS Library 1.1.1
37613  * Copyright(c) 2006-2007, Ext JS, LLC.
37614  *
37615  * Originally Released Under LGPL - original licence link has changed is not relivant.
37616  *
37617  * Fork - LGPL
37618  * <script type="text/javascript">
37619  */
37620  
37621
37622 /**
37623  * @class Roo.form.TextField
37624  * @extends Roo.form.Field
37625  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37626  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37627  * @constructor
37628  * Creates a new TextField
37629  * @param {Object} config Configuration options
37630  */
37631 Roo.form.TextField = function(config){
37632     Roo.form.TextField.superclass.constructor.call(this, config);
37633     this.addEvents({
37634         /**
37635          * @event autosize
37636          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37637          * according to the default logic, but this event provides a hook for the developer to apply additional
37638          * logic at runtime to resize the field if needed.
37639              * @param {Roo.form.Field} this This text field
37640              * @param {Number} width The new field width
37641              */
37642         autosize : true
37643     });
37644 };
37645
37646 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37647     /**
37648      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37649      */
37650     grow : false,
37651     /**
37652      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37653      */
37654     growMin : 30,
37655     /**
37656      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37657      */
37658     growMax : 800,
37659     /**
37660      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37661      */
37662     vtype : null,
37663     /**
37664      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37665      */
37666     maskRe : null,
37667     /**
37668      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37669      */
37670     disableKeyFilter : false,
37671     /**
37672      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37673      */
37674     allowBlank : true,
37675     /**
37676      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37677      */
37678     minLength : 0,
37679     /**
37680      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37681      */
37682     maxLength : Number.MAX_VALUE,
37683     /**
37684      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37685      */
37686     minLengthText : "The minimum length for this field is {0}",
37687     /**
37688      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37689      */
37690     maxLengthText : "The maximum length for this field is {0}",
37691     /**
37692      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37693      */
37694     selectOnFocus : false,
37695     /**
37696      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37697      */
37698     blankText : "This field is required",
37699     /**
37700      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37701      * If available, this function will be called only after the basic validators all return true, and will be passed the
37702      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37703      */
37704     validator : null,
37705     /**
37706      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37707      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37708      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37709      */
37710     regex : null,
37711     /**
37712      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37713      */
37714     regexText : "",
37715     /**
37716      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37717      */
37718     emptyText : null,
37719    
37720
37721     // private
37722     initEvents : function()
37723     {
37724         if (this.emptyText) {
37725             this.el.attr('placeholder', this.emptyText);
37726         }
37727         
37728         Roo.form.TextField.superclass.initEvents.call(this);
37729         if(this.validationEvent == 'keyup'){
37730             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37731             this.el.on('keyup', this.filterValidation, this);
37732         }
37733         else if(this.validationEvent !== false){
37734             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37735         }
37736         
37737         if(this.selectOnFocus){
37738             this.on("focus", this.preFocus, this);
37739             
37740         }
37741         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37742             this.el.on("keypress", this.filterKeys, this);
37743         }
37744         if(this.grow){
37745             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37746             this.el.on("click", this.autoSize,  this);
37747         }
37748         if(this.el.is('input[type=password]') && Roo.isSafari){
37749             this.el.on('keydown', this.SafariOnKeyDown, this);
37750         }
37751     },
37752
37753     processValue : function(value){
37754         if(this.stripCharsRe){
37755             var newValue = value.replace(this.stripCharsRe, '');
37756             if(newValue !== value){
37757                 this.setRawValue(newValue);
37758                 return newValue;
37759             }
37760         }
37761         return value;
37762     },
37763
37764     filterValidation : function(e){
37765         if(!e.isNavKeyPress()){
37766             this.validationTask.delay(this.validationDelay);
37767         }
37768     },
37769
37770     // private
37771     onKeyUp : function(e){
37772         if(!e.isNavKeyPress()){
37773             this.autoSize();
37774         }
37775     },
37776
37777     /**
37778      * Resets the current field value to the originally-loaded value and clears any validation messages.
37779      *  
37780      */
37781     reset : function(){
37782         Roo.form.TextField.superclass.reset.call(this);
37783        
37784     },
37785
37786     
37787     // private
37788     preFocus : function(){
37789         
37790         if(this.selectOnFocus){
37791             this.el.dom.select();
37792         }
37793     },
37794
37795     
37796     // private
37797     filterKeys : function(e){
37798         var k = e.getKey();
37799         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37800             return;
37801         }
37802         var c = e.getCharCode(), cc = String.fromCharCode(c);
37803         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37804             return;
37805         }
37806         if(!this.maskRe.test(cc)){
37807             e.stopEvent();
37808         }
37809     },
37810
37811     setValue : function(v){
37812         
37813         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37814         
37815         this.autoSize();
37816     },
37817
37818     /**
37819      * Validates a value according to the field's validation rules and marks the field as invalid
37820      * if the validation fails
37821      * @param {Mixed} value The value to validate
37822      * @return {Boolean} True if the value is valid, else false
37823      */
37824     validateValue : function(value){
37825         if(value.length < 1)  { // if it's blank
37826              if(this.allowBlank){
37827                 this.clearInvalid();
37828                 return true;
37829              }else{
37830                 this.markInvalid(this.blankText);
37831                 return false;
37832              }
37833         }
37834         if(value.length < this.minLength){
37835             this.markInvalid(String.format(this.minLengthText, this.minLength));
37836             return false;
37837         }
37838         if(value.length > this.maxLength){
37839             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37840             return false;
37841         }
37842         if(this.vtype){
37843             var vt = Roo.form.VTypes;
37844             if(!vt[this.vtype](value, this)){
37845                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37846                 return false;
37847             }
37848         }
37849         if(typeof this.validator == "function"){
37850             var msg = this.validator(value);
37851             if(msg !== true){
37852                 this.markInvalid(msg);
37853                 return false;
37854             }
37855         }
37856         if(this.regex && !this.regex.test(value)){
37857             this.markInvalid(this.regexText);
37858             return false;
37859         }
37860         return true;
37861     },
37862
37863     /**
37864      * Selects text in this field
37865      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37866      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37867      */
37868     selectText : function(start, end){
37869         var v = this.getRawValue();
37870         if(v.length > 0){
37871             start = start === undefined ? 0 : start;
37872             end = end === undefined ? v.length : end;
37873             var d = this.el.dom;
37874             if(d.setSelectionRange){
37875                 d.setSelectionRange(start, end);
37876             }else if(d.createTextRange){
37877                 var range = d.createTextRange();
37878                 range.moveStart("character", start);
37879                 range.moveEnd("character", v.length-end);
37880                 range.select();
37881             }
37882         }
37883     },
37884
37885     /**
37886      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37887      * This only takes effect if grow = true, and fires the autosize event.
37888      */
37889     autoSize : function(){
37890         if(!this.grow || !this.rendered){
37891             return;
37892         }
37893         if(!this.metrics){
37894             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37895         }
37896         var el = this.el;
37897         var v = el.dom.value;
37898         var d = document.createElement('div');
37899         d.appendChild(document.createTextNode(v));
37900         v = d.innerHTML;
37901         d = null;
37902         v += "&#160;";
37903         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37904         this.el.setWidth(w);
37905         this.fireEvent("autosize", this, w);
37906     },
37907     
37908     // private
37909     SafariOnKeyDown : function(event)
37910     {
37911         // this is a workaround for a password hang bug on chrome/ webkit.
37912         
37913         var isSelectAll = false;
37914         
37915         if(this.el.dom.selectionEnd > 0){
37916             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37917         }
37918         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37919             event.preventDefault();
37920             this.setValue('');
37921             return;
37922         }
37923         
37924         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
37925             
37926             event.preventDefault();
37927             // this is very hacky as keydown always get's upper case.
37928             
37929             var cc = String.fromCharCode(event.getCharCode());
37930             
37931             
37932             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37933             
37934         }
37935         
37936         
37937     }
37938 });/*
37939  * Based on:
37940  * Ext JS Library 1.1.1
37941  * Copyright(c) 2006-2007, Ext JS, LLC.
37942  *
37943  * Originally Released Under LGPL - original licence link has changed is not relivant.
37944  *
37945  * Fork - LGPL
37946  * <script type="text/javascript">
37947  */
37948  
37949 /**
37950  * @class Roo.form.Hidden
37951  * @extends Roo.form.TextField
37952  * Simple Hidden element used on forms 
37953  * 
37954  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37955  * 
37956  * @constructor
37957  * Creates a new Hidden form element.
37958  * @param {Object} config Configuration options
37959  */
37960
37961
37962
37963 // easy hidden field...
37964 Roo.form.Hidden = function(config){
37965     Roo.form.Hidden.superclass.constructor.call(this, config);
37966 };
37967   
37968 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37969     fieldLabel:      '',
37970     inputType:      'hidden',
37971     width:          50,
37972     allowBlank:     true,
37973     labelSeparator: '',
37974     hidden:         true,
37975     itemCls :       'x-form-item-display-none'
37976
37977
37978 });
37979
37980
37981 /*
37982  * Based on:
37983  * Ext JS Library 1.1.1
37984  * Copyright(c) 2006-2007, Ext JS, LLC.
37985  *
37986  * Originally Released Under LGPL - original licence link has changed is not relivant.
37987  *
37988  * Fork - LGPL
37989  * <script type="text/javascript">
37990  */
37991  
37992 /**
37993  * @class Roo.form.TriggerField
37994  * @extends Roo.form.TextField
37995  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37996  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37997  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37998  * for which you can provide a custom implementation.  For example:
37999  * <pre><code>
38000 var trigger = new Roo.form.TriggerField();
38001 trigger.onTriggerClick = myTriggerFn;
38002 trigger.applyTo('my-field');
38003 </code></pre>
38004  *
38005  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
38006  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
38007  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
38008  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
38009  * @constructor
38010  * Create a new TriggerField.
38011  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
38012  * to the base TextField)
38013  */
38014 Roo.form.TriggerField = function(config){
38015     this.mimicing = false;
38016     Roo.form.TriggerField.superclass.constructor.call(this, config);
38017 };
38018
38019 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
38020     /**
38021      * @cfg {String} triggerClass A CSS class to apply to the trigger
38022      */
38023     /**
38024      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38025      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
38026      */
38027     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
38028     /**
38029      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
38030      */
38031     hideTrigger:false,
38032
38033     /** @cfg {Boolean} grow @hide */
38034     /** @cfg {Number} growMin @hide */
38035     /** @cfg {Number} growMax @hide */
38036
38037     /**
38038      * @hide 
38039      * @method
38040      */
38041     autoSize: Roo.emptyFn,
38042     // private
38043     monitorTab : true,
38044     // private
38045     deferHeight : true,
38046
38047     
38048     actionMode : 'wrap',
38049     // private
38050     onResize : function(w, h){
38051         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
38052         if(typeof w == 'number'){
38053             var x = w - this.trigger.getWidth();
38054             this.el.setWidth(this.adjustWidth('input', x));
38055             this.trigger.setStyle('left', x+'px');
38056         }
38057     },
38058
38059     // private
38060     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38061
38062     // private
38063     getResizeEl : function(){
38064         return this.wrap;
38065     },
38066
38067     // private
38068     getPositionEl : function(){
38069         return this.wrap;
38070     },
38071
38072     // private
38073     alignErrorIcon : function(){
38074         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
38075     },
38076
38077     // private
38078     onRender : function(ct, position){
38079         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
38080         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
38081         this.trigger = this.wrap.createChild(this.triggerConfig ||
38082                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
38083         if(this.hideTrigger){
38084             this.trigger.setDisplayed(false);
38085         }
38086         this.initTrigger();
38087         if(!this.width){
38088             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
38089         }
38090     },
38091
38092     // private
38093     initTrigger : function(){
38094         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
38095         this.trigger.addClassOnOver('x-form-trigger-over');
38096         this.trigger.addClassOnClick('x-form-trigger-click');
38097     },
38098
38099     // private
38100     onDestroy : function(){
38101         if(this.trigger){
38102             this.trigger.removeAllListeners();
38103             this.trigger.remove();
38104         }
38105         if(this.wrap){
38106             this.wrap.remove();
38107         }
38108         Roo.form.TriggerField.superclass.onDestroy.call(this);
38109     },
38110
38111     // private
38112     onFocus : function(){
38113         Roo.form.TriggerField.superclass.onFocus.call(this);
38114         if(!this.mimicing){
38115             this.wrap.addClass('x-trigger-wrap-focus');
38116             this.mimicing = true;
38117             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
38118             if(this.monitorTab){
38119                 this.el.on("keydown", this.checkTab, this);
38120             }
38121         }
38122     },
38123
38124     // private
38125     checkTab : function(e){
38126         if(e.getKey() == e.TAB){
38127             this.triggerBlur();
38128         }
38129     },
38130
38131     // private
38132     onBlur : function(){
38133         // do nothing
38134     },
38135
38136     // private
38137     mimicBlur : function(e, t){
38138         if(!this.wrap.contains(t) && this.validateBlur()){
38139             this.triggerBlur();
38140         }
38141     },
38142
38143     // private
38144     triggerBlur : function(){
38145         this.mimicing = false;
38146         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
38147         if(this.monitorTab){
38148             this.el.un("keydown", this.checkTab, this);
38149         }
38150         this.wrap.removeClass('x-trigger-wrap-focus');
38151         Roo.form.TriggerField.superclass.onBlur.call(this);
38152     },
38153
38154     // private
38155     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
38156     validateBlur : function(e, t){
38157         return true;
38158     },
38159
38160     // private
38161     onDisable : function(){
38162         Roo.form.TriggerField.superclass.onDisable.call(this);
38163         if(this.wrap){
38164             this.wrap.addClass('x-item-disabled');
38165         }
38166     },
38167
38168     // private
38169     onEnable : function(){
38170         Roo.form.TriggerField.superclass.onEnable.call(this);
38171         if(this.wrap){
38172             this.wrap.removeClass('x-item-disabled');
38173         }
38174     },
38175
38176     // private
38177     onShow : function(){
38178         var ae = this.getActionEl();
38179         
38180         if(ae){
38181             ae.dom.style.display = '';
38182             ae.dom.style.visibility = 'visible';
38183         }
38184     },
38185
38186     // private
38187     
38188     onHide : function(){
38189         var ae = this.getActionEl();
38190         ae.dom.style.display = 'none';
38191     },
38192
38193     /**
38194      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
38195      * by an implementing function.
38196      * @method
38197      * @param {EventObject} e
38198      */
38199     onTriggerClick : Roo.emptyFn
38200 });
38201
38202 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
38203 // to be extended by an implementing class.  For an example of implementing this class, see the custom
38204 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
38205 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
38206     initComponent : function(){
38207         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
38208
38209         this.triggerConfig = {
38210             tag:'span', cls:'x-form-twin-triggers', cn:[
38211             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
38212             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
38213         ]};
38214     },
38215
38216     getTrigger : function(index){
38217         return this.triggers[index];
38218     },
38219
38220     initTrigger : function(){
38221         var ts = this.trigger.select('.x-form-trigger', true);
38222         this.wrap.setStyle('overflow', 'hidden');
38223         var triggerField = this;
38224         ts.each(function(t, all, index){
38225             t.hide = function(){
38226                 var w = triggerField.wrap.getWidth();
38227                 this.dom.style.display = 'none';
38228                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38229             };
38230             t.show = function(){
38231                 var w = triggerField.wrap.getWidth();
38232                 this.dom.style.display = '';
38233                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38234             };
38235             var triggerIndex = 'Trigger'+(index+1);
38236
38237             if(this['hide'+triggerIndex]){
38238                 t.dom.style.display = 'none';
38239             }
38240             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38241             t.addClassOnOver('x-form-trigger-over');
38242             t.addClassOnClick('x-form-trigger-click');
38243         }, this);
38244         this.triggers = ts.elements;
38245     },
38246
38247     onTrigger1Click : Roo.emptyFn,
38248     onTrigger2Click : Roo.emptyFn
38249 });/*
38250  * Based on:
38251  * Ext JS Library 1.1.1
38252  * Copyright(c) 2006-2007, Ext JS, LLC.
38253  *
38254  * Originally Released Under LGPL - original licence link has changed is not relivant.
38255  *
38256  * Fork - LGPL
38257  * <script type="text/javascript">
38258  */
38259  
38260 /**
38261  * @class Roo.form.TextArea
38262  * @extends Roo.form.TextField
38263  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38264  * support for auto-sizing.
38265  * @constructor
38266  * Creates a new TextArea
38267  * @param {Object} config Configuration options
38268  */
38269 Roo.form.TextArea = function(config){
38270     Roo.form.TextArea.superclass.constructor.call(this, config);
38271     // these are provided exchanges for backwards compat
38272     // minHeight/maxHeight were replaced by growMin/growMax to be
38273     // compatible with TextField growing config values
38274     if(this.minHeight !== undefined){
38275         this.growMin = this.minHeight;
38276     }
38277     if(this.maxHeight !== undefined){
38278         this.growMax = this.maxHeight;
38279     }
38280 };
38281
38282 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38283     /**
38284      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38285      */
38286     growMin : 60,
38287     /**
38288      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38289      */
38290     growMax: 1000,
38291     /**
38292      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38293      * in the field (equivalent to setting overflow: hidden, defaults to false)
38294      */
38295     preventScrollbars: false,
38296     /**
38297      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38298      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38299      */
38300
38301     // private
38302     onRender : function(ct, position){
38303         if(!this.el){
38304             this.defaultAutoCreate = {
38305                 tag: "textarea",
38306                 style:"width:300px;height:60px;",
38307                 autocomplete: "new-password"
38308             };
38309         }
38310         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38311         if(this.grow){
38312             this.textSizeEl = Roo.DomHelper.append(document.body, {
38313                 tag: "pre", cls: "x-form-grow-sizer"
38314             });
38315             if(this.preventScrollbars){
38316                 this.el.setStyle("overflow", "hidden");
38317             }
38318             this.el.setHeight(this.growMin);
38319         }
38320     },
38321
38322     onDestroy : function(){
38323         if(this.textSizeEl){
38324             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38325         }
38326         Roo.form.TextArea.superclass.onDestroy.call(this);
38327     },
38328
38329     // private
38330     onKeyUp : function(e){
38331         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38332             this.autoSize();
38333         }
38334     },
38335
38336     /**
38337      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38338      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38339      */
38340     autoSize : function(){
38341         if(!this.grow || !this.textSizeEl){
38342             return;
38343         }
38344         var el = this.el;
38345         var v = el.dom.value;
38346         var ts = this.textSizeEl;
38347
38348         ts.innerHTML = '';
38349         ts.appendChild(document.createTextNode(v));
38350         v = ts.innerHTML;
38351
38352         Roo.fly(ts).setWidth(this.el.getWidth());
38353         if(v.length < 1){
38354             v = "&#160;&#160;";
38355         }else{
38356             if(Roo.isIE){
38357                 v = v.replace(/\n/g, '<p>&#160;</p>');
38358             }
38359             v += "&#160;\n&#160;";
38360         }
38361         ts.innerHTML = v;
38362         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38363         if(h != this.lastHeight){
38364             this.lastHeight = h;
38365             this.el.setHeight(h);
38366             this.fireEvent("autosize", this, h);
38367         }
38368     }
38369 });/*
38370  * Based on:
38371  * Ext JS Library 1.1.1
38372  * Copyright(c) 2006-2007, Ext JS, LLC.
38373  *
38374  * Originally Released Under LGPL - original licence link has changed is not relivant.
38375  *
38376  * Fork - LGPL
38377  * <script type="text/javascript">
38378  */
38379  
38380
38381 /**
38382  * @class Roo.form.NumberField
38383  * @extends Roo.form.TextField
38384  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38385  * @constructor
38386  * Creates a new NumberField
38387  * @param {Object} config Configuration options
38388  */
38389 Roo.form.NumberField = function(config){
38390     Roo.form.NumberField.superclass.constructor.call(this, config);
38391 };
38392
38393 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38394     /**
38395      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38396      */
38397     fieldClass: "x-form-field x-form-num-field",
38398     /**
38399      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38400      */
38401     allowDecimals : true,
38402     /**
38403      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38404      */
38405     decimalSeparator : ".",
38406     /**
38407      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38408      */
38409     decimalPrecision : 2,
38410     /**
38411      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38412      */
38413     allowNegative : true,
38414     /**
38415      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38416      */
38417     minValue : Number.NEGATIVE_INFINITY,
38418     /**
38419      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38420      */
38421     maxValue : Number.MAX_VALUE,
38422     /**
38423      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38424      */
38425     minText : "The minimum value for this field is {0}",
38426     /**
38427      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38428      */
38429     maxText : "The maximum value for this field is {0}",
38430     /**
38431      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38432      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38433      */
38434     nanText : "{0} is not a valid number",
38435
38436     // private
38437     initEvents : function(){
38438         Roo.form.NumberField.superclass.initEvents.call(this);
38439         var allowed = "0123456789";
38440         if(this.allowDecimals){
38441             allowed += this.decimalSeparator;
38442         }
38443         if(this.allowNegative){
38444             allowed += "-";
38445         }
38446         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38447         var keyPress = function(e){
38448             var k = e.getKey();
38449             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38450                 return;
38451             }
38452             var c = e.getCharCode();
38453             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38454                 e.stopEvent();
38455             }
38456         };
38457         this.el.on("keypress", keyPress, this);
38458     },
38459
38460     // private
38461     validateValue : function(value){
38462         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38463             return false;
38464         }
38465         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38466              return true;
38467         }
38468         var num = this.parseValue(value);
38469         if(isNaN(num)){
38470             this.markInvalid(String.format(this.nanText, value));
38471             return false;
38472         }
38473         if(num < this.minValue){
38474             this.markInvalid(String.format(this.minText, this.minValue));
38475             return false;
38476         }
38477         if(num > this.maxValue){
38478             this.markInvalid(String.format(this.maxText, this.maxValue));
38479             return false;
38480         }
38481         return true;
38482     },
38483
38484     getValue : function(){
38485         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38486     },
38487
38488     // private
38489     parseValue : function(value){
38490         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38491         return isNaN(value) ? '' : value;
38492     },
38493
38494     // private
38495     fixPrecision : function(value){
38496         var nan = isNaN(value);
38497         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38498             return nan ? '' : value;
38499         }
38500         return parseFloat(value).toFixed(this.decimalPrecision);
38501     },
38502
38503     setValue : function(v){
38504         v = this.fixPrecision(v);
38505         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38506     },
38507
38508     // private
38509     decimalPrecisionFcn : function(v){
38510         return Math.floor(v);
38511     },
38512
38513     beforeBlur : function(){
38514         var v = this.parseValue(this.getRawValue());
38515         if(v){
38516             this.setValue(v);
38517         }
38518     }
38519 });/*
38520  * Based on:
38521  * Ext JS Library 1.1.1
38522  * Copyright(c) 2006-2007, Ext JS, LLC.
38523  *
38524  * Originally Released Under LGPL - original licence link has changed is not relivant.
38525  *
38526  * Fork - LGPL
38527  * <script type="text/javascript">
38528  */
38529  
38530 /**
38531  * @class Roo.form.DateField
38532  * @extends Roo.form.TriggerField
38533  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38534 * @constructor
38535 * Create a new DateField
38536 * @param {Object} config
38537  */
38538 Roo.form.DateField = function(config){
38539     Roo.form.DateField.superclass.constructor.call(this, config);
38540     
38541       this.addEvents({
38542          
38543         /**
38544          * @event select
38545          * Fires when a date is selected
38546              * @param {Roo.form.DateField} combo This combo box
38547              * @param {Date} date The date selected
38548              */
38549         'select' : true
38550          
38551     });
38552     
38553     
38554     if(typeof this.minValue == "string") {
38555         this.minValue = this.parseDate(this.minValue);
38556     }
38557     if(typeof this.maxValue == "string") {
38558         this.maxValue = this.parseDate(this.maxValue);
38559     }
38560     this.ddMatch = null;
38561     if(this.disabledDates){
38562         var dd = this.disabledDates;
38563         var re = "(?:";
38564         for(var i = 0; i < dd.length; i++){
38565             re += dd[i];
38566             if(i != dd.length-1) {
38567                 re += "|";
38568             }
38569         }
38570         this.ddMatch = new RegExp(re + ")");
38571     }
38572 };
38573
38574 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38575     /**
38576      * @cfg {String} format
38577      * The default date format string which can be overriden for localization support.  The format must be
38578      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38579      */
38580     format : "m/d/y",
38581     /**
38582      * @cfg {String} altFormats
38583      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38584      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38585      */
38586     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38587     /**
38588      * @cfg {Array} disabledDays
38589      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38590      */
38591     disabledDays : null,
38592     /**
38593      * @cfg {String} disabledDaysText
38594      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38595      */
38596     disabledDaysText : "Disabled",
38597     /**
38598      * @cfg {Array} disabledDates
38599      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38600      * expression so they are very powerful. Some examples:
38601      * <ul>
38602      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38603      * <li>["03/08", "09/16"] would disable those days for every year</li>
38604      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38605      * <li>["03/../2006"] would disable every day in March 2006</li>
38606      * <li>["^03"] would disable every day in every March</li>
38607      * </ul>
38608      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38609      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38610      */
38611     disabledDates : null,
38612     /**
38613      * @cfg {String} disabledDatesText
38614      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38615      */
38616     disabledDatesText : "Disabled",
38617     /**
38618      * @cfg {Date/String} minValue
38619      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38620      * valid format (defaults to null).
38621      */
38622     minValue : null,
38623     /**
38624      * @cfg {Date/String} maxValue
38625      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38626      * valid format (defaults to null).
38627      */
38628     maxValue : null,
38629     /**
38630      * @cfg {String} minText
38631      * The error text to display when the date in the cell is before minValue (defaults to
38632      * 'The date in this field must be after {minValue}').
38633      */
38634     minText : "The date in this field must be equal to or after {0}",
38635     /**
38636      * @cfg {String} maxText
38637      * The error text to display when the date in the cell is after maxValue (defaults to
38638      * 'The date in this field must be before {maxValue}').
38639      */
38640     maxText : "The date in this field must be equal to or before {0}",
38641     /**
38642      * @cfg {String} invalidText
38643      * The error text to display when the date in the field is invalid (defaults to
38644      * '{value} is not a valid date - it must be in the format {format}').
38645      */
38646     invalidText : "{0} is not a valid date - it must be in the format {1}",
38647     /**
38648      * @cfg {String} triggerClass
38649      * An additional CSS class used to style the trigger button.  The trigger will always get the
38650      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38651      * which displays a calendar icon).
38652      */
38653     triggerClass : 'x-form-date-trigger',
38654     
38655
38656     /**
38657      * @cfg {Boolean} useIso
38658      * if enabled, then the date field will use a hidden field to store the 
38659      * real value as iso formated date. default (false)
38660      */ 
38661     useIso : false,
38662     /**
38663      * @cfg {String/Object} autoCreate
38664      * A DomHelper element spec, or true for a default element spec (defaults to
38665      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38666      */ 
38667     // private
38668     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38669     
38670     // private
38671     hiddenField: false,
38672     
38673     onRender : function(ct, position)
38674     {
38675         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38676         if (this.useIso) {
38677             //this.el.dom.removeAttribute('name'); 
38678             Roo.log("Changing name?");
38679             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38680             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38681                     'before', true);
38682             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38683             // prevent input submission
38684             this.hiddenName = this.name;
38685         }
38686             
38687             
38688     },
38689     
38690     // private
38691     validateValue : function(value)
38692     {
38693         value = this.formatDate(value);
38694         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38695             Roo.log('super failed');
38696             return false;
38697         }
38698         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38699              return true;
38700         }
38701         var svalue = value;
38702         value = this.parseDate(value);
38703         if(!value){
38704             Roo.log('parse date failed' + svalue);
38705             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38706             return false;
38707         }
38708         var time = value.getTime();
38709         if(this.minValue && time < this.minValue.getTime()){
38710             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38711             return false;
38712         }
38713         if(this.maxValue && time > this.maxValue.getTime()){
38714             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38715             return false;
38716         }
38717         if(this.disabledDays){
38718             var day = value.getDay();
38719             for(var i = 0; i < this.disabledDays.length; i++) {
38720                 if(day === this.disabledDays[i]){
38721                     this.markInvalid(this.disabledDaysText);
38722                     return false;
38723                 }
38724             }
38725         }
38726         var fvalue = this.formatDate(value);
38727         if(this.ddMatch && this.ddMatch.test(fvalue)){
38728             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38729             return false;
38730         }
38731         return true;
38732     },
38733
38734     // private
38735     // Provides logic to override the default TriggerField.validateBlur which just returns true
38736     validateBlur : function(){
38737         return !this.menu || !this.menu.isVisible();
38738     },
38739     
38740     getName: function()
38741     {
38742         // returns hidden if it's set..
38743         if (!this.rendered) {return ''};
38744         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38745         
38746     },
38747
38748     /**
38749      * Returns the current date value of the date field.
38750      * @return {Date} The date value
38751      */
38752     getValue : function(){
38753         
38754         return  this.hiddenField ?
38755                 this.hiddenField.value :
38756                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38757     },
38758
38759     /**
38760      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38761      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38762      * (the default format used is "m/d/y").
38763      * <br />Usage:
38764      * <pre><code>
38765 //All of these calls set the same date value (May 4, 2006)
38766
38767 //Pass a date object:
38768 var dt = new Date('5/4/06');
38769 dateField.setValue(dt);
38770
38771 //Pass a date string (default format):
38772 dateField.setValue('5/4/06');
38773
38774 //Pass a date string (custom format):
38775 dateField.format = 'Y-m-d';
38776 dateField.setValue('2006-5-4');
38777 </code></pre>
38778      * @param {String/Date} date The date or valid date string
38779      */
38780     setValue : function(date){
38781         if (this.hiddenField) {
38782             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38783         }
38784         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38785         // make sure the value field is always stored as a date..
38786         this.value = this.parseDate(date);
38787         
38788         
38789     },
38790
38791     // private
38792     parseDate : function(value){
38793         if(!value || value instanceof Date){
38794             return value;
38795         }
38796         var v = Date.parseDate(value, this.format);
38797          if (!v && this.useIso) {
38798             v = Date.parseDate(value, 'Y-m-d');
38799         }
38800         if(!v && this.altFormats){
38801             if(!this.altFormatsArray){
38802                 this.altFormatsArray = this.altFormats.split("|");
38803             }
38804             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38805                 v = Date.parseDate(value, this.altFormatsArray[i]);
38806             }
38807         }
38808         return v;
38809     },
38810
38811     // private
38812     formatDate : function(date, fmt){
38813         return (!date || !(date instanceof Date)) ?
38814                date : date.dateFormat(fmt || this.format);
38815     },
38816
38817     // private
38818     menuListeners : {
38819         select: function(m, d){
38820             
38821             this.setValue(d);
38822             this.fireEvent('select', this, d);
38823         },
38824         show : function(){ // retain focus styling
38825             this.onFocus();
38826         },
38827         hide : function(){
38828             this.focus.defer(10, this);
38829             var ml = this.menuListeners;
38830             this.menu.un("select", ml.select,  this);
38831             this.menu.un("show", ml.show,  this);
38832             this.menu.un("hide", ml.hide,  this);
38833         }
38834     },
38835
38836     // private
38837     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38838     onTriggerClick : function(){
38839         if(this.disabled){
38840             return;
38841         }
38842         if(this.menu == null){
38843             this.menu = new Roo.menu.DateMenu();
38844         }
38845         Roo.apply(this.menu.picker,  {
38846             showClear: this.allowBlank,
38847             minDate : this.minValue,
38848             maxDate : this.maxValue,
38849             disabledDatesRE : this.ddMatch,
38850             disabledDatesText : this.disabledDatesText,
38851             disabledDays : this.disabledDays,
38852             disabledDaysText : this.disabledDaysText,
38853             format : this.useIso ? 'Y-m-d' : this.format,
38854             minText : String.format(this.minText, this.formatDate(this.minValue)),
38855             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38856         });
38857         this.menu.on(Roo.apply({}, this.menuListeners, {
38858             scope:this
38859         }));
38860         this.menu.picker.setValue(this.getValue() || new Date());
38861         this.menu.show(this.el, "tl-bl?");
38862     },
38863
38864     beforeBlur : function(){
38865         var v = this.parseDate(this.getRawValue());
38866         if(v){
38867             this.setValue(v);
38868         }
38869     },
38870
38871     /*@
38872      * overide
38873      * 
38874      */
38875     isDirty : function() {
38876         if(this.disabled) {
38877             return false;
38878         }
38879         
38880         if(typeof(this.startValue) === 'undefined'){
38881             return false;
38882         }
38883         
38884         return String(this.getValue()) !== String(this.startValue);
38885         
38886     }
38887 });/*
38888  * Based on:
38889  * Ext JS Library 1.1.1
38890  * Copyright(c) 2006-2007, Ext JS, LLC.
38891  *
38892  * Originally Released Under LGPL - original licence link has changed is not relivant.
38893  *
38894  * Fork - LGPL
38895  * <script type="text/javascript">
38896  */
38897  
38898 /**
38899  * @class Roo.form.MonthField
38900  * @extends Roo.form.TriggerField
38901  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38902 * @constructor
38903 * Create a new MonthField
38904 * @param {Object} config
38905  */
38906 Roo.form.MonthField = function(config){
38907     
38908     Roo.form.MonthField.superclass.constructor.call(this, config);
38909     
38910       this.addEvents({
38911          
38912         /**
38913          * @event select
38914          * Fires when a date is selected
38915              * @param {Roo.form.MonthFieeld} combo This combo box
38916              * @param {Date} date The date selected
38917              */
38918         'select' : true
38919          
38920     });
38921     
38922     
38923     if(typeof this.minValue == "string") {
38924         this.minValue = this.parseDate(this.minValue);
38925     }
38926     if(typeof this.maxValue == "string") {
38927         this.maxValue = this.parseDate(this.maxValue);
38928     }
38929     this.ddMatch = null;
38930     if(this.disabledDates){
38931         var dd = this.disabledDates;
38932         var re = "(?:";
38933         for(var i = 0; i < dd.length; i++){
38934             re += dd[i];
38935             if(i != dd.length-1) {
38936                 re += "|";
38937             }
38938         }
38939         this.ddMatch = new RegExp(re + ")");
38940     }
38941 };
38942
38943 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38944     /**
38945      * @cfg {String} format
38946      * The default date format string which can be overriden for localization support.  The format must be
38947      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38948      */
38949     format : "M Y",
38950     /**
38951      * @cfg {String} altFormats
38952      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38953      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38954      */
38955     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38956     /**
38957      * @cfg {Array} disabledDays
38958      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38959      */
38960     disabledDays : [0,1,2,3,4,5,6],
38961     /**
38962      * @cfg {String} disabledDaysText
38963      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38964      */
38965     disabledDaysText : "Disabled",
38966     /**
38967      * @cfg {Array} disabledDates
38968      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38969      * expression so they are very powerful. Some examples:
38970      * <ul>
38971      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38972      * <li>["03/08", "09/16"] would disable those days for every year</li>
38973      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38974      * <li>["03/../2006"] would disable every day in March 2006</li>
38975      * <li>["^03"] would disable every day in every March</li>
38976      * </ul>
38977      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38978      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38979      */
38980     disabledDates : null,
38981     /**
38982      * @cfg {String} disabledDatesText
38983      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38984      */
38985     disabledDatesText : "Disabled",
38986     /**
38987      * @cfg {Date/String} minValue
38988      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38989      * valid format (defaults to null).
38990      */
38991     minValue : null,
38992     /**
38993      * @cfg {Date/String} maxValue
38994      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38995      * valid format (defaults to null).
38996      */
38997     maxValue : null,
38998     /**
38999      * @cfg {String} minText
39000      * The error text to display when the date in the cell is before minValue (defaults to
39001      * 'The date in this field must be after {minValue}').
39002      */
39003     minText : "The date in this field must be equal to or after {0}",
39004     /**
39005      * @cfg {String} maxTextf
39006      * The error text to display when the date in the cell is after maxValue (defaults to
39007      * 'The date in this field must be before {maxValue}').
39008      */
39009     maxText : "The date in this field must be equal to or before {0}",
39010     /**
39011      * @cfg {String} invalidText
39012      * The error text to display when the date in the field is invalid (defaults to
39013      * '{value} is not a valid date - it must be in the format {format}').
39014      */
39015     invalidText : "{0} is not a valid date - it must be in the format {1}",
39016     /**
39017      * @cfg {String} triggerClass
39018      * An additional CSS class used to style the trigger button.  The trigger will always get the
39019      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
39020      * which displays a calendar icon).
39021      */
39022     triggerClass : 'x-form-date-trigger',
39023     
39024
39025     /**
39026      * @cfg {Boolean} useIso
39027      * if enabled, then the date field will use a hidden field to store the 
39028      * real value as iso formated date. default (true)
39029      */ 
39030     useIso : true,
39031     /**
39032      * @cfg {String/Object} autoCreate
39033      * A DomHelper element spec, or true for a default element spec (defaults to
39034      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
39035      */ 
39036     // private
39037     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
39038     
39039     // private
39040     hiddenField: false,
39041     
39042     hideMonthPicker : false,
39043     
39044     onRender : function(ct, position)
39045     {
39046         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
39047         if (this.useIso) {
39048             this.el.dom.removeAttribute('name'); 
39049             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
39050                     'before', true);
39051             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
39052             // prevent input submission
39053             this.hiddenName = this.name;
39054         }
39055             
39056             
39057     },
39058     
39059     // private
39060     validateValue : function(value)
39061     {
39062         value = this.formatDate(value);
39063         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
39064             return false;
39065         }
39066         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
39067              return true;
39068         }
39069         var svalue = value;
39070         value = this.parseDate(value);
39071         if(!value){
39072             this.markInvalid(String.format(this.invalidText, svalue, this.format));
39073             return false;
39074         }
39075         var time = value.getTime();
39076         if(this.minValue && time < this.minValue.getTime()){
39077             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
39078             return false;
39079         }
39080         if(this.maxValue && time > this.maxValue.getTime()){
39081             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
39082             return false;
39083         }
39084         /*if(this.disabledDays){
39085             var day = value.getDay();
39086             for(var i = 0; i < this.disabledDays.length; i++) {
39087                 if(day === this.disabledDays[i]){
39088                     this.markInvalid(this.disabledDaysText);
39089                     return false;
39090                 }
39091             }
39092         }
39093         */
39094         var fvalue = this.formatDate(value);
39095         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
39096             this.markInvalid(String.format(this.disabledDatesText, fvalue));
39097             return false;
39098         }
39099         */
39100         return true;
39101     },
39102
39103     // private
39104     // Provides logic to override the default TriggerField.validateBlur which just returns true
39105     validateBlur : function(){
39106         return !this.menu || !this.menu.isVisible();
39107     },
39108
39109     /**
39110      * Returns the current date value of the date field.
39111      * @return {Date} The date value
39112      */
39113     getValue : function(){
39114         
39115         
39116         
39117         return  this.hiddenField ?
39118                 this.hiddenField.value :
39119                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
39120     },
39121
39122     /**
39123      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
39124      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
39125      * (the default format used is "m/d/y").
39126      * <br />Usage:
39127      * <pre><code>
39128 //All of these calls set the same date value (May 4, 2006)
39129
39130 //Pass a date object:
39131 var dt = new Date('5/4/06');
39132 monthField.setValue(dt);
39133
39134 //Pass a date string (default format):
39135 monthField.setValue('5/4/06');
39136
39137 //Pass a date string (custom format):
39138 monthField.format = 'Y-m-d';
39139 monthField.setValue('2006-5-4');
39140 </code></pre>
39141      * @param {String/Date} date The date or valid date string
39142      */
39143     setValue : function(date){
39144         Roo.log('month setValue' + date);
39145         // can only be first of month..
39146         
39147         var val = this.parseDate(date);
39148         
39149         if (this.hiddenField) {
39150             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
39151         }
39152         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
39153         this.value = this.parseDate(date);
39154     },
39155
39156     // private
39157     parseDate : function(value){
39158         if(!value || value instanceof Date){
39159             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
39160             return value;
39161         }
39162         var v = Date.parseDate(value, this.format);
39163         if (!v && this.useIso) {
39164             v = Date.parseDate(value, 'Y-m-d');
39165         }
39166         if (v) {
39167             // 
39168             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
39169         }
39170         
39171         
39172         if(!v && this.altFormats){
39173             if(!this.altFormatsArray){
39174                 this.altFormatsArray = this.altFormats.split("|");
39175             }
39176             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
39177                 v = Date.parseDate(value, this.altFormatsArray[i]);
39178             }
39179         }
39180         return v;
39181     },
39182
39183     // private
39184     formatDate : function(date, fmt){
39185         return (!date || !(date instanceof Date)) ?
39186                date : date.dateFormat(fmt || this.format);
39187     },
39188
39189     // private
39190     menuListeners : {
39191         select: function(m, d){
39192             this.setValue(d);
39193             this.fireEvent('select', this, d);
39194         },
39195         show : function(){ // retain focus styling
39196             this.onFocus();
39197         },
39198         hide : function(){
39199             this.focus.defer(10, this);
39200             var ml = this.menuListeners;
39201             this.menu.un("select", ml.select,  this);
39202             this.menu.un("show", ml.show,  this);
39203             this.menu.un("hide", ml.hide,  this);
39204         }
39205     },
39206     // private
39207     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
39208     onTriggerClick : function(){
39209         if(this.disabled){
39210             return;
39211         }
39212         if(this.menu == null){
39213             this.menu = new Roo.menu.DateMenu();
39214            
39215         }
39216         
39217         Roo.apply(this.menu.picker,  {
39218             
39219             showClear: this.allowBlank,
39220             minDate : this.minValue,
39221             maxDate : this.maxValue,
39222             disabledDatesRE : this.ddMatch,
39223             disabledDatesText : this.disabledDatesText,
39224             
39225             format : this.useIso ? 'Y-m-d' : this.format,
39226             minText : String.format(this.minText, this.formatDate(this.minValue)),
39227             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39228             
39229         });
39230          this.menu.on(Roo.apply({}, this.menuListeners, {
39231             scope:this
39232         }));
39233        
39234         
39235         var m = this.menu;
39236         var p = m.picker;
39237         
39238         // hide month picker get's called when we called by 'before hide';
39239         
39240         var ignorehide = true;
39241         p.hideMonthPicker  = function(disableAnim){
39242             if (ignorehide) {
39243                 return;
39244             }
39245              if(this.monthPicker){
39246                 Roo.log("hideMonthPicker called");
39247                 if(disableAnim === true){
39248                     this.monthPicker.hide();
39249                 }else{
39250                     this.monthPicker.slideOut('t', {duration:.2});
39251                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39252                     p.fireEvent("select", this, this.value);
39253                     m.hide();
39254                 }
39255             }
39256         }
39257         
39258         Roo.log('picker set value');
39259         Roo.log(this.getValue());
39260         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39261         m.show(this.el, 'tl-bl?');
39262         ignorehide  = false;
39263         // this will trigger hideMonthPicker..
39264         
39265         
39266         // hidden the day picker
39267         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39268         
39269         
39270         
39271       
39272         
39273         p.showMonthPicker.defer(100, p);
39274     
39275         
39276        
39277     },
39278
39279     beforeBlur : function(){
39280         var v = this.parseDate(this.getRawValue());
39281         if(v){
39282             this.setValue(v);
39283         }
39284     }
39285
39286     /** @cfg {Boolean} grow @hide */
39287     /** @cfg {Number} growMin @hide */
39288     /** @cfg {Number} growMax @hide */
39289     /**
39290      * @hide
39291      * @method autoSize
39292      */
39293 });/*
39294  * Based on:
39295  * Ext JS Library 1.1.1
39296  * Copyright(c) 2006-2007, Ext JS, LLC.
39297  *
39298  * Originally Released Under LGPL - original licence link has changed is not relivant.
39299  *
39300  * Fork - LGPL
39301  * <script type="text/javascript">
39302  */
39303  
39304
39305 /**
39306  * @class Roo.form.ComboBox
39307  * @extends Roo.form.TriggerField
39308  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39309  * @constructor
39310  * Create a new ComboBox.
39311  * @param {Object} config Configuration options
39312  */
39313 Roo.form.ComboBox = function(config){
39314     Roo.form.ComboBox.superclass.constructor.call(this, config);
39315     this.addEvents({
39316         /**
39317          * @event expand
39318          * Fires when the dropdown list is expanded
39319              * @param {Roo.form.ComboBox} combo This combo box
39320              */
39321         'expand' : true,
39322         /**
39323          * @event collapse
39324          * Fires when the dropdown list is collapsed
39325              * @param {Roo.form.ComboBox} combo This combo box
39326              */
39327         'collapse' : true,
39328         /**
39329          * @event beforeselect
39330          * Fires before a list item is selected. Return false to cancel the selection.
39331              * @param {Roo.form.ComboBox} combo This combo box
39332              * @param {Roo.data.Record} record The data record returned from the underlying store
39333              * @param {Number} index The index of the selected item in the dropdown list
39334              */
39335         'beforeselect' : true,
39336         /**
39337          * @event select
39338          * Fires when a list item is selected
39339              * @param {Roo.form.ComboBox} combo This combo box
39340              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39341              * @param {Number} index The index of the selected item in the dropdown list
39342              */
39343         'select' : true,
39344         /**
39345          * @event beforequery
39346          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39347          * The event object passed has these properties:
39348              * @param {Roo.form.ComboBox} combo This combo box
39349              * @param {String} query The query
39350              * @param {Boolean} forceAll true to force "all" query
39351              * @param {Boolean} cancel true to cancel the query
39352              * @param {Object} e The query event object
39353              */
39354         'beforequery': true,
39355          /**
39356          * @event add
39357          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39358              * @param {Roo.form.ComboBox} combo This combo box
39359              */
39360         'add' : true,
39361         /**
39362          * @event edit
39363          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39364              * @param {Roo.form.ComboBox} combo This combo box
39365              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39366              */
39367         'edit' : true
39368         
39369         
39370     });
39371     if(this.transform){
39372         this.allowDomMove = false;
39373         var s = Roo.getDom(this.transform);
39374         if(!this.hiddenName){
39375             this.hiddenName = s.name;
39376         }
39377         if(!this.store){
39378             this.mode = 'local';
39379             var d = [], opts = s.options;
39380             for(var i = 0, len = opts.length;i < len; i++){
39381                 var o = opts[i];
39382                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39383                 if(o.selected) {
39384                     this.value = value;
39385                 }
39386                 d.push([value, o.text]);
39387             }
39388             this.store = new Roo.data.SimpleStore({
39389                 'id': 0,
39390                 fields: ['value', 'text'],
39391                 data : d
39392             });
39393             this.valueField = 'value';
39394             this.displayField = 'text';
39395         }
39396         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39397         if(!this.lazyRender){
39398             this.target = true;
39399             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39400             s.parentNode.removeChild(s); // remove it
39401             this.render(this.el.parentNode);
39402         }else{
39403             s.parentNode.removeChild(s); // remove it
39404         }
39405
39406     }
39407     if (this.store) {
39408         this.store = Roo.factory(this.store, Roo.data);
39409     }
39410     
39411     this.selectedIndex = -1;
39412     if(this.mode == 'local'){
39413         if(config.queryDelay === undefined){
39414             this.queryDelay = 10;
39415         }
39416         if(config.minChars === undefined){
39417             this.minChars = 0;
39418         }
39419     }
39420 };
39421
39422 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39423     /**
39424      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39425      */
39426     /**
39427      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39428      * rendering into an Roo.Editor, defaults to false)
39429      */
39430     /**
39431      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39432      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39433      */
39434     /**
39435      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39436      */
39437     /**
39438      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39439      * the dropdown list (defaults to undefined, with no header element)
39440      */
39441
39442      /**
39443      * @cfg {String/Roo.Template} tpl The template to use to render the output
39444      */
39445      
39446     // private
39447     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39448     /**
39449      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39450      */
39451     listWidth: undefined,
39452     /**
39453      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39454      * mode = 'remote' or 'text' if mode = 'local')
39455      */
39456     displayField: undefined,
39457     /**
39458      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39459      * mode = 'remote' or 'value' if mode = 'local'). 
39460      * Note: use of a valueField requires the user make a selection
39461      * in order for a value to be mapped.
39462      */
39463     valueField: undefined,
39464     
39465     
39466     /**
39467      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39468      * field's data value (defaults to the underlying DOM element's name)
39469      */
39470     hiddenName: undefined,
39471     /**
39472      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39473      */
39474     listClass: '',
39475     /**
39476      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39477      */
39478     selectedClass: 'x-combo-selected',
39479     /**
39480      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39481      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39482      * which displays a downward arrow icon).
39483      */
39484     triggerClass : 'x-form-arrow-trigger',
39485     /**
39486      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39487      */
39488     shadow:'sides',
39489     /**
39490      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39491      * anchor positions (defaults to 'tl-bl')
39492      */
39493     listAlign: 'tl-bl?',
39494     /**
39495      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39496      */
39497     maxHeight: 300,
39498     /**
39499      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39500      * query specified by the allQuery config option (defaults to 'query')
39501      */
39502     triggerAction: 'query',
39503     /**
39504      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39505      * (defaults to 4, does not apply if editable = false)
39506      */
39507     minChars : 4,
39508     /**
39509      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39510      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39511      */
39512     typeAhead: false,
39513     /**
39514      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39515      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39516      */
39517     queryDelay: 500,
39518     /**
39519      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39520      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39521      */
39522     pageSize: 0,
39523     /**
39524      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39525      * when editable = true (defaults to false)
39526      */
39527     selectOnFocus:false,
39528     /**
39529      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39530      */
39531     queryParam: 'query',
39532     /**
39533      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39534      * when mode = 'remote' (defaults to 'Loading...')
39535      */
39536     loadingText: 'Loading...',
39537     /**
39538      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39539      */
39540     resizable: false,
39541     /**
39542      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39543      */
39544     handleHeight : 8,
39545     /**
39546      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39547      * traditional select (defaults to true)
39548      */
39549     editable: true,
39550     /**
39551      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39552      */
39553     allQuery: '',
39554     /**
39555      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39556      */
39557     mode: 'remote',
39558     /**
39559      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39560      * listWidth has a higher value)
39561      */
39562     minListWidth : 70,
39563     /**
39564      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39565      * allow the user to set arbitrary text into the field (defaults to false)
39566      */
39567     forceSelection:false,
39568     /**
39569      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39570      * if typeAhead = true (defaults to 250)
39571      */
39572     typeAheadDelay : 250,
39573     /**
39574      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39575      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39576      */
39577     valueNotFoundText : undefined,
39578     /**
39579      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39580      */
39581     blockFocus : false,
39582     
39583     /**
39584      * @cfg {Boolean} disableClear Disable showing of clear button.
39585      */
39586     disableClear : false,
39587     /**
39588      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39589      */
39590     alwaysQuery : false,
39591     
39592     //private
39593     addicon : false,
39594     editicon: false,
39595     
39596     // element that contains real text value.. (when hidden is used..)
39597      
39598     // private
39599     onRender : function(ct, position){
39600         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39601         if(this.hiddenName){
39602             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39603                     'before', true);
39604             this.hiddenField.value =
39605                 this.hiddenValue !== undefined ? this.hiddenValue :
39606                 this.value !== undefined ? this.value : '';
39607
39608             // prevent input submission
39609             this.el.dom.removeAttribute('name');
39610              
39611              
39612         }
39613         if(Roo.isGecko){
39614             this.el.dom.setAttribute('autocomplete', 'off');
39615         }
39616
39617         var cls = 'x-combo-list';
39618
39619         this.list = new Roo.Layer({
39620             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39621         });
39622
39623         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39624         this.list.setWidth(lw);
39625         this.list.swallowEvent('mousewheel');
39626         this.assetHeight = 0;
39627
39628         if(this.title){
39629             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39630             this.assetHeight += this.header.getHeight();
39631         }
39632
39633         this.innerList = this.list.createChild({cls:cls+'-inner'});
39634         this.innerList.on('mouseover', this.onViewOver, this);
39635         this.innerList.on('mousemove', this.onViewMove, this);
39636         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39637         
39638         if(this.allowBlank && !this.pageSize && !this.disableClear){
39639             this.footer = this.list.createChild({cls:cls+'-ft'});
39640             this.pageTb = new Roo.Toolbar(this.footer);
39641            
39642         }
39643         if(this.pageSize){
39644             this.footer = this.list.createChild({cls:cls+'-ft'});
39645             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39646                     {pageSize: this.pageSize});
39647             
39648         }
39649         
39650         if (this.pageTb && this.allowBlank && !this.disableClear) {
39651             var _this = this;
39652             this.pageTb.add(new Roo.Toolbar.Fill(), {
39653                 cls: 'x-btn-icon x-btn-clear',
39654                 text: '&#160;',
39655                 handler: function()
39656                 {
39657                     _this.collapse();
39658                     _this.clearValue();
39659                     _this.onSelect(false, -1);
39660                 }
39661             });
39662         }
39663         if (this.footer) {
39664             this.assetHeight += this.footer.getHeight();
39665         }
39666         
39667
39668         if(!this.tpl){
39669             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39670         }
39671
39672         this.view = new Roo.View(this.innerList, this.tpl, {
39673             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39674         });
39675
39676         this.view.on('click', this.onViewClick, this);
39677
39678         this.store.on('beforeload', this.onBeforeLoad, this);
39679         this.store.on('load', this.onLoad, this);
39680         this.store.on('loadexception', this.onLoadException, this);
39681
39682         if(this.resizable){
39683             this.resizer = new Roo.Resizable(this.list,  {
39684                pinned:true, handles:'se'
39685             });
39686             this.resizer.on('resize', function(r, w, h){
39687                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39688                 this.listWidth = w;
39689                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39690                 this.restrictHeight();
39691             }, this);
39692             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39693         }
39694         if(!this.editable){
39695             this.editable = true;
39696             this.setEditable(false);
39697         }  
39698         
39699         
39700         if (typeof(this.events.add.listeners) != 'undefined') {
39701             
39702             this.addicon = this.wrap.createChild(
39703                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39704        
39705             this.addicon.on('click', function(e) {
39706                 this.fireEvent('add', this);
39707             }, this);
39708         }
39709         if (typeof(this.events.edit.listeners) != 'undefined') {
39710             
39711             this.editicon = this.wrap.createChild(
39712                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39713             if (this.addicon) {
39714                 this.editicon.setStyle('margin-left', '40px');
39715             }
39716             this.editicon.on('click', function(e) {
39717                 
39718                 // we fire even  if inothing is selected..
39719                 this.fireEvent('edit', this, this.lastData );
39720                 
39721             }, this);
39722         }
39723         
39724         
39725         
39726     },
39727
39728     // private
39729     initEvents : function(){
39730         Roo.form.ComboBox.superclass.initEvents.call(this);
39731
39732         this.keyNav = new Roo.KeyNav(this.el, {
39733             "up" : function(e){
39734                 this.inKeyMode = true;
39735                 this.selectPrev();
39736             },
39737
39738             "down" : function(e){
39739                 if(!this.isExpanded()){
39740                     this.onTriggerClick();
39741                 }else{
39742                     this.inKeyMode = true;
39743                     this.selectNext();
39744                 }
39745             },
39746
39747             "enter" : function(e){
39748                 this.onViewClick();
39749                 //return true;
39750             },
39751
39752             "esc" : function(e){
39753                 this.collapse();
39754             },
39755
39756             "tab" : function(e){
39757                 this.onViewClick(false);
39758                 this.fireEvent("specialkey", this, e);
39759                 return true;
39760             },
39761
39762             scope : this,
39763
39764             doRelay : function(foo, bar, hname){
39765                 if(hname == 'down' || this.scope.isExpanded()){
39766                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39767                 }
39768                 return true;
39769             },
39770
39771             forceKeyDown: true
39772         });
39773         this.queryDelay = Math.max(this.queryDelay || 10,
39774                 this.mode == 'local' ? 10 : 250);
39775         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39776         if(this.typeAhead){
39777             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39778         }
39779         if(this.editable !== false){
39780             this.el.on("keyup", this.onKeyUp, this);
39781         }
39782         if(this.forceSelection){
39783             this.on('blur', this.doForce, this);
39784         }
39785     },
39786
39787     onDestroy : function(){
39788         if(this.view){
39789             this.view.setStore(null);
39790             this.view.el.removeAllListeners();
39791             this.view.el.remove();
39792             this.view.purgeListeners();
39793         }
39794         if(this.list){
39795             this.list.destroy();
39796         }
39797         if(this.store){
39798             this.store.un('beforeload', this.onBeforeLoad, this);
39799             this.store.un('load', this.onLoad, this);
39800             this.store.un('loadexception', this.onLoadException, this);
39801         }
39802         Roo.form.ComboBox.superclass.onDestroy.call(this);
39803     },
39804
39805     // private
39806     fireKey : function(e){
39807         if(e.isNavKeyPress() && !this.list.isVisible()){
39808             this.fireEvent("specialkey", this, e);
39809         }
39810     },
39811
39812     // private
39813     onResize: function(w, h){
39814         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39815         
39816         if(typeof w != 'number'){
39817             // we do not handle it!?!?
39818             return;
39819         }
39820         var tw = this.trigger.getWidth();
39821         tw += this.addicon ? this.addicon.getWidth() : 0;
39822         tw += this.editicon ? this.editicon.getWidth() : 0;
39823         var x = w - tw;
39824         this.el.setWidth( this.adjustWidth('input', x));
39825             
39826         this.trigger.setStyle('left', x+'px');
39827         
39828         if(this.list && this.listWidth === undefined){
39829             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39830             this.list.setWidth(lw);
39831             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39832         }
39833         
39834     
39835         
39836     },
39837
39838     /**
39839      * Allow or prevent the user from directly editing the field text.  If false is passed,
39840      * the user will only be able to select from the items defined in the dropdown list.  This method
39841      * is the runtime equivalent of setting the 'editable' config option at config time.
39842      * @param {Boolean} value True to allow the user to directly edit the field text
39843      */
39844     setEditable : function(value){
39845         if(value == this.editable){
39846             return;
39847         }
39848         this.editable = value;
39849         if(!value){
39850             this.el.dom.setAttribute('readOnly', true);
39851             this.el.on('mousedown', this.onTriggerClick,  this);
39852             this.el.addClass('x-combo-noedit');
39853         }else{
39854             this.el.dom.setAttribute('readOnly', false);
39855             this.el.un('mousedown', this.onTriggerClick,  this);
39856             this.el.removeClass('x-combo-noedit');
39857         }
39858     },
39859
39860     // private
39861     onBeforeLoad : function(){
39862         if(!this.hasFocus){
39863             return;
39864         }
39865         this.innerList.update(this.loadingText ?
39866                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39867         this.restrictHeight();
39868         this.selectedIndex = -1;
39869     },
39870
39871     // private
39872     onLoad : function(){
39873         if(!this.hasFocus){
39874             return;
39875         }
39876         if(this.store.getCount() > 0){
39877             this.expand();
39878             this.restrictHeight();
39879             if(this.lastQuery == this.allQuery){
39880                 if(this.editable){
39881                     this.el.dom.select();
39882                 }
39883                 if(!this.selectByValue(this.value, true)){
39884                     this.select(0, true);
39885                 }
39886             }else{
39887                 this.selectNext();
39888                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39889                     this.taTask.delay(this.typeAheadDelay);
39890                 }
39891             }
39892         }else{
39893             this.onEmptyResults();
39894         }
39895         //this.el.focus();
39896     },
39897     // private
39898     onLoadException : function()
39899     {
39900         this.collapse();
39901         Roo.log(this.store.reader.jsonData);
39902         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39903             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39904         }
39905         
39906         
39907     },
39908     // private
39909     onTypeAhead : function(){
39910         if(this.store.getCount() > 0){
39911             var r = this.store.getAt(0);
39912             var newValue = r.data[this.displayField];
39913             var len = newValue.length;
39914             var selStart = this.getRawValue().length;
39915             if(selStart != len){
39916                 this.setRawValue(newValue);
39917                 this.selectText(selStart, newValue.length);
39918             }
39919         }
39920     },
39921
39922     // private
39923     onSelect : function(record, index){
39924         if(this.fireEvent('beforeselect', this, record, index) !== false){
39925             this.setFromData(index > -1 ? record.data : false);
39926             this.collapse();
39927             this.fireEvent('select', this, record, index);
39928         }
39929     },
39930
39931     /**
39932      * Returns the currently selected field value or empty string if no value is set.
39933      * @return {String} value The selected value
39934      */
39935     getValue : function(){
39936         if(this.valueField){
39937             return typeof this.value != 'undefined' ? this.value : '';
39938         }
39939         return Roo.form.ComboBox.superclass.getValue.call(this);
39940     },
39941
39942     /**
39943      * Clears any text/value currently set in the field
39944      */
39945     clearValue : function(){
39946         if(this.hiddenField){
39947             this.hiddenField.value = '';
39948         }
39949         this.value = '';
39950         this.setRawValue('');
39951         this.lastSelectionText = '';
39952         
39953     },
39954
39955     /**
39956      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39957      * will be displayed in the field.  If the value does not match the data value of an existing item,
39958      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39959      * Otherwise the field will be blank (although the value will still be set).
39960      * @param {String} value The value to match
39961      */
39962     setValue : function(v){
39963         var text = v;
39964         if(this.valueField){
39965             var r = this.findRecord(this.valueField, v);
39966             if(r){
39967                 text = r.data[this.displayField];
39968             }else if(this.valueNotFoundText !== undefined){
39969                 text = this.valueNotFoundText;
39970             }
39971         }
39972         this.lastSelectionText = text;
39973         if(this.hiddenField){
39974             this.hiddenField.value = v;
39975         }
39976         Roo.form.ComboBox.superclass.setValue.call(this, text);
39977         this.value = v;
39978     },
39979     /**
39980      * @property {Object} the last set data for the element
39981      */
39982     
39983     lastData : false,
39984     /**
39985      * Sets the value of the field based on a object which is related to the record format for the store.
39986      * @param {Object} value the value to set as. or false on reset?
39987      */
39988     setFromData : function(o){
39989         var dv = ''; // display value
39990         var vv = ''; // value value..
39991         this.lastData = o;
39992         if (this.displayField) {
39993             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39994         } else {
39995             // this is an error condition!!!
39996             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39997         }
39998         
39999         if(this.valueField){
40000             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
40001         }
40002         if(this.hiddenField){
40003             this.hiddenField.value = vv;
40004             
40005             this.lastSelectionText = dv;
40006             Roo.form.ComboBox.superclass.setValue.call(this, dv);
40007             this.value = vv;
40008             return;
40009         }
40010         // no hidden field.. - we store the value in 'value', but still display
40011         // display field!!!!
40012         this.lastSelectionText = dv;
40013         Roo.form.ComboBox.superclass.setValue.call(this, dv);
40014         this.value = vv;
40015         
40016         
40017     },
40018     // private
40019     reset : function(){
40020         // overridden so that last data is reset..
40021         this.setValue(this.resetValue);
40022         this.clearInvalid();
40023         this.lastData = false;
40024         if (this.view) {
40025             this.view.clearSelections();
40026         }
40027     },
40028     // private
40029     findRecord : function(prop, value){
40030         var record;
40031         if(this.store.getCount() > 0){
40032             this.store.each(function(r){
40033                 if(r.data[prop] == value){
40034                     record = r;
40035                     return false;
40036                 }
40037                 return true;
40038             });
40039         }
40040         return record;
40041     },
40042     
40043     getName: function()
40044     {
40045         // returns hidden if it's set..
40046         if (!this.rendered) {return ''};
40047         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40048         
40049     },
40050     // private
40051     onViewMove : function(e, t){
40052         this.inKeyMode = false;
40053     },
40054
40055     // private
40056     onViewOver : function(e, t){
40057         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
40058             return;
40059         }
40060         var item = this.view.findItemFromChild(t);
40061         if(item){
40062             var index = this.view.indexOf(item);
40063             this.select(index, false);
40064         }
40065     },
40066
40067     // private
40068     onViewClick : function(doFocus)
40069     {
40070         var index = this.view.getSelectedIndexes()[0];
40071         var r = this.store.getAt(index);
40072         if(r){
40073             this.onSelect(r, index);
40074         }
40075         if(doFocus !== false && !this.blockFocus){
40076             this.el.focus();
40077         }
40078     },
40079
40080     // private
40081     restrictHeight : function(){
40082         this.innerList.dom.style.height = '';
40083         var inner = this.innerList.dom;
40084         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
40085         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
40086         this.list.beginUpdate();
40087         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
40088         this.list.alignTo(this.el, this.listAlign);
40089         this.list.endUpdate();
40090     },
40091
40092     // private
40093     onEmptyResults : function(){
40094         this.collapse();
40095     },
40096
40097     /**
40098      * Returns true if the dropdown list is expanded, else false.
40099      */
40100     isExpanded : function(){
40101         return this.list.isVisible();
40102     },
40103
40104     /**
40105      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
40106      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40107      * @param {String} value The data value of the item to select
40108      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40109      * selected item if it is not currently in view (defaults to true)
40110      * @return {Boolean} True if the value matched an item in the list, else false
40111      */
40112     selectByValue : function(v, scrollIntoView){
40113         if(v !== undefined && v !== null){
40114             var r = this.findRecord(this.valueField || this.displayField, v);
40115             if(r){
40116                 this.select(this.store.indexOf(r), scrollIntoView);
40117                 return true;
40118             }
40119         }
40120         return false;
40121     },
40122
40123     /**
40124      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
40125      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40126      * @param {Number} index The zero-based index of the list item to select
40127      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40128      * selected item if it is not currently in view (defaults to true)
40129      */
40130     select : function(index, scrollIntoView){
40131         this.selectedIndex = index;
40132         this.view.select(index);
40133         if(scrollIntoView !== false){
40134             var el = this.view.getNode(index);
40135             if(el){
40136                 this.innerList.scrollChildIntoView(el, false);
40137             }
40138         }
40139     },
40140
40141     // private
40142     selectNext : function(){
40143         var ct = this.store.getCount();
40144         if(ct > 0){
40145             if(this.selectedIndex == -1){
40146                 this.select(0);
40147             }else if(this.selectedIndex < ct-1){
40148                 this.select(this.selectedIndex+1);
40149             }
40150         }
40151     },
40152
40153     // private
40154     selectPrev : function(){
40155         var ct = this.store.getCount();
40156         if(ct > 0){
40157             if(this.selectedIndex == -1){
40158                 this.select(0);
40159             }else if(this.selectedIndex != 0){
40160                 this.select(this.selectedIndex-1);
40161             }
40162         }
40163     },
40164
40165     // private
40166     onKeyUp : function(e){
40167         if(this.editable !== false && !e.isSpecialKey()){
40168             this.lastKey = e.getKey();
40169             this.dqTask.delay(this.queryDelay);
40170         }
40171     },
40172
40173     // private
40174     validateBlur : function(){
40175         return !this.list || !this.list.isVisible();   
40176     },
40177
40178     // private
40179     initQuery : function(){
40180         this.doQuery(this.getRawValue());
40181     },
40182
40183     // private
40184     doForce : function(){
40185         if(this.el.dom.value.length > 0){
40186             this.el.dom.value =
40187                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
40188              
40189         }
40190     },
40191
40192     /**
40193      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
40194      * query allowing the query action to be canceled if needed.
40195      * @param {String} query The SQL query to execute
40196      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
40197      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
40198      * saved in the current store (defaults to false)
40199      */
40200     doQuery : function(q, forceAll){
40201         if(q === undefined || q === null){
40202             q = '';
40203         }
40204         var qe = {
40205             query: q,
40206             forceAll: forceAll,
40207             combo: this,
40208             cancel:false
40209         };
40210         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
40211             return false;
40212         }
40213         q = qe.query;
40214         forceAll = qe.forceAll;
40215         if(forceAll === true || (q.length >= this.minChars)){
40216             if(this.lastQuery != q || this.alwaysQuery){
40217                 this.lastQuery = q;
40218                 if(this.mode == 'local'){
40219                     this.selectedIndex = -1;
40220                     if(forceAll){
40221                         this.store.clearFilter();
40222                     }else{
40223                         this.store.filter(this.displayField, q);
40224                     }
40225                     this.onLoad();
40226                 }else{
40227                     this.store.baseParams[this.queryParam] = q;
40228                     this.store.load({
40229                         params: this.getParams(q)
40230                     });
40231                     this.expand();
40232                 }
40233             }else{
40234                 this.selectedIndex = -1;
40235                 this.onLoad();   
40236             }
40237         }
40238     },
40239
40240     // private
40241     getParams : function(q){
40242         var p = {};
40243         //p[this.queryParam] = q;
40244         if(this.pageSize){
40245             p.start = 0;
40246             p.limit = this.pageSize;
40247         }
40248         return p;
40249     },
40250
40251     /**
40252      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40253      */
40254     collapse : function(){
40255         if(!this.isExpanded()){
40256             return;
40257         }
40258         this.list.hide();
40259         Roo.get(document).un('mousedown', this.collapseIf, this);
40260         Roo.get(document).un('mousewheel', this.collapseIf, this);
40261         if (!this.editable) {
40262             Roo.get(document).un('keydown', this.listKeyPress, this);
40263         }
40264         this.fireEvent('collapse', this);
40265     },
40266
40267     // private
40268     collapseIf : function(e){
40269         if(!e.within(this.wrap) && !e.within(this.list)){
40270             this.collapse();
40271         }
40272     },
40273
40274     /**
40275      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40276      */
40277     expand : function(){
40278         if(this.isExpanded() || !this.hasFocus){
40279             return;
40280         }
40281         this.list.alignTo(this.el, this.listAlign);
40282         this.list.show();
40283         Roo.get(document).on('mousedown', this.collapseIf, this);
40284         Roo.get(document).on('mousewheel', this.collapseIf, this);
40285         if (!this.editable) {
40286             Roo.get(document).on('keydown', this.listKeyPress, this);
40287         }
40288         
40289         this.fireEvent('expand', this);
40290     },
40291
40292     // private
40293     // Implements the default empty TriggerField.onTriggerClick function
40294     onTriggerClick : function(){
40295         if(this.disabled){
40296             return;
40297         }
40298         if(this.isExpanded()){
40299             this.collapse();
40300             if (!this.blockFocus) {
40301                 this.el.focus();
40302             }
40303             
40304         }else {
40305             this.hasFocus = true;
40306             if(this.triggerAction == 'all') {
40307                 this.doQuery(this.allQuery, true);
40308             } else {
40309                 this.doQuery(this.getRawValue());
40310             }
40311             if (!this.blockFocus) {
40312                 this.el.focus();
40313             }
40314         }
40315     },
40316     listKeyPress : function(e)
40317     {
40318         //Roo.log('listkeypress');
40319         // scroll to first matching element based on key pres..
40320         if (e.isSpecialKey()) {
40321             return false;
40322         }
40323         var k = String.fromCharCode(e.getKey()).toUpperCase();
40324         //Roo.log(k);
40325         var match  = false;
40326         var csel = this.view.getSelectedNodes();
40327         var cselitem = false;
40328         if (csel.length) {
40329             var ix = this.view.indexOf(csel[0]);
40330             cselitem  = this.store.getAt(ix);
40331             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40332                 cselitem = false;
40333             }
40334             
40335         }
40336         
40337         this.store.each(function(v) { 
40338             if (cselitem) {
40339                 // start at existing selection.
40340                 if (cselitem.id == v.id) {
40341                     cselitem = false;
40342                 }
40343                 return;
40344             }
40345                 
40346             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40347                 match = this.store.indexOf(v);
40348                 return false;
40349             }
40350         }, this);
40351         
40352         if (match === false) {
40353             return true; // no more action?
40354         }
40355         // scroll to?
40356         this.view.select(match);
40357         var sn = Roo.get(this.view.getSelectedNodes()[0]);
40358         sn.scrollIntoView(sn.dom.parentNode, false);
40359     }
40360
40361     /** 
40362     * @cfg {Boolean} grow 
40363     * @hide 
40364     */
40365     /** 
40366     * @cfg {Number} growMin 
40367     * @hide 
40368     */
40369     /** 
40370     * @cfg {Number} growMax 
40371     * @hide 
40372     */
40373     /**
40374      * @hide
40375      * @method autoSize
40376      */
40377 });/*
40378  * Copyright(c) 2010-2012, Roo J Solutions Limited
40379  *
40380  * Licence LGPL
40381  *
40382  */
40383
40384 /**
40385  * @class Roo.form.ComboBoxArray
40386  * @extends Roo.form.TextField
40387  * A facebook style adder... for lists of email / people / countries  etc...
40388  * pick multiple items from a combo box, and shows each one.
40389  *
40390  *  Fred [x]  Brian [x]  [Pick another |v]
40391  *
40392  *
40393  *  For this to work: it needs various extra information
40394  *    - normal combo problay has
40395  *      name, hiddenName
40396  *    + displayField, valueField
40397  *
40398  *    For our purpose...
40399  *
40400  *
40401  *   If we change from 'extends' to wrapping...
40402  *   
40403  *  
40404  *
40405  
40406  
40407  * @constructor
40408  * Create a new ComboBoxArray.
40409  * @param {Object} config Configuration options
40410  */
40411  
40412
40413 Roo.form.ComboBoxArray = function(config)
40414 {
40415     this.addEvents({
40416         /**
40417          * @event beforeremove
40418          * Fires before remove the value from the list
40419              * @param {Roo.form.ComboBoxArray} _self This combo box array
40420              * @param {Roo.form.ComboBoxArray.Item} item removed item
40421              */
40422         'beforeremove' : true,
40423         /**
40424          * @event remove
40425          * Fires when remove the value from the list
40426              * @param {Roo.form.ComboBoxArray} _self This combo box array
40427              * @param {Roo.form.ComboBoxArray.Item} item removed item
40428              */
40429         'remove' : true
40430         
40431         
40432     });
40433     
40434     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40435     
40436     this.items = new Roo.util.MixedCollection(false);
40437     
40438     // construct the child combo...
40439     
40440     
40441     
40442     
40443    
40444     
40445 }
40446
40447  
40448 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40449
40450     /**
40451      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40452      */
40453     
40454     lastData : false,
40455     
40456     // behavies liek a hiddne field
40457     inputType:      'hidden',
40458     /**
40459      * @cfg {Number} width The width of the box that displays the selected element
40460      */ 
40461     width:          300,
40462
40463     
40464     
40465     /**
40466      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40467      */
40468     name : false,
40469     /**
40470      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40471      */
40472     hiddenName : false,
40473     
40474     
40475     // private the array of items that are displayed..
40476     items  : false,
40477     // private - the hidden field el.
40478     hiddenEl : false,
40479     // private - the filed el..
40480     el : false,
40481     
40482     //validateValue : function() { return true; }, // all values are ok!
40483     //onAddClick: function() { },
40484     
40485     onRender : function(ct, position) 
40486     {
40487         
40488         // create the standard hidden element
40489         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40490         
40491         
40492         // give fake names to child combo;
40493         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40494         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40495         
40496         this.combo = Roo.factory(this.combo, Roo.form);
40497         this.combo.onRender(ct, position);
40498         if (typeof(this.combo.width) != 'undefined') {
40499             this.combo.onResize(this.combo.width,0);
40500         }
40501         
40502         this.combo.initEvents();
40503         
40504         // assigned so form know we need to do this..
40505         this.store          = this.combo.store;
40506         this.valueField     = this.combo.valueField;
40507         this.displayField   = this.combo.displayField ;
40508         
40509         
40510         this.combo.wrap.addClass('x-cbarray-grp');
40511         
40512         var cbwrap = this.combo.wrap.createChild(
40513             {tag: 'div', cls: 'x-cbarray-cb'},
40514             this.combo.el.dom
40515         );
40516         
40517              
40518         this.hiddenEl = this.combo.wrap.createChild({
40519             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40520         });
40521         this.el = this.combo.wrap.createChild({
40522             tag: 'input',  type:'hidden' , name: this.name, value : ''
40523         });
40524          //   this.el.dom.removeAttribute("name");
40525         
40526         
40527         this.outerWrap = this.combo.wrap;
40528         this.wrap = cbwrap;
40529         
40530         this.outerWrap.setWidth(this.width);
40531         this.outerWrap.dom.removeChild(this.el.dom);
40532         
40533         this.wrap.dom.appendChild(this.el.dom);
40534         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40535         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40536         
40537         this.combo.trigger.setStyle('position','relative');
40538         this.combo.trigger.setStyle('left', '0px');
40539         this.combo.trigger.setStyle('top', '2px');
40540         
40541         this.combo.el.setStyle('vertical-align', 'text-bottom');
40542         
40543         //this.trigger.setStyle('vertical-align', 'top');
40544         
40545         // this should use the code from combo really... on('add' ....)
40546         if (this.adder) {
40547             
40548         
40549             this.adder = this.outerWrap.createChild(
40550                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40551             var _t = this;
40552             this.adder.on('click', function(e) {
40553                 _t.fireEvent('adderclick', this, e);
40554             }, _t);
40555         }
40556         //var _t = this;
40557         //this.adder.on('click', this.onAddClick, _t);
40558         
40559         
40560         this.combo.on('select', function(cb, rec, ix) {
40561             this.addItem(rec.data);
40562             
40563             cb.setValue('');
40564             cb.el.dom.value = '';
40565             //cb.lastData = rec.data;
40566             // add to list
40567             
40568         }, this);
40569         
40570         
40571     },
40572     
40573     
40574     getName: function()
40575     {
40576         // returns hidden if it's set..
40577         if (!this.rendered) {return ''};
40578         return  this.hiddenName ? this.hiddenName : this.name;
40579         
40580     },
40581     
40582     
40583     onResize: function(w, h){
40584         
40585         return;
40586         // not sure if this is needed..
40587         //this.combo.onResize(w,h);
40588         
40589         if(typeof w != 'number'){
40590             // we do not handle it!?!?
40591             return;
40592         }
40593         var tw = this.combo.trigger.getWidth();
40594         tw += this.addicon ? this.addicon.getWidth() : 0;
40595         tw += this.editicon ? this.editicon.getWidth() : 0;
40596         var x = w - tw;
40597         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40598             
40599         this.combo.trigger.setStyle('left', '0px');
40600         
40601         if(this.list && this.listWidth === undefined){
40602             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40603             this.list.setWidth(lw);
40604             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40605         }
40606         
40607     
40608         
40609     },
40610     
40611     addItem: function(rec)
40612     {
40613         var valueField = this.combo.valueField;
40614         var displayField = this.combo.displayField;
40615         if (this.items.indexOfKey(rec[valueField]) > -1) {
40616             //console.log("GOT " + rec.data.id);
40617             return;
40618         }
40619         
40620         var x = new Roo.form.ComboBoxArray.Item({
40621             //id : rec[this.idField],
40622             data : rec,
40623             displayField : displayField ,
40624             tipField : displayField ,
40625             cb : this
40626         });
40627         // use the 
40628         this.items.add(rec[valueField],x);
40629         // add it before the element..
40630         this.updateHiddenEl();
40631         x.render(this.outerWrap, this.wrap.dom);
40632         // add the image handler..
40633     },
40634     
40635     updateHiddenEl : function()
40636     {
40637         this.validate();
40638         if (!this.hiddenEl) {
40639             return;
40640         }
40641         var ar = [];
40642         var idField = this.combo.valueField;
40643         
40644         this.items.each(function(f) {
40645             ar.push(f.data[idField]);
40646            
40647         });
40648         this.hiddenEl.dom.value = ar.join(',');
40649         this.validate();
40650     },
40651     
40652     reset : function()
40653     {
40654         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40655         this.items.each(function(f) {
40656            f.remove(); 
40657         });
40658         this.el.dom.value = '';
40659         if (this.hiddenEl) {
40660             this.hiddenEl.dom.value = '';
40661         }
40662         
40663     },
40664     getValue: function()
40665     {
40666         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40667     },
40668     setValue: function(v) // not a valid action - must use addItems..
40669     {
40670          
40671         this.reset();
40672         
40673         
40674         
40675         if (this.store.isLocal && (typeof(v) == 'string')) {
40676             // then we can use the store to find the values..
40677             // comma seperated at present.. this needs to allow JSON based encoding..
40678             this.hiddenEl.value  = v;
40679             var v_ar = [];
40680             Roo.each(v.split(','), function(k) {
40681                 Roo.log("CHECK " + this.valueField + ',' + k);
40682                 var li = this.store.query(this.valueField, k);
40683                 if (!li.length) {
40684                     return;
40685                 }
40686                 var add = {};
40687                 add[this.valueField] = k;
40688                 add[this.displayField] = li.item(0).data[this.displayField];
40689                 
40690                 this.addItem(add);
40691             }, this) 
40692              
40693         }
40694         if (typeof(v) == 'object' ) {
40695             // then let's assume it's an array of objects..
40696             Roo.each(v, function(l) {
40697                 this.addItem(l);
40698             }, this);
40699              
40700         }
40701         
40702         
40703     },
40704     setFromData: function(v)
40705     {
40706         // this recieves an object, if setValues is called.
40707         this.reset();
40708         this.el.dom.value = v[this.displayField];
40709         this.hiddenEl.dom.value = v[this.valueField];
40710         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40711             return;
40712         }
40713         var kv = v[this.valueField];
40714         var dv = v[this.displayField];
40715         kv = typeof(kv) != 'string' ? '' : kv;
40716         dv = typeof(dv) != 'string' ? '' : dv;
40717         
40718         
40719         var keys = kv.split(',');
40720         var display = dv.split(',');
40721         for (var i = 0 ; i < keys.length; i++) {
40722             
40723             add = {};
40724             add[this.valueField] = keys[i];
40725             add[this.displayField] = display[i];
40726             this.addItem(add);
40727         }
40728       
40729         
40730     },
40731     
40732     /**
40733      * Validates the combox array value
40734      * @return {Boolean} True if the value is valid, else false
40735      */
40736     validate : function(){
40737         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40738             this.clearInvalid();
40739             return true;
40740         }
40741         return false;
40742     },
40743     
40744     validateValue : function(value){
40745         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40746         
40747     },
40748     
40749     /*@
40750      * overide
40751      * 
40752      */
40753     isDirty : function() {
40754         if(this.disabled) {
40755             return false;
40756         }
40757         
40758         try {
40759             var d = Roo.decode(String(this.originalValue));
40760         } catch (e) {
40761             return String(this.getValue()) !== String(this.originalValue);
40762         }
40763         
40764         var originalValue = [];
40765         
40766         for (var i = 0; i < d.length; i++){
40767             originalValue.push(d[i][this.valueField]);
40768         }
40769         
40770         return String(this.getValue()) !== String(originalValue.join(','));
40771         
40772     }
40773     
40774 });
40775
40776
40777
40778 /**
40779  * @class Roo.form.ComboBoxArray.Item
40780  * @extends Roo.BoxComponent
40781  * A selected item in the list
40782  *  Fred [x]  Brian [x]  [Pick another |v]
40783  * 
40784  * @constructor
40785  * Create a new item.
40786  * @param {Object} config Configuration options
40787  */
40788  
40789 Roo.form.ComboBoxArray.Item = function(config) {
40790     config.id = Roo.id();
40791     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40792 }
40793
40794 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40795     data : {},
40796     cb: false,
40797     displayField : false,
40798     tipField : false,
40799     
40800     
40801     defaultAutoCreate : {
40802         tag: 'div',
40803         cls: 'x-cbarray-item',
40804         cn : [ 
40805             { tag: 'div' },
40806             {
40807                 tag: 'img',
40808                 width:16,
40809                 height : 16,
40810                 src : Roo.BLANK_IMAGE_URL ,
40811                 align: 'center'
40812             }
40813         ]
40814         
40815     },
40816     
40817  
40818     onRender : function(ct, position)
40819     {
40820         Roo.form.Field.superclass.onRender.call(this, ct, position);
40821         
40822         if(!this.el){
40823             var cfg = this.getAutoCreate();
40824             this.el = ct.createChild(cfg, position);
40825         }
40826         
40827         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40828         
40829         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40830             this.cb.renderer(this.data) :
40831             String.format('{0}',this.data[this.displayField]);
40832         
40833             
40834         this.el.child('div').dom.setAttribute('qtip',
40835                         String.format('{0}',this.data[this.tipField])
40836         );
40837         
40838         this.el.child('img').on('click', this.remove, this);
40839         
40840     },
40841    
40842     remove : function()
40843     {
40844         if(this.cb.disabled){
40845             return;
40846         }
40847         
40848         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
40849             this.cb.items.remove(this);
40850             this.el.child('img').un('click', this.remove, this);
40851             this.el.remove();
40852             this.cb.updateHiddenEl();
40853
40854             this.cb.fireEvent('remove', this.cb, this);
40855         }
40856         
40857     }
40858 });/*
40859  * Based on:
40860  * Ext JS Library 1.1.1
40861  * Copyright(c) 2006-2007, Ext JS, LLC.
40862  *
40863  * Originally Released Under LGPL - original licence link has changed is not relivant.
40864  *
40865  * Fork - LGPL
40866  * <script type="text/javascript">
40867  */
40868 /**
40869  * @class Roo.form.Checkbox
40870  * @extends Roo.form.Field
40871  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40872  * @constructor
40873  * Creates a new Checkbox
40874  * @param {Object} config Configuration options
40875  */
40876 Roo.form.Checkbox = function(config){
40877     Roo.form.Checkbox.superclass.constructor.call(this, config);
40878     this.addEvents({
40879         /**
40880          * @event check
40881          * Fires when the checkbox is checked or unchecked.
40882              * @param {Roo.form.Checkbox} this This checkbox
40883              * @param {Boolean} checked The new checked value
40884              */
40885         check : true
40886     });
40887 };
40888
40889 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40890     /**
40891      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40892      */
40893     focusClass : undefined,
40894     /**
40895      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40896      */
40897     fieldClass: "x-form-field",
40898     /**
40899      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40900      */
40901     checked: false,
40902     /**
40903      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40904      * {tag: "input", type: "checkbox", autocomplete: "off"})
40905      */
40906     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40907     /**
40908      * @cfg {String} boxLabel The text that appears beside the checkbox
40909      */
40910     boxLabel : "",
40911     /**
40912      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40913      */  
40914     inputValue : '1',
40915     /**
40916      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40917      */
40918      valueOff: '0', // value when not checked..
40919
40920     actionMode : 'viewEl', 
40921     //
40922     // private
40923     itemCls : 'x-menu-check-item x-form-item',
40924     groupClass : 'x-menu-group-item',
40925     inputType : 'hidden',
40926     
40927     
40928     inSetChecked: false, // check that we are not calling self...
40929     
40930     inputElement: false, // real input element?
40931     basedOn: false, // ????
40932     
40933     isFormField: true, // not sure where this is needed!!!!
40934
40935     onResize : function(){
40936         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40937         if(!this.boxLabel){
40938             this.el.alignTo(this.wrap, 'c-c');
40939         }
40940     },
40941
40942     initEvents : function(){
40943         Roo.form.Checkbox.superclass.initEvents.call(this);
40944         this.el.on("click", this.onClick,  this);
40945         this.el.on("change", this.onClick,  this);
40946     },
40947
40948
40949     getResizeEl : function(){
40950         return this.wrap;
40951     },
40952
40953     getPositionEl : function(){
40954         return this.wrap;
40955     },
40956
40957     // private
40958     onRender : function(ct, position){
40959         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40960         /*
40961         if(this.inputValue !== undefined){
40962             this.el.dom.value = this.inputValue;
40963         }
40964         */
40965         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40966         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40967         var viewEl = this.wrap.createChild({ 
40968             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40969         this.viewEl = viewEl;   
40970         this.wrap.on('click', this.onClick,  this); 
40971         
40972         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40973         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40974         
40975         
40976         
40977         if(this.boxLabel){
40978             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40979         //    viewEl.on('click', this.onClick,  this); 
40980         }
40981         //if(this.checked){
40982             this.setChecked(this.checked);
40983         //}else{
40984             //this.checked = this.el.dom;
40985         //}
40986
40987     },
40988
40989     // private
40990     initValue : Roo.emptyFn,
40991
40992     /**
40993      * Returns the checked state of the checkbox.
40994      * @return {Boolean} True if checked, else false
40995      */
40996     getValue : function(){
40997         if(this.el){
40998             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40999         }
41000         return this.valueOff;
41001         
41002     },
41003
41004         // private
41005     onClick : function(){ 
41006         if (this.disabled) {
41007             return;
41008         }
41009         this.setChecked(!this.checked);
41010
41011         //if(this.el.dom.checked != this.checked){
41012         //    this.setValue(this.el.dom.checked);
41013        // }
41014     },
41015
41016     /**
41017      * Sets the checked state of the checkbox.
41018      * On is always based on a string comparison between inputValue and the param.
41019      * @param {Boolean/String} value - the value to set 
41020      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
41021      */
41022     setValue : function(v,suppressEvent){
41023         
41024         
41025         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
41026         //if(this.el && this.el.dom){
41027         //    this.el.dom.checked = this.checked;
41028         //    this.el.dom.defaultChecked = this.checked;
41029         //}
41030         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
41031         //this.fireEvent("check", this, this.checked);
41032     },
41033     // private..
41034     setChecked : function(state,suppressEvent)
41035     {
41036         if (this.inSetChecked) {
41037             this.checked = state;
41038             return;
41039         }
41040         
41041     
41042         if(this.wrap){
41043             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
41044         }
41045         this.checked = state;
41046         if(suppressEvent !== true){
41047             this.fireEvent('check', this, state);
41048         }
41049         this.inSetChecked = true;
41050         this.el.dom.value = state ? this.inputValue : this.valueOff;
41051         this.inSetChecked = false;
41052         
41053     },
41054     // handle setting of hidden value by some other method!!?!?
41055     setFromHidden: function()
41056     {
41057         if(!this.el){
41058             return;
41059         }
41060         //console.log("SET FROM HIDDEN");
41061         //alert('setFrom hidden');
41062         this.setValue(this.el.dom.value);
41063     },
41064     
41065     onDestroy : function()
41066     {
41067         if(this.viewEl){
41068             Roo.get(this.viewEl).remove();
41069         }
41070          
41071         Roo.form.Checkbox.superclass.onDestroy.call(this);
41072     }
41073
41074 });/*
41075  * Based on:
41076  * Ext JS Library 1.1.1
41077  * Copyright(c) 2006-2007, Ext JS, LLC.
41078  *
41079  * Originally Released Under LGPL - original licence link has changed is not relivant.
41080  *
41081  * Fork - LGPL
41082  * <script type="text/javascript">
41083  */
41084  
41085 /**
41086  * @class Roo.form.Radio
41087  * @extends Roo.form.Checkbox
41088  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
41089  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
41090  * @constructor
41091  * Creates a new Radio
41092  * @param {Object} config Configuration options
41093  */
41094 Roo.form.Radio = function(){
41095     Roo.form.Radio.superclass.constructor.apply(this, arguments);
41096 };
41097 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
41098     inputType: 'radio',
41099
41100     /**
41101      * If this radio is part of a group, it will return the selected value
41102      * @return {String}
41103      */
41104     getGroupValue : function(){
41105         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
41106     },
41107     
41108     
41109     onRender : function(ct, position){
41110         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
41111         
41112         if(this.inputValue !== undefined){
41113             this.el.dom.value = this.inputValue;
41114         }
41115          
41116         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
41117         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
41118         //var viewEl = this.wrap.createChild({ 
41119         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
41120         //this.viewEl = viewEl;   
41121         //this.wrap.on('click', this.onClick,  this); 
41122         
41123         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
41124         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
41125         
41126         
41127         
41128         if(this.boxLabel){
41129             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
41130         //    viewEl.on('click', this.onClick,  this); 
41131         }
41132          if(this.checked){
41133             this.el.dom.checked =   'checked' ;
41134         }
41135          
41136     } 
41137     
41138     
41139 });//<script type="text/javascript">
41140
41141 /*
41142  * Based  Ext JS Library 1.1.1
41143  * Copyright(c) 2006-2007, Ext JS, LLC.
41144  * LGPL
41145  *
41146  */
41147  
41148 /**
41149  * @class Roo.HtmlEditorCore
41150  * @extends Roo.Component
41151  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
41152  *
41153  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
41154  */
41155
41156 Roo.HtmlEditorCore = function(config){
41157     
41158     
41159     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
41160     
41161     
41162     this.addEvents({
41163         /**
41164          * @event initialize
41165          * Fires when the editor is fully initialized (including the iframe)
41166          * @param {Roo.HtmlEditorCore} this
41167          */
41168         initialize: true,
41169         /**
41170          * @event activate
41171          * Fires when the editor is first receives the focus. Any insertion must wait
41172          * until after this event.
41173          * @param {Roo.HtmlEditorCore} this
41174          */
41175         activate: true,
41176          /**
41177          * @event beforesync
41178          * Fires before the textarea is updated with content from the editor iframe. Return false
41179          * to cancel the sync.
41180          * @param {Roo.HtmlEditorCore} this
41181          * @param {String} html
41182          */
41183         beforesync: true,
41184          /**
41185          * @event beforepush
41186          * Fires before the iframe editor is updated with content from the textarea. Return false
41187          * to cancel the push.
41188          * @param {Roo.HtmlEditorCore} this
41189          * @param {String} html
41190          */
41191         beforepush: true,
41192          /**
41193          * @event sync
41194          * Fires when the textarea is updated with content from the editor iframe.
41195          * @param {Roo.HtmlEditorCore} this
41196          * @param {String} html
41197          */
41198         sync: true,
41199          /**
41200          * @event push
41201          * Fires when the iframe editor is updated with content from the textarea.
41202          * @param {Roo.HtmlEditorCore} this
41203          * @param {String} html
41204          */
41205         push: true,
41206         
41207         /**
41208          * @event editorevent
41209          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41210          * @param {Roo.HtmlEditorCore} this
41211          */
41212         editorevent: true
41213         
41214     });
41215     
41216     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
41217     
41218     // defaults : white / black...
41219     this.applyBlacklists();
41220     
41221     
41222     
41223 };
41224
41225
41226 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41227
41228
41229      /**
41230      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41231      */
41232     
41233     owner : false,
41234     
41235      /**
41236      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41237      *                        Roo.resizable.
41238      */
41239     resizable : false,
41240      /**
41241      * @cfg {Number} height (in pixels)
41242      */   
41243     height: 300,
41244    /**
41245      * @cfg {Number} width (in pixels)
41246      */   
41247     width: 500,
41248     
41249     /**
41250      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41251      * 
41252      */
41253     stylesheets: false,
41254     
41255     // id of frame..
41256     frameId: false,
41257     
41258     // private properties
41259     validationEvent : false,
41260     deferHeight: true,
41261     initialized : false,
41262     activated : false,
41263     sourceEditMode : false,
41264     onFocus : Roo.emptyFn,
41265     iframePad:3,
41266     hideMode:'offsets',
41267     
41268     clearUp: true,
41269     
41270     // blacklist + whitelisted elements..
41271     black: false,
41272     white: false,
41273      
41274     
41275
41276     /**
41277      * Protected method that will not generally be called directly. It
41278      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41279      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41280      */
41281     getDocMarkup : function(){
41282         // body styles..
41283         var st = '';
41284         
41285         // inherit styels from page...?? 
41286         if (this.stylesheets === false) {
41287             
41288             Roo.get(document.head).select('style').each(function(node) {
41289                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41290             });
41291             
41292             Roo.get(document.head).select('link').each(function(node) { 
41293                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41294             });
41295             
41296         } else if (!this.stylesheets.length) {
41297                 // simple..
41298                 st = '<style type="text/css">' +
41299                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41300                    '</style>';
41301         } else { 
41302             
41303         }
41304         
41305         st +=  '<style type="text/css">' +
41306             'IMG { cursor: pointer } ' +
41307         '</style>';
41308
41309         
41310         return '<html><head>' + st  +
41311             //<style type="text/css">' +
41312             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41313             //'</style>' +
41314             ' </head><body class="roo-htmleditor-body"></body></html>';
41315     },
41316
41317     // private
41318     onRender : function(ct, position)
41319     {
41320         var _t = this;
41321         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41322         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41323         
41324         
41325         this.el.dom.style.border = '0 none';
41326         this.el.dom.setAttribute('tabIndex', -1);
41327         this.el.addClass('x-hidden hide');
41328         
41329         
41330         
41331         if(Roo.isIE){ // fix IE 1px bogus margin
41332             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41333         }
41334        
41335         
41336         this.frameId = Roo.id();
41337         
41338          
41339         
41340         var iframe = this.owner.wrap.createChild({
41341             tag: 'iframe',
41342             cls: 'form-control', // bootstrap..
41343             id: this.frameId,
41344             name: this.frameId,
41345             frameBorder : 'no',
41346             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41347         }, this.el
41348         );
41349         
41350         
41351         this.iframe = iframe.dom;
41352
41353          this.assignDocWin();
41354         
41355         this.doc.designMode = 'on';
41356        
41357         this.doc.open();
41358         this.doc.write(this.getDocMarkup());
41359         this.doc.close();
41360
41361         
41362         var task = { // must defer to wait for browser to be ready
41363             run : function(){
41364                 //console.log("run task?" + this.doc.readyState);
41365                 this.assignDocWin();
41366                 if(this.doc.body || this.doc.readyState == 'complete'){
41367                     try {
41368                         this.doc.designMode="on";
41369                     } catch (e) {
41370                         return;
41371                     }
41372                     Roo.TaskMgr.stop(task);
41373                     this.initEditor.defer(10, this);
41374                 }
41375             },
41376             interval : 10,
41377             duration: 10000,
41378             scope: this
41379         };
41380         Roo.TaskMgr.start(task);
41381
41382     },
41383
41384     // private
41385     onResize : function(w, h)
41386     {
41387          Roo.log('resize: ' +w + ',' + h );
41388         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41389         if(!this.iframe){
41390             return;
41391         }
41392         if(typeof w == 'number'){
41393             
41394             this.iframe.style.width = w + 'px';
41395         }
41396         if(typeof h == 'number'){
41397             
41398             this.iframe.style.height = h + 'px';
41399             if(this.doc){
41400                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41401             }
41402         }
41403         
41404     },
41405
41406     /**
41407      * Toggles the editor between standard and source edit mode.
41408      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41409      */
41410     toggleSourceEdit : function(sourceEditMode){
41411         
41412         this.sourceEditMode = sourceEditMode === true;
41413         
41414         if(this.sourceEditMode){
41415  
41416             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41417             
41418         }else{
41419             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41420             //this.iframe.className = '';
41421             this.deferFocus();
41422         }
41423         //this.setSize(this.owner.wrap.getSize());
41424         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41425     },
41426
41427     
41428   
41429
41430     /**
41431      * Protected method that will not generally be called directly. If you need/want
41432      * custom HTML cleanup, this is the method you should override.
41433      * @param {String} html The HTML to be cleaned
41434      * return {String} The cleaned HTML
41435      */
41436     cleanHtml : function(html){
41437         html = String(html);
41438         if(html.length > 5){
41439             if(Roo.isSafari){ // strip safari nonsense
41440                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41441             }
41442         }
41443         if(html == '&nbsp;'){
41444             html = '';
41445         }
41446         return html;
41447     },
41448
41449     /**
41450      * HTML Editor -> Textarea
41451      * Protected method that will not generally be called directly. Syncs the contents
41452      * of the editor iframe with the textarea.
41453      */
41454     syncValue : function(){
41455         if(this.initialized){
41456             var bd = (this.doc.body || this.doc.documentElement);
41457             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41458             var html = bd.innerHTML;
41459             if(Roo.isSafari){
41460                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41461                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41462                 if(m && m[1]){
41463                     html = '<div style="'+m[0]+'">' + html + '</div>';
41464                 }
41465             }
41466             html = this.cleanHtml(html);
41467             // fix up the special chars.. normaly like back quotes in word...
41468             // however we do not want to do this with chinese..
41469             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41470                 var cc = b.charCodeAt();
41471                 if (
41472                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41473                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41474                     (cc >= 0xf900 && cc < 0xfb00 )
41475                 ) {
41476                         return b;
41477                 }
41478                 return "&#"+cc+";" 
41479             });
41480             if(this.owner.fireEvent('beforesync', this, html) !== false){
41481                 this.el.dom.value = html;
41482                 this.owner.fireEvent('sync', this, html);
41483             }
41484         }
41485     },
41486
41487     /**
41488      * Protected method that will not generally be called directly. Pushes the value of the textarea
41489      * into the iframe editor.
41490      */
41491     pushValue : function(){
41492         if(this.initialized){
41493             var v = this.el.dom.value.trim();
41494             
41495 //            if(v.length < 1){
41496 //                v = '&#160;';
41497 //            }
41498             
41499             if(this.owner.fireEvent('beforepush', this, v) !== false){
41500                 var d = (this.doc.body || this.doc.documentElement);
41501                 d.innerHTML = v;
41502                 this.cleanUpPaste();
41503                 this.el.dom.value = d.innerHTML;
41504                 this.owner.fireEvent('push', this, v);
41505             }
41506         }
41507     },
41508
41509     // private
41510     deferFocus : function(){
41511         this.focus.defer(10, this);
41512     },
41513
41514     // doc'ed in Field
41515     focus : function(){
41516         if(this.win && !this.sourceEditMode){
41517             this.win.focus();
41518         }else{
41519             this.el.focus();
41520         }
41521     },
41522     
41523     assignDocWin: function()
41524     {
41525         var iframe = this.iframe;
41526         
41527          if(Roo.isIE){
41528             this.doc = iframe.contentWindow.document;
41529             this.win = iframe.contentWindow;
41530         } else {
41531 //            if (!Roo.get(this.frameId)) {
41532 //                return;
41533 //            }
41534 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41535 //            this.win = Roo.get(this.frameId).dom.contentWindow;
41536             
41537             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
41538                 return;
41539             }
41540             
41541             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41542             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
41543         }
41544     },
41545     
41546     // private
41547     initEditor : function(){
41548         //console.log("INIT EDITOR");
41549         this.assignDocWin();
41550         
41551         
41552         
41553         this.doc.designMode="on";
41554         this.doc.open();
41555         this.doc.write(this.getDocMarkup());
41556         this.doc.close();
41557         
41558         var dbody = (this.doc.body || this.doc.documentElement);
41559         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41560         // this copies styles from the containing element into thsi one..
41561         // not sure why we need all of this..
41562         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41563         
41564         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41565         //ss['background-attachment'] = 'fixed'; // w3c
41566         dbody.bgProperties = 'fixed'; // ie
41567         //Roo.DomHelper.applyStyles(dbody, ss);
41568         Roo.EventManager.on(this.doc, {
41569             //'mousedown': this.onEditorEvent,
41570             'mouseup': this.onEditorEvent,
41571             'dblclick': this.onEditorEvent,
41572             'click': this.onEditorEvent,
41573             'keyup': this.onEditorEvent,
41574             buffer:100,
41575             scope: this
41576         });
41577         if(Roo.isGecko){
41578             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41579         }
41580         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41581             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41582         }
41583         this.initialized = true;
41584
41585         this.owner.fireEvent('initialize', this);
41586         this.pushValue();
41587     },
41588
41589     // private
41590     onDestroy : function(){
41591         
41592         
41593         
41594         if(this.rendered){
41595             
41596             //for (var i =0; i < this.toolbars.length;i++) {
41597             //    // fixme - ask toolbars for heights?
41598             //    this.toolbars[i].onDestroy();
41599            // }
41600             
41601             //this.wrap.dom.innerHTML = '';
41602             //this.wrap.remove();
41603         }
41604     },
41605
41606     // private
41607     onFirstFocus : function(){
41608         
41609         this.assignDocWin();
41610         
41611         
41612         this.activated = true;
41613          
41614     
41615         if(Roo.isGecko){ // prevent silly gecko errors
41616             this.win.focus();
41617             var s = this.win.getSelection();
41618             if(!s.focusNode || s.focusNode.nodeType != 3){
41619                 var r = s.getRangeAt(0);
41620                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41621                 r.collapse(true);
41622                 this.deferFocus();
41623             }
41624             try{
41625                 this.execCmd('useCSS', true);
41626                 this.execCmd('styleWithCSS', false);
41627             }catch(e){}
41628         }
41629         this.owner.fireEvent('activate', this);
41630     },
41631
41632     // private
41633     adjustFont: function(btn){
41634         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41635         //if(Roo.isSafari){ // safari
41636         //    adjust *= 2;
41637        // }
41638         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41639         if(Roo.isSafari){ // safari
41640             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41641             v =  (v < 10) ? 10 : v;
41642             v =  (v > 48) ? 48 : v;
41643             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41644             
41645         }
41646         
41647         
41648         v = Math.max(1, v+adjust);
41649         
41650         this.execCmd('FontSize', v  );
41651     },
41652
41653     onEditorEvent : function(e)
41654     {
41655         this.owner.fireEvent('editorevent', this, e);
41656       //  this.updateToolbar();
41657         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41658     },
41659
41660     insertTag : function(tg)
41661     {
41662         // could be a bit smarter... -> wrap the current selected tRoo..
41663         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41664             
41665             range = this.createRange(this.getSelection());
41666             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41667             wrappingNode.appendChild(range.extractContents());
41668             range.insertNode(wrappingNode);
41669
41670             return;
41671             
41672             
41673             
41674         }
41675         this.execCmd("formatblock",   tg);
41676         
41677     },
41678     
41679     insertText : function(txt)
41680     {
41681         
41682         
41683         var range = this.createRange();
41684         range.deleteContents();
41685                //alert(Sender.getAttribute('label'));
41686                
41687         range.insertNode(this.doc.createTextNode(txt));
41688     } ,
41689     
41690      
41691
41692     /**
41693      * Executes a Midas editor command on the editor document and performs necessary focus and
41694      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41695      * @param {String} cmd The Midas command
41696      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41697      */
41698     relayCmd : function(cmd, value){
41699         this.win.focus();
41700         this.execCmd(cmd, value);
41701         this.owner.fireEvent('editorevent', this);
41702         //this.updateToolbar();
41703         this.owner.deferFocus();
41704     },
41705
41706     /**
41707      * Executes a Midas editor command directly on the editor document.
41708      * For visual commands, you should use {@link #relayCmd} instead.
41709      * <b>This should only be called after the editor is initialized.</b>
41710      * @param {String} cmd The Midas command
41711      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41712      */
41713     execCmd : function(cmd, value){
41714         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41715         this.syncValue();
41716     },
41717  
41718  
41719    
41720     /**
41721      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41722      * to insert tRoo.
41723      * @param {String} text | dom node.. 
41724      */
41725     insertAtCursor : function(text)
41726     {
41727         
41728         
41729         
41730         if(!this.activated){
41731             return;
41732         }
41733         /*
41734         if(Roo.isIE){
41735             this.win.focus();
41736             var r = this.doc.selection.createRange();
41737             if(r){
41738                 r.collapse(true);
41739                 r.pasteHTML(text);
41740                 this.syncValue();
41741                 this.deferFocus();
41742             
41743             }
41744             return;
41745         }
41746         */
41747         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41748             this.win.focus();
41749             
41750             
41751             // from jquery ui (MIT licenced)
41752             var range, node;
41753             var win = this.win;
41754             
41755             if (win.getSelection && win.getSelection().getRangeAt) {
41756                 range = win.getSelection().getRangeAt(0);
41757                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41758                 range.insertNode(node);
41759             } else if (win.document.selection && win.document.selection.createRange) {
41760                 // no firefox support
41761                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41762                 win.document.selection.createRange().pasteHTML(txt);
41763             } else {
41764                 // no firefox support
41765                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41766                 this.execCmd('InsertHTML', txt);
41767             } 
41768             
41769             this.syncValue();
41770             
41771             this.deferFocus();
41772         }
41773     },
41774  // private
41775     mozKeyPress : function(e){
41776         if(e.ctrlKey){
41777             var c = e.getCharCode(), cmd;
41778           
41779             if(c > 0){
41780                 c = String.fromCharCode(c).toLowerCase();
41781                 switch(c){
41782                     case 'b':
41783                         cmd = 'bold';
41784                         break;
41785                     case 'i':
41786                         cmd = 'italic';
41787                         break;
41788                     
41789                     case 'u':
41790                         cmd = 'underline';
41791                         break;
41792                     
41793                     case 'v':
41794                         this.cleanUpPaste.defer(100, this);
41795                         return;
41796                         
41797                 }
41798                 if(cmd){
41799                     this.win.focus();
41800                     this.execCmd(cmd);
41801                     this.deferFocus();
41802                     e.preventDefault();
41803                 }
41804                 
41805             }
41806         }
41807     },
41808
41809     // private
41810     fixKeys : function(){ // load time branching for fastest keydown performance
41811         if(Roo.isIE){
41812             return function(e){
41813                 var k = e.getKey(), r;
41814                 if(k == e.TAB){
41815                     e.stopEvent();
41816                     r = this.doc.selection.createRange();
41817                     if(r){
41818                         r.collapse(true);
41819                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41820                         this.deferFocus();
41821                     }
41822                     return;
41823                 }
41824                 
41825                 if(k == e.ENTER){
41826                     r = this.doc.selection.createRange();
41827                     if(r){
41828                         var target = r.parentElement();
41829                         if(!target || target.tagName.toLowerCase() != 'li'){
41830                             e.stopEvent();
41831                             r.pasteHTML('<br />');
41832                             r.collapse(false);
41833                             r.select();
41834                         }
41835                     }
41836                 }
41837                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41838                     this.cleanUpPaste.defer(100, this);
41839                     return;
41840                 }
41841                 
41842                 
41843             };
41844         }else if(Roo.isOpera){
41845             return function(e){
41846                 var k = e.getKey();
41847                 if(k == e.TAB){
41848                     e.stopEvent();
41849                     this.win.focus();
41850                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41851                     this.deferFocus();
41852                 }
41853                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41854                     this.cleanUpPaste.defer(100, this);
41855                     return;
41856                 }
41857                 
41858             };
41859         }else if(Roo.isSafari){
41860             return function(e){
41861                 var k = e.getKey();
41862                 
41863                 if(k == e.TAB){
41864                     e.stopEvent();
41865                     this.execCmd('InsertText','\t');
41866                     this.deferFocus();
41867                     return;
41868                 }
41869                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41870                     this.cleanUpPaste.defer(100, this);
41871                     return;
41872                 }
41873                 
41874              };
41875         }
41876     }(),
41877     
41878     getAllAncestors: function()
41879     {
41880         var p = this.getSelectedNode();
41881         var a = [];
41882         if (!p) {
41883             a.push(p); // push blank onto stack..
41884             p = this.getParentElement();
41885         }
41886         
41887         
41888         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41889             a.push(p);
41890             p = p.parentNode;
41891         }
41892         a.push(this.doc.body);
41893         return a;
41894     },
41895     lastSel : false,
41896     lastSelNode : false,
41897     
41898     
41899     getSelection : function() 
41900     {
41901         this.assignDocWin();
41902         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41903     },
41904     
41905     getSelectedNode: function() 
41906     {
41907         // this may only work on Gecko!!!
41908         
41909         // should we cache this!!!!
41910         
41911         
41912         
41913          
41914         var range = this.createRange(this.getSelection()).cloneRange();
41915         
41916         if (Roo.isIE) {
41917             var parent = range.parentElement();
41918             while (true) {
41919                 var testRange = range.duplicate();
41920                 testRange.moveToElementText(parent);
41921                 if (testRange.inRange(range)) {
41922                     break;
41923                 }
41924                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41925                     break;
41926                 }
41927                 parent = parent.parentElement;
41928             }
41929             return parent;
41930         }
41931         
41932         // is ancestor a text element.
41933         var ac =  range.commonAncestorContainer;
41934         if (ac.nodeType == 3) {
41935             ac = ac.parentNode;
41936         }
41937         
41938         var ar = ac.childNodes;
41939          
41940         var nodes = [];
41941         var other_nodes = [];
41942         var has_other_nodes = false;
41943         for (var i=0;i<ar.length;i++) {
41944             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41945                 continue;
41946             }
41947             // fullly contained node.
41948             
41949             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41950                 nodes.push(ar[i]);
41951                 continue;
41952             }
41953             
41954             // probably selected..
41955             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41956                 other_nodes.push(ar[i]);
41957                 continue;
41958             }
41959             // outer..
41960             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41961                 continue;
41962             }
41963             
41964             
41965             has_other_nodes = true;
41966         }
41967         if (!nodes.length && other_nodes.length) {
41968             nodes= other_nodes;
41969         }
41970         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41971             return false;
41972         }
41973         
41974         return nodes[0];
41975     },
41976     createRange: function(sel)
41977     {
41978         // this has strange effects when using with 
41979         // top toolbar - not sure if it's a great idea.
41980         //this.editor.contentWindow.focus();
41981         if (typeof sel != "undefined") {
41982             try {
41983                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41984             } catch(e) {
41985                 return this.doc.createRange();
41986             }
41987         } else {
41988             return this.doc.createRange();
41989         }
41990     },
41991     getParentElement: function()
41992     {
41993         
41994         this.assignDocWin();
41995         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41996         
41997         var range = this.createRange(sel);
41998          
41999         try {
42000             var p = range.commonAncestorContainer;
42001             while (p.nodeType == 3) { // text node
42002                 p = p.parentNode;
42003             }
42004             return p;
42005         } catch (e) {
42006             return null;
42007         }
42008     
42009     },
42010     /***
42011      *
42012      * Range intersection.. the hard stuff...
42013      *  '-1' = before
42014      *  '0' = hits..
42015      *  '1' = after.
42016      *         [ -- selected range --- ]
42017      *   [fail]                        [fail]
42018      *
42019      *    basically..
42020      *      if end is before start or  hits it. fail.
42021      *      if start is after end or hits it fail.
42022      *
42023      *   if either hits (but other is outside. - then it's not 
42024      *   
42025      *    
42026      **/
42027     
42028     
42029     // @see http://www.thismuchiknow.co.uk/?p=64.
42030     rangeIntersectsNode : function(range, node)
42031     {
42032         var nodeRange = node.ownerDocument.createRange();
42033         try {
42034             nodeRange.selectNode(node);
42035         } catch (e) {
42036             nodeRange.selectNodeContents(node);
42037         }
42038     
42039         var rangeStartRange = range.cloneRange();
42040         rangeStartRange.collapse(true);
42041     
42042         var rangeEndRange = range.cloneRange();
42043         rangeEndRange.collapse(false);
42044     
42045         var nodeStartRange = nodeRange.cloneRange();
42046         nodeStartRange.collapse(true);
42047     
42048         var nodeEndRange = nodeRange.cloneRange();
42049         nodeEndRange.collapse(false);
42050     
42051         return rangeStartRange.compareBoundaryPoints(
42052                  Range.START_TO_START, nodeEndRange) == -1 &&
42053                rangeEndRange.compareBoundaryPoints(
42054                  Range.START_TO_START, nodeStartRange) == 1;
42055         
42056          
42057     },
42058     rangeCompareNode : function(range, node)
42059     {
42060         var nodeRange = node.ownerDocument.createRange();
42061         try {
42062             nodeRange.selectNode(node);
42063         } catch (e) {
42064             nodeRange.selectNodeContents(node);
42065         }
42066         
42067         
42068         range.collapse(true);
42069     
42070         nodeRange.collapse(true);
42071      
42072         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
42073         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
42074          
42075         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
42076         
42077         var nodeIsBefore   =  ss == 1;
42078         var nodeIsAfter    = ee == -1;
42079         
42080         if (nodeIsBefore && nodeIsAfter) {
42081             return 0; // outer
42082         }
42083         if (!nodeIsBefore && nodeIsAfter) {
42084             return 1; //right trailed.
42085         }
42086         
42087         if (nodeIsBefore && !nodeIsAfter) {
42088             return 2;  // left trailed.
42089         }
42090         // fully contined.
42091         return 3;
42092     },
42093
42094     // private? - in a new class?
42095     cleanUpPaste :  function()
42096     {
42097         // cleans up the whole document..
42098         Roo.log('cleanuppaste');
42099         
42100         this.cleanUpChildren(this.doc.body);
42101         var clean = this.cleanWordChars(this.doc.body.innerHTML);
42102         if (clean != this.doc.body.innerHTML) {
42103             this.doc.body.innerHTML = clean;
42104         }
42105         
42106     },
42107     
42108     cleanWordChars : function(input) {// change the chars to hex code
42109         var he = Roo.HtmlEditorCore;
42110         
42111         var output = input;
42112         Roo.each(he.swapCodes, function(sw) { 
42113             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
42114             
42115             output = output.replace(swapper, sw[1]);
42116         });
42117         
42118         return output;
42119     },
42120     
42121     
42122     cleanUpChildren : function (n)
42123     {
42124         if (!n.childNodes.length) {
42125             return;
42126         }
42127         for (var i = n.childNodes.length-1; i > -1 ; i--) {
42128            this.cleanUpChild(n.childNodes[i]);
42129         }
42130     },
42131     
42132     
42133         
42134     
42135     cleanUpChild : function (node)
42136     {
42137         var ed = this;
42138         //console.log(node);
42139         if (node.nodeName == "#text") {
42140             // clean up silly Windows -- stuff?
42141             return; 
42142         }
42143         if (node.nodeName == "#comment") {
42144             node.parentNode.removeChild(node);
42145             // clean up silly Windows -- stuff?
42146             return; 
42147         }
42148         var lcname = node.tagName.toLowerCase();
42149         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
42150         // whitelist of tags..
42151         
42152         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
42153             // remove node.
42154             node.parentNode.removeChild(node);
42155             return;
42156             
42157         }
42158         
42159         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
42160         
42161         // remove <a name=....> as rendering on yahoo mailer is borked with this.
42162         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
42163         
42164         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
42165         //    remove_keep_children = true;
42166         //}
42167         
42168         if (remove_keep_children) {
42169             this.cleanUpChildren(node);
42170             // inserts everything just before this node...
42171             while (node.childNodes.length) {
42172                 var cn = node.childNodes[0];
42173                 node.removeChild(cn);
42174                 node.parentNode.insertBefore(cn, node);
42175             }
42176             node.parentNode.removeChild(node);
42177             return;
42178         }
42179         
42180         if (!node.attributes || !node.attributes.length) {
42181             this.cleanUpChildren(node);
42182             return;
42183         }
42184         
42185         function cleanAttr(n,v)
42186         {
42187             
42188             if (v.match(/^\./) || v.match(/^\//)) {
42189                 return;
42190             }
42191             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
42192                 return;
42193             }
42194             if (v.match(/^#/)) {
42195                 return;
42196             }
42197 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
42198             node.removeAttribute(n);
42199             
42200         }
42201         
42202         var cwhite = this.cwhite;
42203         var cblack = this.cblack;
42204             
42205         function cleanStyle(n,v)
42206         {
42207             if (v.match(/expression/)) { //XSS?? should we even bother..
42208                 node.removeAttribute(n);
42209                 return;
42210             }
42211             
42212             var parts = v.split(/;/);
42213             var clean = [];
42214             
42215             Roo.each(parts, function(p) {
42216                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42217                 if (!p.length) {
42218                     return true;
42219                 }
42220                 var l = p.split(':').shift().replace(/\s+/g,'');
42221                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42222                 
42223                 if ( cwhite.length && cblack.indexOf(l) > -1) {
42224 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42225                     //node.removeAttribute(n);
42226                     return true;
42227                 }
42228                 //Roo.log()
42229                 // only allow 'c whitelisted system attributes'
42230                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42231 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42232                     //node.removeAttribute(n);
42233                     return true;
42234                 }
42235                 
42236                 
42237                  
42238                 
42239                 clean.push(p);
42240                 return true;
42241             });
42242             if (clean.length) { 
42243                 node.setAttribute(n, clean.join(';'));
42244             } else {
42245                 node.removeAttribute(n);
42246             }
42247             
42248         }
42249         
42250         
42251         for (var i = node.attributes.length-1; i > -1 ; i--) {
42252             var a = node.attributes[i];
42253             //console.log(a);
42254             
42255             if (a.name.toLowerCase().substr(0,2)=='on')  {
42256                 node.removeAttribute(a.name);
42257                 continue;
42258             }
42259             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42260                 node.removeAttribute(a.name);
42261                 continue;
42262             }
42263             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42264                 cleanAttr(a.name,a.value); // fixme..
42265                 continue;
42266             }
42267             if (a.name == 'style') {
42268                 cleanStyle(a.name,a.value);
42269                 continue;
42270             }
42271             /// clean up MS crap..
42272             // tecnically this should be a list of valid class'es..
42273             
42274             
42275             if (a.name == 'class') {
42276                 if (a.value.match(/^Mso/)) {
42277                     node.className = '';
42278                 }
42279                 
42280                 if (a.value.match(/body/)) {
42281                     node.className = '';
42282                 }
42283                 continue;
42284             }
42285             
42286             // style cleanup!?
42287             // class cleanup?
42288             
42289         }
42290         
42291         
42292         this.cleanUpChildren(node);
42293         
42294         
42295     },
42296     
42297     /**
42298      * Clean up MS wordisms...
42299      */
42300     cleanWord : function(node)
42301     {
42302         
42303         
42304         if (!node) {
42305             this.cleanWord(this.doc.body);
42306             return;
42307         }
42308         if (node.nodeName == "#text") {
42309             // clean up silly Windows -- stuff?
42310             return; 
42311         }
42312         if (node.nodeName == "#comment") {
42313             node.parentNode.removeChild(node);
42314             // clean up silly Windows -- stuff?
42315             return; 
42316         }
42317         
42318         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42319             node.parentNode.removeChild(node);
42320             return;
42321         }
42322         
42323         // remove - but keep children..
42324         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42325             while (node.childNodes.length) {
42326                 var cn = node.childNodes[0];
42327                 node.removeChild(cn);
42328                 node.parentNode.insertBefore(cn, node);
42329             }
42330             node.parentNode.removeChild(node);
42331             this.iterateChildren(node, this.cleanWord);
42332             return;
42333         }
42334         // clean styles
42335         if (node.className.length) {
42336             
42337             var cn = node.className.split(/\W+/);
42338             var cna = [];
42339             Roo.each(cn, function(cls) {
42340                 if (cls.match(/Mso[a-zA-Z]+/)) {
42341                     return;
42342                 }
42343                 cna.push(cls);
42344             });
42345             node.className = cna.length ? cna.join(' ') : '';
42346             if (!cna.length) {
42347                 node.removeAttribute("class");
42348             }
42349         }
42350         
42351         if (node.hasAttribute("lang")) {
42352             node.removeAttribute("lang");
42353         }
42354         
42355         if (node.hasAttribute("style")) {
42356             
42357             var styles = node.getAttribute("style").split(";");
42358             var nstyle = [];
42359             Roo.each(styles, function(s) {
42360                 if (!s.match(/:/)) {
42361                     return;
42362                 }
42363                 var kv = s.split(":");
42364                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42365                     return;
42366                 }
42367                 // what ever is left... we allow.
42368                 nstyle.push(s);
42369             });
42370             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42371             if (!nstyle.length) {
42372                 node.removeAttribute('style');
42373             }
42374         }
42375         this.iterateChildren(node, this.cleanWord);
42376         
42377         
42378         
42379     },
42380     /**
42381      * iterateChildren of a Node, calling fn each time, using this as the scole..
42382      * @param {DomNode} node node to iterate children of.
42383      * @param {Function} fn method of this class to call on each item.
42384      */
42385     iterateChildren : function(node, fn)
42386     {
42387         if (!node.childNodes.length) {
42388                 return;
42389         }
42390         for (var i = node.childNodes.length-1; i > -1 ; i--) {
42391            fn.call(this, node.childNodes[i])
42392         }
42393     },
42394     
42395     
42396     /**
42397      * cleanTableWidths.
42398      *
42399      * Quite often pasting from word etc.. results in tables with column and widths.
42400      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
42401      *
42402      */
42403     cleanTableWidths : function(node)
42404     {
42405          
42406          
42407         if (!node) {
42408             this.cleanTableWidths(this.doc.body);
42409             return;
42410         }
42411         
42412         // ignore list...
42413         if (node.nodeName == "#text" || node.nodeName == "#comment") {
42414             return; 
42415         }
42416         Roo.log(node.tagName);
42417         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
42418             this.iterateChildren(node, this.cleanTableWidths);
42419             return;
42420         }
42421         if (node.hasAttribute('width')) {
42422             node.removeAttribute('width');
42423         }
42424         
42425          
42426         if (node.hasAttribute("style")) {
42427             // pretty basic...
42428             
42429             var styles = node.getAttribute("style").split(";");
42430             var nstyle = [];
42431             Roo.each(styles, function(s) {
42432                 if (!s.match(/:/)) {
42433                     return;
42434                 }
42435                 var kv = s.split(":");
42436                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
42437                     return;
42438                 }
42439                 // what ever is left... we allow.
42440                 nstyle.push(s);
42441             });
42442             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42443             if (!nstyle.length) {
42444                 node.removeAttribute('style');
42445             }
42446         }
42447         
42448         this.iterateChildren(node, this.cleanTableWidths);
42449         
42450         
42451     },
42452     
42453     
42454     
42455     
42456     domToHTML : function(currentElement, depth, nopadtext) {
42457         
42458         depth = depth || 0;
42459         nopadtext = nopadtext || false;
42460     
42461         if (!currentElement) {
42462             return this.domToHTML(this.doc.body);
42463         }
42464         
42465         //Roo.log(currentElement);
42466         var j;
42467         var allText = false;
42468         var nodeName = currentElement.nodeName;
42469         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42470         
42471         if  (nodeName == '#text') {
42472             
42473             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
42474         }
42475         
42476         
42477         var ret = '';
42478         if (nodeName != 'BODY') {
42479              
42480             var i = 0;
42481             // Prints the node tagName, such as <A>, <IMG>, etc
42482             if (tagName) {
42483                 var attr = [];
42484                 for(i = 0; i < currentElement.attributes.length;i++) {
42485                     // quoting?
42486                     var aname = currentElement.attributes.item(i).name;
42487                     if (!currentElement.attributes.item(i).value.length) {
42488                         continue;
42489                     }
42490                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42491                 }
42492                 
42493                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42494             } 
42495             else {
42496                 
42497                 // eack
42498             }
42499         } else {
42500             tagName = false;
42501         }
42502         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42503             return ret;
42504         }
42505         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42506             nopadtext = true;
42507         }
42508         
42509         
42510         // Traverse the tree
42511         i = 0;
42512         var currentElementChild = currentElement.childNodes.item(i);
42513         var allText = true;
42514         var innerHTML  = '';
42515         lastnode = '';
42516         while (currentElementChild) {
42517             // Formatting code (indent the tree so it looks nice on the screen)
42518             var nopad = nopadtext;
42519             if (lastnode == 'SPAN') {
42520                 nopad  = true;
42521             }
42522             // text
42523             if  (currentElementChild.nodeName == '#text') {
42524                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42525                 toadd = nopadtext ? toadd : toadd.trim();
42526                 if (!nopad && toadd.length > 80) {
42527                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42528                 }
42529                 innerHTML  += toadd;
42530                 
42531                 i++;
42532                 currentElementChild = currentElement.childNodes.item(i);
42533                 lastNode = '';
42534                 continue;
42535             }
42536             allText = false;
42537             
42538             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42539                 
42540             // Recursively traverse the tree structure of the child node
42541             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42542             lastnode = currentElementChild.nodeName;
42543             i++;
42544             currentElementChild=currentElement.childNodes.item(i);
42545         }
42546         
42547         ret += innerHTML;
42548         
42549         if (!allText) {
42550                 // The remaining code is mostly for formatting the tree
42551             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42552         }
42553         
42554         
42555         if (tagName) {
42556             ret+= "</"+tagName+">";
42557         }
42558         return ret;
42559         
42560     },
42561         
42562     applyBlacklists : function()
42563     {
42564         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
42565         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
42566         
42567         this.white = [];
42568         this.black = [];
42569         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
42570             if (b.indexOf(tag) > -1) {
42571                 return;
42572             }
42573             this.white.push(tag);
42574             
42575         }, this);
42576         
42577         Roo.each(w, function(tag) {
42578             if (b.indexOf(tag) > -1) {
42579                 return;
42580             }
42581             if (this.white.indexOf(tag) > -1) {
42582                 return;
42583             }
42584             this.white.push(tag);
42585             
42586         }, this);
42587         
42588         
42589         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
42590             if (w.indexOf(tag) > -1) {
42591                 return;
42592             }
42593             this.black.push(tag);
42594             
42595         }, this);
42596         
42597         Roo.each(b, function(tag) {
42598             if (w.indexOf(tag) > -1) {
42599                 return;
42600             }
42601             if (this.black.indexOf(tag) > -1) {
42602                 return;
42603             }
42604             this.black.push(tag);
42605             
42606         }, this);
42607         
42608         
42609         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
42610         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
42611         
42612         this.cwhite = [];
42613         this.cblack = [];
42614         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
42615             if (b.indexOf(tag) > -1) {
42616                 return;
42617             }
42618             this.cwhite.push(tag);
42619             
42620         }, this);
42621         
42622         Roo.each(w, function(tag) {
42623             if (b.indexOf(tag) > -1) {
42624                 return;
42625             }
42626             if (this.cwhite.indexOf(tag) > -1) {
42627                 return;
42628             }
42629             this.cwhite.push(tag);
42630             
42631         }, this);
42632         
42633         
42634         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
42635             if (w.indexOf(tag) > -1) {
42636                 return;
42637             }
42638             this.cblack.push(tag);
42639             
42640         }, this);
42641         
42642         Roo.each(b, function(tag) {
42643             if (w.indexOf(tag) > -1) {
42644                 return;
42645             }
42646             if (this.cblack.indexOf(tag) > -1) {
42647                 return;
42648             }
42649             this.cblack.push(tag);
42650             
42651         }, this);
42652     },
42653     
42654     setStylesheets : function(stylesheets)
42655     {
42656         if(typeof(stylesheets) == 'string'){
42657             Roo.get(this.iframe.contentDocument.head).createChild({
42658                 tag : 'link',
42659                 rel : 'stylesheet',
42660                 type : 'text/css',
42661                 href : stylesheets
42662             });
42663             
42664             return;
42665         }
42666         var _this = this;
42667      
42668         Roo.each(stylesheets, function(s) {
42669             if(!s.length){
42670                 return;
42671             }
42672             
42673             Roo.get(_this.iframe.contentDocument.head).createChild({
42674                 tag : 'link',
42675                 rel : 'stylesheet',
42676                 type : 'text/css',
42677                 href : s
42678             });
42679         });
42680
42681         
42682     },
42683     
42684     removeStylesheets : function()
42685     {
42686         var _this = this;
42687         
42688         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
42689             s.remove();
42690         });
42691     }
42692     
42693     // hide stuff that is not compatible
42694     /**
42695      * @event blur
42696      * @hide
42697      */
42698     /**
42699      * @event change
42700      * @hide
42701      */
42702     /**
42703      * @event focus
42704      * @hide
42705      */
42706     /**
42707      * @event specialkey
42708      * @hide
42709      */
42710     /**
42711      * @cfg {String} fieldClass @hide
42712      */
42713     /**
42714      * @cfg {String} focusClass @hide
42715      */
42716     /**
42717      * @cfg {String} autoCreate @hide
42718      */
42719     /**
42720      * @cfg {String} inputType @hide
42721      */
42722     /**
42723      * @cfg {String} invalidClass @hide
42724      */
42725     /**
42726      * @cfg {String} invalidText @hide
42727      */
42728     /**
42729      * @cfg {String} msgFx @hide
42730      */
42731     /**
42732      * @cfg {String} validateOnBlur @hide
42733      */
42734 });
42735
42736 Roo.HtmlEditorCore.white = [
42737         'area', 'br', 'img', 'input', 'hr', 'wbr',
42738         
42739        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42740        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42741        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42742        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42743        'table',   'ul',         'xmp', 
42744        
42745        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42746       'thead',   'tr', 
42747      
42748       'dir', 'menu', 'ol', 'ul', 'dl',
42749        
42750       'embed',  'object'
42751 ];
42752
42753
42754 Roo.HtmlEditorCore.black = [
42755     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42756         'applet', // 
42757         'base',   'basefont', 'bgsound', 'blink',  'body', 
42758         'frame',  'frameset', 'head',    'html',   'ilayer', 
42759         'iframe', 'layer',  'link',     'meta',    'object',   
42760         'script', 'style' ,'title',  'xml' // clean later..
42761 ];
42762 Roo.HtmlEditorCore.clean = [
42763     'script', 'style', 'title', 'xml'
42764 ];
42765 Roo.HtmlEditorCore.remove = [
42766     'font'
42767 ];
42768 // attributes..
42769
42770 Roo.HtmlEditorCore.ablack = [
42771     'on'
42772 ];
42773     
42774 Roo.HtmlEditorCore.aclean = [ 
42775     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42776 ];
42777
42778 // protocols..
42779 Roo.HtmlEditorCore.pwhite= [
42780         'http',  'https',  'mailto'
42781 ];
42782
42783 // white listed style attributes.
42784 Roo.HtmlEditorCore.cwhite= [
42785       //  'text-align', /// default is to allow most things..
42786       
42787          
42788 //        'font-size'//??
42789 ];
42790
42791 // black listed style attributes.
42792 Roo.HtmlEditorCore.cblack= [
42793       //  'font-size' -- this can be set by the project 
42794 ];
42795
42796
42797 Roo.HtmlEditorCore.swapCodes   =[ 
42798     [    8211, "--" ], 
42799     [    8212, "--" ], 
42800     [    8216,  "'" ],  
42801     [    8217, "'" ],  
42802     [    8220, '"' ],  
42803     [    8221, '"' ],  
42804     [    8226, "*" ],  
42805     [    8230, "..." ]
42806 ]; 
42807
42808     //<script type="text/javascript">
42809
42810 /*
42811  * Ext JS Library 1.1.1
42812  * Copyright(c) 2006-2007, Ext JS, LLC.
42813  * Licence LGPL
42814  * 
42815  */
42816  
42817  
42818 Roo.form.HtmlEditor = function(config){
42819     
42820     
42821     
42822     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42823     
42824     if (!this.toolbars) {
42825         this.toolbars = [];
42826     }
42827     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42828     
42829     
42830 };
42831
42832 /**
42833  * @class Roo.form.HtmlEditor
42834  * @extends Roo.form.Field
42835  * Provides a lightweight HTML Editor component.
42836  *
42837  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42838  * 
42839  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42840  * supported by this editor.</b><br/><br/>
42841  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42842  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42843  */
42844 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42845     /**
42846      * @cfg {Boolean} clearUp
42847      */
42848     clearUp : true,
42849       /**
42850      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42851      */
42852     toolbars : false,
42853    
42854      /**
42855      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42856      *                        Roo.resizable.
42857      */
42858     resizable : false,
42859      /**
42860      * @cfg {Number} height (in pixels)
42861      */   
42862     height: 300,
42863    /**
42864      * @cfg {Number} width (in pixels)
42865      */   
42866     width: 500,
42867     
42868     /**
42869      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42870      * 
42871      */
42872     stylesheets: false,
42873     
42874     
42875      /**
42876      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
42877      * 
42878      */
42879     cblack: false,
42880     /**
42881      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
42882      * 
42883      */
42884     cwhite: false,
42885     
42886      /**
42887      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
42888      * 
42889      */
42890     black: false,
42891     /**
42892      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
42893      * 
42894      */
42895     white: false,
42896     
42897     // id of frame..
42898     frameId: false,
42899     
42900     // private properties
42901     validationEvent : false,
42902     deferHeight: true,
42903     initialized : false,
42904     activated : false,
42905     
42906     onFocus : Roo.emptyFn,
42907     iframePad:3,
42908     hideMode:'offsets',
42909     
42910     actionMode : 'container', // defaults to hiding it...
42911     
42912     defaultAutoCreate : { // modified by initCompnoent..
42913         tag: "textarea",
42914         style:"width:500px;height:300px;",
42915         autocomplete: "new-password"
42916     },
42917
42918     // private
42919     initComponent : function(){
42920         this.addEvents({
42921             /**
42922              * @event initialize
42923              * Fires when the editor is fully initialized (including the iframe)
42924              * @param {HtmlEditor} this
42925              */
42926             initialize: true,
42927             /**
42928              * @event activate
42929              * Fires when the editor is first receives the focus. Any insertion must wait
42930              * until after this event.
42931              * @param {HtmlEditor} this
42932              */
42933             activate: true,
42934              /**
42935              * @event beforesync
42936              * Fires before the textarea is updated with content from the editor iframe. Return false
42937              * to cancel the sync.
42938              * @param {HtmlEditor} this
42939              * @param {String} html
42940              */
42941             beforesync: true,
42942              /**
42943              * @event beforepush
42944              * Fires before the iframe editor is updated with content from the textarea. Return false
42945              * to cancel the push.
42946              * @param {HtmlEditor} this
42947              * @param {String} html
42948              */
42949             beforepush: true,
42950              /**
42951              * @event sync
42952              * Fires when the textarea is updated with content from the editor iframe.
42953              * @param {HtmlEditor} this
42954              * @param {String} html
42955              */
42956             sync: true,
42957              /**
42958              * @event push
42959              * Fires when the iframe editor is updated with content from the textarea.
42960              * @param {HtmlEditor} this
42961              * @param {String} html
42962              */
42963             push: true,
42964              /**
42965              * @event editmodechange
42966              * Fires when the editor switches edit modes
42967              * @param {HtmlEditor} this
42968              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42969              */
42970             editmodechange: true,
42971             /**
42972              * @event editorevent
42973              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42974              * @param {HtmlEditor} this
42975              */
42976             editorevent: true,
42977             /**
42978              * @event firstfocus
42979              * Fires when on first focus - needed by toolbars..
42980              * @param {HtmlEditor} this
42981              */
42982             firstfocus: true,
42983             /**
42984              * @event autosave
42985              * Auto save the htmlEditor value as a file into Events
42986              * @param {HtmlEditor} this
42987              */
42988             autosave: true,
42989             /**
42990              * @event savedpreview
42991              * preview the saved version of htmlEditor
42992              * @param {HtmlEditor} this
42993              */
42994             savedpreview: true,
42995             
42996             /**
42997             * @event stylesheetsclick
42998             * Fires when press the Sytlesheets button
42999             * @param {Roo.HtmlEditorCore} this
43000             */
43001             stylesheetsclick: true
43002         });
43003         this.defaultAutoCreate =  {
43004             tag: "textarea",
43005             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
43006             autocomplete: "new-password"
43007         };
43008     },
43009
43010     /**
43011      * Protected method that will not generally be called directly. It
43012      * is called when the editor creates its toolbar. Override this method if you need to
43013      * add custom toolbar buttons.
43014      * @param {HtmlEditor} editor
43015      */
43016     createToolbar : function(editor){
43017         Roo.log("create toolbars");
43018         if (!editor.toolbars || !editor.toolbars.length) {
43019             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
43020         }
43021         
43022         for (var i =0 ; i < editor.toolbars.length;i++) {
43023             editor.toolbars[i] = Roo.factory(
43024                     typeof(editor.toolbars[i]) == 'string' ?
43025                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
43026                 Roo.form.HtmlEditor);
43027             editor.toolbars[i].init(editor);
43028         }
43029          
43030         
43031     },
43032
43033      
43034     // private
43035     onRender : function(ct, position)
43036     {
43037         var _t = this;
43038         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
43039         
43040         this.wrap = this.el.wrap({
43041             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
43042         });
43043         
43044         this.editorcore.onRender(ct, position);
43045          
43046         if (this.resizable) {
43047             this.resizeEl = new Roo.Resizable(this.wrap, {
43048                 pinned : true,
43049                 wrap: true,
43050                 dynamic : true,
43051                 minHeight : this.height,
43052                 height: this.height,
43053                 handles : this.resizable,
43054                 width: this.width,
43055                 listeners : {
43056                     resize : function(r, w, h) {
43057                         _t.onResize(w,h); // -something
43058                     }
43059                 }
43060             });
43061             
43062         }
43063         this.createToolbar(this);
43064        
43065         
43066         if(!this.width){
43067             this.setSize(this.wrap.getSize());
43068         }
43069         if (this.resizeEl) {
43070             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
43071             // should trigger onReize..
43072         }
43073         
43074         this.keyNav = new Roo.KeyNav(this.el, {
43075             
43076             "tab" : function(e){
43077                 e.preventDefault();
43078                 
43079                 var value = this.getValue();
43080                 
43081                 var start = this.el.dom.selectionStart;
43082                 var end = this.el.dom.selectionEnd;
43083                 
43084                 if(!e.shiftKey){
43085                     
43086                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
43087                     this.el.dom.setSelectionRange(end + 1, end + 1);
43088                     return;
43089                 }
43090                 
43091                 var f = value.substring(0, start).split("\t");
43092                 
43093                 if(f.pop().length != 0){
43094                     return;
43095                 }
43096                 
43097                 this.setValue(f.join("\t") + value.substring(end));
43098                 this.el.dom.setSelectionRange(start - 1, start - 1);
43099                 
43100             },
43101             
43102             "home" : function(e){
43103                 e.preventDefault();
43104                 
43105                 var curr = this.el.dom.selectionStart;
43106                 var lines = this.getValue().split("\n");
43107                 
43108                 if(!lines.length){
43109                     return;
43110                 }
43111                 
43112                 if(e.ctrlKey){
43113                     this.el.dom.setSelectionRange(0, 0);
43114                     return;
43115                 }
43116                 
43117                 var pos = 0;
43118                 
43119                 for (var i = 0; i < lines.length;i++) {
43120                     pos += lines[i].length;
43121                     
43122                     if(i != 0){
43123                         pos += 1;
43124                     }
43125                     
43126                     if(pos < curr){
43127                         continue;
43128                     }
43129                     
43130                     pos -= lines[i].length;
43131                     
43132                     break;
43133                 }
43134                 
43135                 if(!e.shiftKey){
43136                     this.el.dom.setSelectionRange(pos, pos);
43137                     return;
43138                 }
43139                 
43140                 this.el.dom.selectionStart = pos;
43141                 this.el.dom.selectionEnd = curr;
43142             },
43143             
43144             "end" : function(e){
43145                 e.preventDefault();
43146                 
43147                 var curr = this.el.dom.selectionStart;
43148                 var lines = this.getValue().split("\n");
43149                 
43150                 if(!lines.length){
43151                     return;
43152                 }
43153                 
43154                 if(e.ctrlKey){
43155                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
43156                     return;
43157                 }
43158                 
43159                 var pos = 0;
43160                 
43161                 for (var i = 0; i < lines.length;i++) {
43162                     
43163                     pos += lines[i].length;
43164                     
43165                     if(i != 0){
43166                         pos += 1;
43167                     }
43168                     
43169                     if(pos < curr){
43170                         continue;
43171                     }
43172                     
43173                     break;
43174                 }
43175                 
43176                 if(!e.shiftKey){
43177                     this.el.dom.setSelectionRange(pos, pos);
43178                     return;
43179                 }
43180                 
43181                 this.el.dom.selectionStart = curr;
43182                 this.el.dom.selectionEnd = pos;
43183             },
43184
43185             scope : this,
43186
43187             doRelay : function(foo, bar, hname){
43188                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43189             },
43190
43191             forceKeyDown: true
43192         });
43193         
43194 //        if(this.autosave && this.w){
43195 //            this.autoSaveFn = setInterval(this.autosave, 1000);
43196 //        }
43197     },
43198
43199     // private
43200     onResize : function(w, h)
43201     {
43202         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
43203         var ew = false;
43204         var eh = false;
43205         
43206         if(this.el ){
43207             if(typeof w == 'number'){
43208                 var aw = w - this.wrap.getFrameWidth('lr');
43209                 this.el.setWidth(this.adjustWidth('textarea', aw));
43210                 ew = aw;
43211             }
43212             if(typeof h == 'number'){
43213                 var tbh = 0;
43214                 for (var i =0; i < this.toolbars.length;i++) {
43215                     // fixme - ask toolbars for heights?
43216                     tbh += this.toolbars[i].tb.el.getHeight();
43217                     if (this.toolbars[i].footer) {
43218                         tbh += this.toolbars[i].footer.el.getHeight();
43219                     }
43220                 }
43221                 
43222                 
43223                 
43224                 
43225                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
43226                 ah -= 5; // knock a few pixes off for look..
43227 //                Roo.log(ah);
43228                 this.el.setHeight(this.adjustWidth('textarea', ah));
43229                 var eh = ah;
43230             }
43231         }
43232         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
43233         this.editorcore.onResize(ew,eh);
43234         
43235     },
43236
43237     /**
43238      * Toggles the editor between standard and source edit mode.
43239      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43240      */
43241     toggleSourceEdit : function(sourceEditMode)
43242     {
43243         this.editorcore.toggleSourceEdit(sourceEditMode);
43244         
43245         if(this.editorcore.sourceEditMode){
43246             Roo.log('editor - showing textarea');
43247             
43248 //            Roo.log('in');
43249 //            Roo.log(this.syncValue());
43250             this.editorcore.syncValue();
43251             this.el.removeClass('x-hidden');
43252             this.el.dom.removeAttribute('tabIndex');
43253             this.el.focus();
43254             
43255             for (var i = 0; i < this.toolbars.length; i++) {
43256                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43257                     this.toolbars[i].tb.hide();
43258                     this.toolbars[i].footer.hide();
43259                 }
43260             }
43261             
43262         }else{
43263             Roo.log('editor - hiding textarea');
43264 //            Roo.log('out')
43265 //            Roo.log(this.pushValue()); 
43266             this.editorcore.pushValue();
43267             
43268             this.el.addClass('x-hidden');
43269             this.el.dom.setAttribute('tabIndex', -1);
43270             
43271             for (var i = 0; i < this.toolbars.length; i++) {
43272                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43273                     this.toolbars[i].tb.show();
43274                     this.toolbars[i].footer.show();
43275                 }
43276             }
43277             
43278             //this.deferFocus();
43279         }
43280         
43281         this.setSize(this.wrap.getSize());
43282         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
43283         
43284         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
43285     },
43286  
43287     // private (for BoxComponent)
43288     adjustSize : Roo.BoxComponent.prototype.adjustSize,
43289
43290     // private (for BoxComponent)
43291     getResizeEl : function(){
43292         return this.wrap;
43293     },
43294
43295     // private (for BoxComponent)
43296     getPositionEl : function(){
43297         return this.wrap;
43298     },
43299
43300     // private
43301     initEvents : function(){
43302         this.originalValue = this.getValue();
43303     },
43304
43305     /**
43306      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43307      * @method
43308      */
43309     markInvalid : Roo.emptyFn,
43310     /**
43311      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43312      * @method
43313      */
43314     clearInvalid : Roo.emptyFn,
43315
43316     setValue : function(v){
43317         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
43318         this.editorcore.pushValue();
43319     },
43320
43321      
43322     // private
43323     deferFocus : function(){
43324         this.focus.defer(10, this);
43325     },
43326
43327     // doc'ed in Field
43328     focus : function(){
43329         this.editorcore.focus();
43330         
43331     },
43332       
43333
43334     // private
43335     onDestroy : function(){
43336         
43337         
43338         
43339         if(this.rendered){
43340             
43341             for (var i =0; i < this.toolbars.length;i++) {
43342                 // fixme - ask toolbars for heights?
43343                 this.toolbars[i].onDestroy();
43344             }
43345             
43346             this.wrap.dom.innerHTML = '';
43347             this.wrap.remove();
43348         }
43349     },
43350
43351     // private
43352     onFirstFocus : function(){
43353         //Roo.log("onFirstFocus");
43354         this.editorcore.onFirstFocus();
43355          for (var i =0; i < this.toolbars.length;i++) {
43356             this.toolbars[i].onFirstFocus();
43357         }
43358         
43359     },
43360     
43361     // private
43362     syncValue : function()
43363     {
43364         this.editorcore.syncValue();
43365     },
43366     
43367     pushValue : function()
43368     {
43369         this.editorcore.pushValue();
43370     },
43371     
43372     setStylesheets : function(stylesheets)
43373     {
43374         this.editorcore.setStylesheets(stylesheets);
43375     },
43376     
43377     removeStylesheets : function()
43378     {
43379         this.editorcore.removeStylesheets();
43380     }
43381      
43382     
43383     // hide stuff that is not compatible
43384     /**
43385      * @event blur
43386      * @hide
43387      */
43388     /**
43389      * @event change
43390      * @hide
43391      */
43392     /**
43393      * @event focus
43394      * @hide
43395      */
43396     /**
43397      * @event specialkey
43398      * @hide
43399      */
43400     /**
43401      * @cfg {String} fieldClass @hide
43402      */
43403     /**
43404      * @cfg {String} focusClass @hide
43405      */
43406     /**
43407      * @cfg {String} autoCreate @hide
43408      */
43409     /**
43410      * @cfg {String} inputType @hide
43411      */
43412     /**
43413      * @cfg {String} invalidClass @hide
43414      */
43415     /**
43416      * @cfg {String} invalidText @hide
43417      */
43418     /**
43419      * @cfg {String} msgFx @hide
43420      */
43421     /**
43422      * @cfg {String} validateOnBlur @hide
43423      */
43424 });
43425  
43426     // <script type="text/javascript">
43427 /*
43428  * Based on
43429  * Ext JS Library 1.1.1
43430  * Copyright(c) 2006-2007, Ext JS, LLC.
43431  *  
43432  
43433  */
43434
43435 /**
43436  * @class Roo.form.HtmlEditorToolbar1
43437  * Basic Toolbar
43438  * 
43439  * Usage:
43440  *
43441  new Roo.form.HtmlEditor({
43442     ....
43443     toolbars : [
43444         new Roo.form.HtmlEditorToolbar1({
43445             disable : { fonts: 1 , format: 1, ..., ... , ...],
43446             btns : [ .... ]
43447         })
43448     }
43449      
43450  * 
43451  * @cfg {Object} disable List of elements to disable..
43452  * @cfg {Array} btns List of additional buttons.
43453  * 
43454  * 
43455  * NEEDS Extra CSS? 
43456  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
43457  */
43458  
43459 Roo.form.HtmlEditor.ToolbarStandard = function(config)
43460 {
43461     
43462     Roo.apply(this, config);
43463     
43464     // default disabled, based on 'good practice'..
43465     this.disable = this.disable || {};
43466     Roo.applyIf(this.disable, {
43467         fontSize : true,
43468         colors : true,
43469         specialElements : true
43470     });
43471     
43472     
43473     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43474     // dont call parent... till later.
43475 }
43476
43477 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
43478     
43479     tb: false,
43480     
43481     rendered: false,
43482     
43483     editor : false,
43484     editorcore : false,
43485     /**
43486      * @cfg {Object} disable  List of toolbar elements to disable
43487          
43488      */
43489     disable : false,
43490     
43491     
43492      /**
43493      * @cfg {String} createLinkText The default text for the create link prompt
43494      */
43495     createLinkText : 'Please enter the URL for the link:',
43496     /**
43497      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
43498      */
43499     defaultLinkValue : 'http:/'+'/',
43500    
43501     
43502       /**
43503      * @cfg {Array} fontFamilies An array of available font families
43504      */
43505     fontFamilies : [
43506         'Arial',
43507         'Courier New',
43508         'Tahoma',
43509         'Times New Roman',
43510         'Verdana'
43511     ],
43512     
43513     specialChars : [
43514            "&#169;",
43515           "&#174;",     
43516           "&#8482;",    
43517           "&#163;" ,    
43518          // "&#8212;",    
43519           "&#8230;",    
43520           "&#247;" ,    
43521         //  "&#225;" ,     ?? a acute?
43522            "&#8364;"    , //Euro
43523        //   "&#8220;"    ,
43524         //  "&#8221;"    ,
43525         //  "&#8226;"    ,
43526           "&#176;"  //   , // degrees
43527
43528          // "&#233;"     , // e ecute
43529          // "&#250;"     , // u ecute?
43530     ],
43531     
43532     specialElements : [
43533         {
43534             text: "Insert Table",
43535             xtype: 'MenuItem',
43536             xns : Roo.Menu,
43537             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
43538                 
43539         },
43540         {    
43541             text: "Insert Image",
43542             xtype: 'MenuItem',
43543             xns : Roo.Menu,
43544             ihtml : '<img src="about:blank"/>'
43545             
43546         }
43547         
43548          
43549     ],
43550     
43551     
43552     inputElements : [ 
43553             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
43554             "input:submit", "input:button", "select", "textarea", "label" ],
43555     formats : [
43556         ["p"] ,  
43557         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
43558         ["pre"],[ "code"], 
43559         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
43560         ['div'],['span']
43561     ],
43562     
43563     cleanStyles : [
43564         "font-size"
43565     ],
43566      /**
43567      * @cfg {String} defaultFont default font to use.
43568      */
43569     defaultFont: 'tahoma',
43570    
43571     fontSelect : false,
43572     
43573     
43574     formatCombo : false,
43575     
43576     init : function(editor)
43577     {
43578         this.editor = editor;
43579         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43580         var editorcore = this.editorcore;
43581         
43582         var _t = this;
43583         
43584         var fid = editorcore.frameId;
43585         var etb = this;
43586         function btn(id, toggle, handler){
43587             var xid = fid + '-'+ id ;
43588             return {
43589                 id : xid,
43590                 cmd : id,
43591                 cls : 'x-btn-icon x-edit-'+id,
43592                 enableToggle:toggle !== false,
43593                 scope: _t, // was editor...
43594                 handler:handler||_t.relayBtnCmd,
43595                 clickEvent:'mousedown',
43596                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43597                 tabIndex:-1
43598             };
43599         }
43600         
43601         
43602         
43603         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43604         this.tb = tb;
43605          // stop form submits
43606         tb.el.on('click', function(e){
43607             e.preventDefault(); // what does this do?
43608         });
43609
43610         if(!this.disable.font) { // && !Roo.isSafari){
43611             /* why no safari for fonts 
43612             editor.fontSelect = tb.el.createChild({
43613                 tag:'select',
43614                 tabIndex: -1,
43615                 cls:'x-font-select',
43616                 html: this.createFontOptions()
43617             });
43618             
43619             editor.fontSelect.on('change', function(){
43620                 var font = editor.fontSelect.dom.value;
43621                 editor.relayCmd('fontname', font);
43622                 editor.deferFocus();
43623             }, editor);
43624             
43625             tb.add(
43626                 editor.fontSelect.dom,
43627                 '-'
43628             );
43629             */
43630             
43631         };
43632         if(!this.disable.formats){
43633             this.formatCombo = new Roo.form.ComboBox({
43634                 store: new Roo.data.SimpleStore({
43635                     id : 'tag',
43636                     fields: ['tag'],
43637                     data : this.formats // from states.js
43638                 }),
43639                 blockFocus : true,
43640                 name : '',
43641                 //autoCreate : {tag: "div",  size: "20"},
43642                 displayField:'tag',
43643                 typeAhead: false,
43644                 mode: 'local',
43645                 editable : false,
43646                 triggerAction: 'all',
43647                 emptyText:'Add tag',
43648                 selectOnFocus:true,
43649                 width:135,
43650                 listeners : {
43651                     'select': function(c, r, i) {
43652                         editorcore.insertTag(r.get('tag'));
43653                         editor.focus();
43654                     }
43655                 }
43656
43657             });
43658             tb.addField(this.formatCombo);
43659             
43660         }
43661         
43662         if(!this.disable.format){
43663             tb.add(
43664                 btn('bold'),
43665                 btn('italic'),
43666                 btn('underline'),
43667                 btn('strikethrough')
43668             );
43669         };
43670         if(!this.disable.fontSize){
43671             tb.add(
43672                 '-',
43673                 
43674                 
43675                 btn('increasefontsize', false, editorcore.adjustFont),
43676                 btn('decreasefontsize', false, editorcore.adjustFont)
43677             );
43678         };
43679         
43680         
43681         if(!this.disable.colors){
43682             tb.add(
43683                 '-', {
43684                     id:editorcore.frameId +'-forecolor',
43685                     cls:'x-btn-icon x-edit-forecolor',
43686                     clickEvent:'mousedown',
43687                     tooltip: this.buttonTips['forecolor'] || undefined,
43688                     tabIndex:-1,
43689                     menu : new Roo.menu.ColorMenu({
43690                         allowReselect: true,
43691                         focus: Roo.emptyFn,
43692                         value:'000000',
43693                         plain:true,
43694                         selectHandler: function(cp, color){
43695                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43696                             editor.deferFocus();
43697                         },
43698                         scope: editorcore,
43699                         clickEvent:'mousedown'
43700                     })
43701                 }, {
43702                     id:editorcore.frameId +'backcolor',
43703                     cls:'x-btn-icon x-edit-backcolor',
43704                     clickEvent:'mousedown',
43705                     tooltip: this.buttonTips['backcolor'] || undefined,
43706                     tabIndex:-1,
43707                     menu : new Roo.menu.ColorMenu({
43708                         focus: Roo.emptyFn,
43709                         value:'FFFFFF',
43710                         plain:true,
43711                         allowReselect: true,
43712                         selectHandler: function(cp, color){
43713                             if(Roo.isGecko){
43714                                 editorcore.execCmd('useCSS', false);
43715                                 editorcore.execCmd('hilitecolor', color);
43716                                 editorcore.execCmd('useCSS', true);
43717                                 editor.deferFocus();
43718                             }else{
43719                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43720                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43721                                 editor.deferFocus();
43722                             }
43723                         },
43724                         scope:editorcore,
43725                         clickEvent:'mousedown'
43726                     })
43727                 }
43728             );
43729         };
43730         // now add all the items...
43731         
43732
43733         if(!this.disable.alignments){
43734             tb.add(
43735                 '-',
43736                 btn('justifyleft'),
43737                 btn('justifycenter'),
43738                 btn('justifyright')
43739             );
43740         };
43741
43742         //if(!Roo.isSafari){
43743             if(!this.disable.links){
43744                 tb.add(
43745                     '-',
43746                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43747                 );
43748             };
43749
43750             if(!this.disable.lists){
43751                 tb.add(
43752                     '-',
43753                     btn('insertorderedlist'),
43754                     btn('insertunorderedlist')
43755                 );
43756             }
43757             if(!this.disable.sourceEdit){
43758                 tb.add(
43759                     '-',
43760                     btn('sourceedit', true, function(btn){
43761                         this.toggleSourceEdit(btn.pressed);
43762                     })
43763                 );
43764             }
43765         //}
43766         
43767         var smenu = { };
43768         // special menu.. - needs to be tidied up..
43769         if (!this.disable.special) {
43770             smenu = {
43771                 text: "&#169;",
43772                 cls: 'x-edit-none',
43773                 
43774                 menu : {
43775                     items : []
43776                 }
43777             };
43778             for (var i =0; i < this.specialChars.length; i++) {
43779                 smenu.menu.items.push({
43780                     
43781                     html: this.specialChars[i],
43782                     handler: function(a,b) {
43783                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43784                         //editor.insertAtCursor(a.html);
43785                         
43786                     },
43787                     tabIndex:-1
43788                 });
43789             }
43790             
43791             
43792             tb.add(smenu);
43793             
43794             
43795         }
43796         
43797         var cmenu = { };
43798         if (!this.disable.cleanStyles) {
43799             cmenu = {
43800                 cls: 'x-btn-icon x-btn-clear',
43801                 
43802                 menu : {
43803                     items : []
43804                 }
43805             };
43806             for (var i =0; i < this.cleanStyles.length; i++) {
43807                 cmenu.menu.items.push({
43808                     actiontype : this.cleanStyles[i],
43809                     html: 'Remove ' + this.cleanStyles[i],
43810                     handler: function(a,b) {
43811 //                        Roo.log(a);
43812 //                        Roo.log(b);
43813                         var c = Roo.get(editorcore.doc.body);
43814                         c.select('[style]').each(function(s) {
43815                             s.dom.style.removeProperty(a.actiontype);
43816                         });
43817                         editorcore.syncValue();
43818                     },
43819                     tabIndex:-1
43820                 });
43821             }
43822              cmenu.menu.items.push({
43823                 actiontype : 'tablewidths',
43824                 html: 'Remove Table Widths',
43825                 handler: function(a,b) {
43826                     editorcore.cleanTableWidths();
43827                     editorcore.syncValue();
43828                 },
43829                 tabIndex:-1
43830             });
43831             cmenu.menu.items.push({
43832                 actiontype : 'word',
43833                 html: 'Remove MS Word Formating',
43834                 handler: function(a,b) {
43835                     editorcore.cleanWord();
43836                     editorcore.syncValue();
43837                 },
43838                 tabIndex:-1
43839             });
43840             
43841             cmenu.menu.items.push({
43842                 actiontype : 'all',
43843                 html: 'Remove All Styles',
43844                 handler: function(a,b) {
43845                     
43846                     var c = Roo.get(editorcore.doc.body);
43847                     c.select('[style]').each(function(s) {
43848                         s.dom.removeAttribute('style');
43849                     });
43850                     editorcore.syncValue();
43851                 },
43852                 tabIndex:-1
43853             });
43854             
43855             cmenu.menu.items.push({
43856                 actiontype : 'all',
43857                 html: 'Remove All CSS Classes',
43858                 handler: function(a,b) {
43859                     
43860                     var c = Roo.get(editorcore.doc.body);
43861                     c.select('[class]').each(function(s) {
43862                         s.dom.className = '';
43863                     });
43864                     editorcore.syncValue();
43865                 },
43866                 tabIndex:-1
43867             });
43868             
43869              cmenu.menu.items.push({
43870                 actiontype : 'tidy',
43871                 html: 'Tidy HTML Source',
43872                 handler: function(a,b) {
43873                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43874                     editorcore.syncValue();
43875                 },
43876                 tabIndex:-1
43877             });
43878             
43879             
43880             tb.add(cmenu);
43881         }
43882          
43883         if (!this.disable.specialElements) {
43884             var semenu = {
43885                 text: "Other;",
43886                 cls: 'x-edit-none',
43887                 menu : {
43888                     items : []
43889                 }
43890             };
43891             for (var i =0; i < this.specialElements.length; i++) {
43892                 semenu.menu.items.push(
43893                     Roo.apply({ 
43894                         handler: function(a,b) {
43895                             editor.insertAtCursor(this.ihtml);
43896                         }
43897                     }, this.specialElements[i])
43898                 );
43899                     
43900             }
43901             
43902             tb.add(semenu);
43903             
43904             
43905         }
43906          
43907         
43908         if (this.btns) {
43909             for(var i =0; i< this.btns.length;i++) {
43910                 var b = Roo.factory(this.btns[i],Roo.form);
43911                 b.cls =  'x-edit-none';
43912                 
43913                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
43914                     b.cls += ' x-init-enable';
43915                 }
43916                 
43917                 b.scope = editorcore;
43918                 tb.add(b);
43919             }
43920         
43921         }
43922         
43923         
43924         
43925         // disable everything...
43926         
43927         this.tb.items.each(function(item){
43928             
43929            if(
43930                 item.id != editorcore.frameId+ '-sourceedit' && 
43931                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
43932             ){
43933                 
43934                 item.disable();
43935             }
43936         });
43937         this.rendered = true;
43938         
43939         // the all the btns;
43940         editor.on('editorevent', this.updateToolbar, this);
43941         // other toolbars need to implement this..
43942         //editor.on('editmodechange', this.updateToolbar, this);
43943     },
43944     
43945     
43946     relayBtnCmd : function(btn) {
43947         this.editorcore.relayCmd(btn.cmd);
43948     },
43949     // private used internally
43950     createLink : function(){
43951         Roo.log("create link?");
43952         var url = prompt(this.createLinkText, this.defaultLinkValue);
43953         if(url && url != 'http:/'+'/'){
43954             this.editorcore.relayCmd('createlink', url);
43955         }
43956     },
43957
43958     
43959     /**
43960      * Protected method that will not generally be called directly. It triggers
43961      * a toolbar update by reading the markup state of the current selection in the editor.
43962      */
43963     updateToolbar: function(){
43964
43965         if(!this.editorcore.activated){
43966             this.editor.onFirstFocus();
43967             return;
43968         }
43969
43970         var btns = this.tb.items.map, 
43971             doc = this.editorcore.doc,
43972             frameId = this.editorcore.frameId;
43973
43974         if(!this.disable.font && !Roo.isSafari){
43975             /*
43976             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43977             if(name != this.fontSelect.dom.value){
43978                 this.fontSelect.dom.value = name;
43979             }
43980             */
43981         }
43982         if(!this.disable.format){
43983             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43984             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43985             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43986             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
43987         }
43988         if(!this.disable.alignments){
43989             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43990             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43991             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43992         }
43993         if(!Roo.isSafari && !this.disable.lists){
43994             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43995             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43996         }
43997         
43998         var ans = this.editorcore.getAllAncestors();
43999         if (this.formatCombo) {
44000             
44001             
44002             var store = this.formatCombo.store;
44003             this.formatCombo.setValue("");
44004             for (var i =0; i < ans.length;i++) {
44005                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
44006                     // select it..
44007                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
44008                     break;
44009                 }
44010             }
44011         }
44012         
44013         
44014         
44015         // hides menus... - so this cant be on a menu...
44016         Roo.menu.MenuMgr.hideAll();
44017
44018         //this.editorsyncValue();
44019     },
44020    
44021     
44022     createFontOptions : function(){
44023         var buf = [], fs = this.fontFamilies, ff, lc;
44024         
44025         
44026         
44027         for(var i = 0, len = fs.length; i< len; i++){
44028             ff = fs[i];
44029             lc = ff.toLowerCase();
44030             buf.push(
44031                 '<option value="',lc,'" style="font-family:',ff,';"',
44032                     (this.defaultFont == lc ? ' selected="true">' : '>'),
44033                     ff,
44034                 '</option>'
44035             );
44036         }
44037         return buf.join('');
44038     },
44039     
44040     toggleSourceEdit : function(sourceEditMode){
44041         
44042         Roo.log("toolbar toogle");
44043         if(sourceEditMode === undefined){
44044             sourceEditMode = !this.sourceEditMode;
44045         }
44046         this.sourceEditMode = sourceEditMode === true;
44047         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
44048         // just toggle the button?
44049         if(btn.pressed !== this.sourceEditMode){
44050             btn.toggle(this.sourceEditMode);
44051             return;
44052         }
44053         
44054         if(sourceEditMode){
44055             Roo.log("disabling buttons");
44056             this.tb.items.each(function(item){
44057                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
44058                     item.disable();
44059                 }
44060             });
44061           
44062         }else{
44063             Roo.log("enabling buttons");
44064             if(this.editorcore.initialized){
44065                 this.tb.items.each(function(item){
44066                     item.enable();
44067                 });
44068             }
44069             
44070         }
44071         Roo.log("calling toggole on editor");
44072         // tell the editor that it's been pressed..
44073         this.editor.toggleSourceEdit(sourceEditMode);
44074        
44075     },
44076      /**
44077      * Object collection of toolbar tooltips for the buttons in the editor. The key
44078      * is the command id associated with that button and the value is a valid QuickTips object.
44079      * For example:
44080 <pre><code>
44081 {
44082     bold : {
44083         title: 'Bold (Ctrl+B)',
44084         text: 'Make the selected text bold.',
44085         cls: 'x-html-editor-tip'
44086     },
44087     italic : {
44088         title: 'Italic (Ctrl+I)',
44089         text: 'Make the selected text italic.',
44090         cls: 'x-html-editor-tip'
44091     },
44092     ...
44093 </code></pre>
44094     * @type Object
44095      */
44096     buttonTips : {
44097         bold : {
44098             title: 'Bold (Ctrl+B)',
44099             text: 'Make the selected text bold.',
44100             cls: 'x-html-editor-tip'
44101         },
44102         italic : {
44103             title: 'Italic (Ctrl+I)',
44104             text: 'Make the selected text italic.',
44105             cls: 'x-html-editor-tip'
44106         },
44107         underline : {
44108             title: 'Underline (Ctrl+U)',
44109             text: 'Underline the selected text.',
44110             cls: 'x-html-editor-tip'
44111         },
44112         strikethrough : {
44113             title: 'Strikethrough',
44114             text: 'Strikethrough the selected text.',
44115             cls: 'x-html-editor-tip'
44116         },
44117         increasefontsize : {
44118             title: 'Grow Text',
44119             text: 'Increase the font size.',
44120             cls: 'x-html-editor-tip'
44121         },
44122         decreasefontsize : {
44123             title: 'Shrink Text',
44124             text: 'Decrease the font size.',
44125             cls: 'x-html-editor-tip'
44126         },
44127         backcolor : {
44128             title: 'Text Highlight Color',
44129             text: 'Change the background color of the selected text.',
44130             cls: 'x-html-editor-tip'
44131         },
44132         forecolor : {
44133             title: 'Font Color',
44134             text: 'Change the color of the selected text.',
44135             cls: 'x-html-editor-tip'
44136         },
44137         justifyleft : {
44138             title: 'Align Text Left',
44139             text: 'Align text to the left.',
44140             cls: 'x-html-editor-tip'
44141         },
44142         justifycenter : {
44143             title: 'Center Text',
44144             text: 'Center text in the editor.',
44145             cls: 'x-html-editor-tip'
44146         },
44147         justifyright : {
44148             title: 'Align Text Right',
44149             text: 'Align text to the right.',
44150             cls: 'x-html-editor-tip'
44151         },
44152         insertunorderedlist : {
44153             title: 'Bullet List',
44154             text: 'Start a bulleted list.',
44155             cls: 'x-html-editor-tip'
44156         },
44157         insertorderedlist : {
44158             title: 'Numbered List',
44159             text: 'Start a numbered list.',
44160             cls: 'x-html-editor-tip'
44161         },
44162         createlink : {
44163             title: 'Hyperlink',
44164             text: 'Make the selected text a hyperlink.',
44165             cls: 'x-html-editor-tip'
44166         },
44167         sourceedit : {
44168             title: 'Source Edit',
44169             text: 'Switch to source editing mode.',
44170             cls: 'x-html-editor-tip'
44171         }
44172     },
44173     // private
44174     onDestroy : function(){
44175         if(this.rendered){
44176             
44177             this.tb.items.each(function(item){
44178                 if(item.menu){
44179                     item.menu.removeAll();
44180                     if(item.menu.el){
44181                         item.menu.el.destroy();
44182                     }
44183                 }
44184                 item.destroy();
44185             });
44186              
44187         }
44188     },
44189     onFirstFocus: function() {
44190         this.tb.items.each(function(item){
44191            item.enable();
44192         });
44193     }
44194 });
44195
44196
44197
44198
44199 // <script type="text/javascript">
44200 /*
44201  * Based on
44202  * Ext JS Library 1.1.1
44203  * Copyright(c) 2006-2007, Ext JS, LLC.
44204  *  
44205  
44206  */
44207
44208  
44209 /**
44210  * @class Roo.form.HtmlEditor.ToolbarContext
44211  * Context Toolbar
44212  * 
44213  * Usage:
44214  *
44215  new Roo.form.HtmlEditor({
44216     ....
44217     toolbars : [
44218         { xtype: 'ToolbarStandard', styles : {} }
44219         { xtype: 'ToolbarContext', disable : {} }
44220     ]
44221 })
44222
44223      
44224  * 
44225  * @config : {Object} disable List of elements to disable.. (not done yet.)
44226  * @config : {Object} styles  Map of styles available.
44227  * 
44228  */
44229
44230 Roo.form.HtmlEditor.ToolbarContext = function(config)
44231 {
44232     
44233     Roo.apply(this, config);
44234     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
44235     // dont call parent... till later.
44236     this.styles = this.styles || {};
44237 }
44238
44239  
44240
44241 Roo.form.HtmlEditor.ToolbarContext.types = {
44242     'IMG' : {
44243         width : {
44244             title: "Width",
44245             width: 40
44246         },
44247         height:  {
44248             title: "Height",
44249             width: 40
44250         },
44251         align: {
44252             title: "Align",
44253             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
44254             width : 80
44255             
44256         },
44257         border: {
44258             title: "Border",
44259             width: 40
44260         },
44261         alt: {
44262             title: "Alt",
44263             width: 120
44264         },
44265         src : {
44266             title: "Src",
44267             width: 220
44268         }
44269         
44270     },
44271     'A' : {
44272         name : {
44273             title: "Name",
44274             width: 50
44275         },
44276         target:  {
44277             title: "Target",
44278             width: 120
44279         },
44280         href:  {
44281             title: "Href",
44282             width: 220
44283         } // border?
44284         
44285     },
44286     'TABLE' : {
44287         rows : {
44288             title: "Rows",
44289             width: 20
44290         },
44291         cols : {
44292             title: "Cols",
44293             width: 20
44294         },
44295         width : {
44296             title: "Width",
44297             width: 40
44298         },
44299         height : {
44300             title: "Height",
44301             width: 40
44302         },
44303         border : {
44304             title: "Border",
44305             width: 20
44306         }
44307     },
44308     'TD' : {
44309         width : {
44310             title: "Width",
44311             width: 40
44312         },
44313         height : {
44314             title: "Height",
44315             width: 40
44316         },   
44317         align: {
44318             title: "Align",
44319             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
44320             width: 80
44321         },
44322         valign: {
44323             title: "Valign",
44324             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
44325             width: 80
44326         },
44327         colspan: {
44328             title: "Colspan",
44329             width: 20
44330             
44331         },
44332          'font-family'  : {
44333             title : "Font",
44334             style : 'fontFamily',
44335             displayField: 'display',
44336             optname : 'font-family',
44337             width: 140
44338         }
44339     },
44340     'INPUT' : {
44341         name : {
44342             title: "name",
44343             width: 120
44344         },
44345         value : {
44346             title: "Value",
44347             width: 120
44348         },
44349         width : {
44350             title: "Width",
44351             width: 40
44352         }
44353     },
44354     'LABEL' : {
44355         'for' : {
44356             title: "For",
44357             width: 120
44358         }
44359     },
44360     'TEXTAREA' : {
44361           name : {
44362             title: "name",
44363             width: 120
44364         },
44365         rows : {
44366             title: "Rows",
44367             width: 20
44368         },
44369         cols : {
44370             title: "Cols",
44371             width: 20
44372         }
44373     },
44374     'SELECT' : {
44375         name : {
44376             title: "name",
44377             width: 120
44378         },
44379         selectoptions : {
44380             title: "Options",
44381             width: 200
44382         }
44383     },
44384     
44385     // should we really allow this??
44386     // should this just be 
44387     'BODY' : {
44388         title : {
44389             title: "Title",
44390             width: 200,
44391             disabled : true
44392         }
44393     },
44394     'SPAN' : {
44395         'font-family'  : {
44396             title : "Font",
44397             style : 'fontFamily',
44398             displayField: 'display',
44399             optname : 'font-family',
44400             width: 140
44401         }
44402     },
44403     'DIV' : {
44404         'font-family'  : {
44405             title : "Font",
44406             style : 'fontFamily',
44407             displayField: 'display',
44408             optname : 'font-family',
44409             width: 140
44410         }
44411     },
44412      'P' : {
44413         'font-family'  : {
44414             title : "Font",
44415             style : 'fontFamily',
44416             displayField: 'display',
44417             optname : 'font-family',
44418             width: 140
44419         }
44420     },
44421     
44422     '*' : {
44423         // empty..
44424     }
44425
44426 };
44427
44428 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
44429 Roo.form.HtmlEditor.ToolbarContext.stores = false;
44430
44431 Roo.form.HtmlEditor.ToolbarContext.options = {
44432         'font-family'  : [ 
44433                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
44434                 [ 'Courier New', 'Courier New'],
44435                 [ 'Tahoma', 'Tahoma'],
44436                 [ 'Times New Roman,serif', 'Times'],
44437                 [ 'Verdana','Verdana' ]
44438         ]
44439 };
44440
44441 // fixme - these need to be configurable..
44442  
44443
44444 //Roo.form.HtmlEditor.ToolbarContext.types
44445
44446
44447 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
44448     
44449     tb: false,
44450     
44451     rendered: false,
44452     
44453     editor : false,
44454     editorcore : false,
44455     /**
44456      * @cfg {Object} disable  List of toolbar elements to disable
44457          
44458      */
44459     disable : false,
44460     /**
44461      * @cfg {Object} styles List of styles 
44462      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
44463      *
44464      * These must be defined in the page, so they get rendered correctly..
44465      * .headline { }
44466      * TD.underline { }
44467      * 
44468      */
44469     styles : false,
44470     
44471     options: false,
44472     
44473     toolbars : false,
44474     
44475     init : function(editor)
44476     {
44477         this.editor = editor;
44478         this.editorcore = editor.editorcore ? editor.editorcore : editor;
44479         var editorcore = this.editorcore;
44480         
44481         var fid = editorcore.frameId;
44482         var etb = this;
44483         function btn(id, toggle, handler){
44484             var xid = fid + '-'+ id ;
44485             return {
44486                 id : xid,
44487                 cmd : id,
44488                 cls : 'x-btn-icon x-edit-'+id,
44489                 enableToggle:toggle !== false,
44490                 scope: editorcore, // was editor...
44491                 handler:handler||editorcore.relayBtnCmd,
44492                 clickEvent:'mousedown',
44493                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
44494                 tabIndex:-1
44495             };
44496         }
44497         // create a new element.
44498         var wdiv = editor.wrap.createChild({
44499                 tag: 'div'
44500             }, editor.wrap.dom.firstChild.nextSibling, true);
44501         
44502         // can we do this more than once??
44503         
44504          // stop form submits
44505       
44506  
44507         // disable everything...
44508         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44509         this.toolbars = {};
44510            
44511         for (var i in  ty) {
44512           
44513             this.toolbars[i] = this.buildToolbar(ty[i],i);
44514         }
44515         this.tb = this.toolbars.BODY;
44516         this.tb.el.show();
44517         this.buildFooter();
44518         this.footer.show();
44519         editor.on('hide', function( ) { this.footer.hide() }, this);
44520         editor.on('show', function( ) { this.footer.show() }, this);
44521         
44522          
44523         this.rendered = true;
44524         
44525         // the all the btns;
44526         editor.on('editorevent', this.updateToolbar, this);
44527         // other toolbars need to implement this..
44528         //editor.on('editmodechange', this.updateToolbar, this);
44529     },
44530     
44531     
44532     
44533     /**
44534      * Protected method that will not generally be called directly. It triggers
44535      * a toolbar update by reading the markup state of the current selection in the editor.
44536      *
44537      * Note you can force an update by calling on('editorevent', scope, false)
44538      */
44539     updateToolbar: function(editor,ev,sel){
44540
44541         //Roo.log(ev);
44542         // capture mouse up - this is handy for selecting images..
44543         // perhaps should go somewhere else...
44544         if(!this.editorcore.activated){
44545              this.editor.onFirstFocus();
44546             return;
44547         }
44548         
44549         
44550         
44551         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
44552         // selectNode - might want to handle IE?
44553         if (ev &&
44554             (ev.type == 'mouseup' || ev.type == 'click' ) &&
44555             ev.target && ev.target.tagName == 'IMG') {
44556             // they have click on an image...
44557             // let's see if we can change the selection...
44558             sel = ev.target;
44559          
44560               var nodeRange = sel.ownerDocument.createRange();
44561             try {
44562                 nodeRange.selectNode(sel);
44563             } catch (e) {
44564                 nodeRange.selectNodeContents(sel);
44565             }
44566             //nodeRange.collapse(true);
44567             var s = this.editorcore.win.getSelection();
44568             s.removeAllRanges();
44569             s.addRange(nodeRange);
44570         }  
44571         
44572       
44573         var updateFooter = sel ? false : true;
44574         
44575         
44576         var ans = this.editorcore.getAllAncestors();
44577         
44578         // pick
44579         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44580         
44581         if (!sel) { 
44582             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
44583             sel = sel ? sel : this.editorcore.doc.body;
44584             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
44585             
44586         }
44587         // pick a menu that exists..
44588         var tn = sel.tagName.toUpperCase();
44589         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
44590         
44591         tn = sel.tagName.toUpperCase();
44592         
44593         var lastSel = this.tb.selectedNode;
44594         
44595         this.tb.selectedNode = sel;
44596         
44597         // if current menu does not match..
44598         
44599         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
44600                 
44601             this.tb.el.hide();
44602             ///console.log("show: " + tn);
44603             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
44604             this.tb.el.show();
44605             // update name
44606             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
44607             
44608             
44609             // update attributes
44610             if (this.tb.fields) {
44611                 this.tb.fields.each(function(e) {
44612                     if (e.stylename) {
44613                         e.setValue(sel.style[e.stylename]);
44614                         return;
44615                     } 
44616                    e.setValue(sel.getAttribute(e.attrname));
44617                 });
44618             }
44619             
44620             var hasStyles = false;
44621             for(var i in this.styles) {
44622                 hasStyles = true;
44623                 break;
44624             }
44625             
44626             // update styles
44627             if (hasStyles) { 
44628                 var st = this.tb.fields.item(0);
44629                 
44630                 st.store.removeAll();
44631                
44632                 
44633                 var cn = sel.className.split(/\s+/);
44634                 
44635                 var avs = [];
44636                 if (this.styles['*']) {
44637                     
44638                     Roo.each(this.styles['*'], function(v) {
44639                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44640                     });
44641                 }
44642                 if (this.styles[tn]) { 
44643                     Roo.each(this.styles[tn], function(v) {
44644                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44645                     });
44646                 }
44647                 
44648                 st.store.loadData(avs);
44649                 st.collapse();
44650                 st.setValue(cn);
44651             }
44652             // flag our selected Node.
44653             this.tb.selectedNode = sel;
44654            
44655            
44656             Roo.menu.MenuMgr.hideAll();
44657
44658         }
44659         
44660         if (!updateFooter) {
44661             //this.footDisp.dom.innerHTML = ''; 
44662             return;
44663         }
44664         // update the footer
44665         //
44666         var html = '';
44667         
44668         this.footerEls = ans.reverse();
44669         Roo.each(this.footerEls, function(a,i) {
44670             if (!a) { return; }
44671             html += html.length ? ' &gt; '  :  '';
44672             
44673             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44674             
44675         });
44676        
44677         // 
44678         var sz = this.footDisp.up('td').getSize();
44679         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44680         this.footDisp.dom.style.marginLeft = '5px';
44681         
44682         this.footDisp.dom.style.overflow = 'hidden';
44683         
44684         this.footDisp.dom.innerHTML = html;
44685             
44686         //this.editorsyncValue();
44687     },
44688      
44689     
44690    
44691        
44692     // private
44693     onDestroy : function(){
44694         if(this.rendered){
44695             
44696             this.tb.items.each(function(item){
44697                 if(item.menu){
44698                     item.menu.removeAll();
44699                     if(item.menu.el){
44700                         item.menu.el.destroy();
44701                     }
44702                 }
44703                 item.destroy();
44704             });
44705              
44706         }
44707     },
44708     onFirstFocus: function() {
44709         // need to do this for all the toolbars..
44710         this.tb.items.each(function(item){
44711            item.enable();
44712         });
44713     },
44714     buildToolbar: function(tlist, nm)
44715     {
44716         var editor = this.editor;
44717         var editorcore = this.editorcore;
44718          // create a new element.
44719         var wdiv = editor.wrap.createChild({
44720                 tag: 'div'
44721             }, editor.wrap.dom.firstChild.nextSibling, true);
44722         
44723        
44724         var tb = new Roo.Toolbar(wdiv);
44725         // add the name..
44726         
44727         tb.add(nm+ ":&nbsp;");
44728         
44729         var styles = [];
44730         for(var i in this.styles) {
44731             styles.push(i);
44732         }
44733         
44734         // styles...
44735         if (styles && styles.length) {
44736             
44737             // this needs a multi-select checkbox...
44738             tb.addField( new Roo.form.ComboBox({
44739                 store: new Roo.data.SimpleStore({
44740                     id : 'val',
44741                     fields: ['val', 'selected'],
44742                     data : [] 
44743                 }),
44744                 name : '-roo-edit-className',
44745                 attrname : 'className',
44746                 displayField: 'val',
44747                 typeAhead: false,
44748                 mode: 'local',
44749                 editable : false,
44750                 triggerAction: 'all',
44751                 emptyText:'Select Style',
44752                 selectOnFocus:true,
44753                 width: 130,
44754                 listeners : {
44755                     'select': function(c, r, i) {
44756                         // initial support only for on class per el..
44757                         tb.selectedNode.className =  r ? r.get('val') : '';
44758                         editorcore.syncValue();
44759                     }
44760                 }
44761     
44762             }));
44763         }
44764         
44765         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44766         var tbops = tbc.options;
44767         
44768         for (var i in tlist) {
44769             
44770             var item = tlist[i];
44771             tb.add(item.title + ":&nbsp;");
44772             
44773             
44774             //optname == used so you can configure the options available..
44775             var opts = item.opts ? item.opts : false;
44776             if (item.optname) {
44777                 opts = tbops[item.optname];
44778            
44779             }
44780             
44781             if (opts) {
44782                 // opts == pulldown..
44783                 tb.addField( new Roo.form.ComboBox({
44784                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44785                         id : 'val',
44786                         fields: ['val', 'display'],
44787                         data : opts  
44788                     }),
44789                     name : '-roo-edit-' + i,
44790                     attrname : i,
44791                     stylename : item.style ? item.style : false,
44792                     displayField: item.displayField ? item.displayField : 'val',
44793                     valueField :  'val',
44794                     typeAhead: false,
44795                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44796                     editable : false,
44797                     triggerAction: 'all',
44798                     emptyText:'Select',
44799                     selectOnFocus:true,
44800                     width: item.width ? item.width  : 130,
44801                     listeners : {
44802                         'select': function(c, r, i) {
44803                             if (c.stylename) {
44804                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44805                                 return;
44806                             }
44807                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44808                         }
44809                     }
44810
44811                 }));
44812                 continue;
44813                     
44814                  
44815                 
44816                 tb.addField( new Roo.form.TextField({
44817                     name: i,
44818                     width: 100,
44819                     //allowBlank:false,
44820                     value: ''
44821                 }));
44822                 continue;
44823             }
44824             tb.addField( new Roo.form.TextField({
44825                 name: '-roo-edit-' + i,
44826                 attrname : i,
44827                 
44828                 width: item.width,
44829                 //allowBlank:true,
44830                 value: '',
44831                 listeners: {
44832                     'change' : function(f, nv, ov) {
44833                         tb.selectedNode.setAttribute(f.attrname, nv);
44834                     }
44835                 }
44836             }));
44837              
44838         }
44839         
44840         var _this = this;
44841         
44842         if(nm == 'BODY'){
44843             tb.addSeparator();
44844         
44845             tb.addButton( {
44846                 text: 'Stylesheets',
44847
44848                 listeners : {
44849                     click : function ()
44850                     {
44851                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
44852                     }
44853                 }
44854             });
44855         }
44856         
44857         tb.addFill();
44858         tb.addButton( {
44859             text: 'Remove Tag',
44860     
44861             listeners : {
44862                 click : function ()
44863                 {
44864                     // remove
44865                     // undo does not work.
44866                      
44867                     var sn = tb.selectedNode;
44868                     
44869                     var pn = sn.parentNode;
44870                     
44871                     var stn =  sn.childNodes[0];
44872                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44873                     while (sn.childNodes.length) {
44874                         var node = sn.childNodes[0];
44875                         sn.removeChild(node);
44876                         //Roo.log(node);
44877                         pn.insertBefore(node, sn);
44878                         
44879                     }
44880                     pn.removeChild(sn);
44881                     var range = editorcore.createRange();
44882         
44883                     range.setStart(stn,0);
44884                     range.setEnd(en,0); //????
44885                     //range.selectNode(sel);
44886                     
44887                     
44888                     var selection = editorcore.getSelection();
44889                     selection.removeAllRanges();
44890                     selection.addRange(range);
44891                     
44892                     
44893                     
44894                     //_this.updateToolbar(null, null, pn);
44895                     _this.updateToolbar(null, null, null);
44896                     _this.footDisp.dom.innerHTML = ''; 
44897                 }
44898             }
44899             
44900                     
44901                 
44902             
44903         });
44904         
44905         
44906         tb.el.on('click', function(e){
44907             e.preventDefault(); // what does this do?
44908         });
44909         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44910         tb.el.hide();
44911         tb.name = nm;
44912         // dont need to disable them... as they will get hidden
44913         return tb;
44914          
44915         
44916     },
44917     buildFooter : function()
44918     {
44919         
44920         var fel = this.editor.wrap.createChild();
44921         this.footer = new Roo.Toolbar(fel);
44922         // toolbar has scrolly on left / right?
44923         var footDisp= new Roo.Toolbar.Fill();
44924         var _t = this;
44925         this.footer.add(
44926             {
44927                 text : '&lt;',
44928                 xtype: 'Button',
44929                 handler : function() {
44930                     _t.footDisp.scrollTo('left',0,true)
44931                 }
44932             }
44933         );
44934         this.footer.add( footDisp );
44935         this.footer.add( 
44936             {
44937                 text : '&gt;',
44938                 xtype: 'Button',
44939                 handler : function() {
44940                     // no animation..
44941                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44942                 }
44943             }
44944         );
44945         var fel = Roo.get(footDisp.el);
44946         fel.addClass('x-editor-context');
44947         this.footDispWrap = fel; 
44948         this.footDispWrap.overflow  = 'hidden';
44949         
44950         this.footDisp = fel.createChild();
44951         this.footDispWrap.on('click', this.onContextClick, this)
44952         
44953         
44954     },
44955     onContextClick : function (ev,dom)
44956     {
44957         ev.preventDefault();
44958         var  cn = dom.className;
44959         //Roo.log(cn);
44960         if (!cn.match(/x-ed-loc-/)) {
44961             return;
44962         }
44963         var n = cn.split('-').pop();
44964         var ans = this.footerEls;
44965         var sel = ans[n];
44966         
44967          // pick
44968         var range = this.editorcore.createRange();
44969         
44970         range.selectNodeContents(sel);
44971         //range.selectNode(sel);
44972         
44973         
44974         var selection = this.editorcore.getSelection();
44975         selection.removeAllRanges();
44976         selection.addRange(range);
44977         
44978         
44979         
44980         this.updateToolbar(null, null, sel);
44981         
44982         
44983     }
44984     
44985     
44986     
44987     
44988     
44989 });
44990
44991
44992
44993
44994
44995 /*
44996  * Based on:
44997  * Ext JS Library 1.1.1
44998  * Copyright(c) 2006-2007, Ext JS, LLC.
44999  *
45000  * Originally Released Under LGPL - original licence link has changed is not relivant.
45001  *
45002  * Fork - LGPL
45003  * <script type="text/javascript">
45004  */
45005  
45006 /**
45007  * @class Roo.form.BasicForm
45008  * @extends Roo.util.Observable
45009  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
45010  * @constructor
45011  * @param {String/HTMLElement/Roo.Element} el The form element or its id
45012  * @param {Object} config Configuration options
45013  */
45014 Roo.form.BasicForm = function(el, config){
45015     this.allItems = [];
45016     this.childForms = [];
45017     Roo.apply(this, config);
45018     /*
45019      * The Roo.form.Field items in this form.
45020      * @type MixedCollection
45021      */
45022      
45023      
45024     this.items = new Roo.util.MixedCollection(false, function(o){
45025         return o.id || (o.id = Roo.id());
45026     });
45027     this.addEvents({
45028         /**
45029          * @event beforeaction
45030          * Fires before any action is performed. Return false to cancel the action.
45031          * @param {Form} this
45032          * @param {Action} action The action to be performed
45033          */
45034         beforeaction: true,
45035         /**
45036          * @event actionfailed
45037          * Fires when an action fails.
45038          * @param {Form} this
45039          * @param {Action} action The action that failed
45040          */
45041         actionfailed : true,
45042         /**
45043          * @event actioncomplete
45044          * Fires when an action is completed.
45045          * @param {Form} this
45046          * @param {Action} action The action that completed
45047          */
45048         actioncomplete : true
45049     });
45050     if(el){
45051         this.initEl(el);
45052     }
45053     Roo.form.BasicForm.superclass.constructor.call(this);
45054 };
45055
45056 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
45057     /**
45058      * @cfg {String} method
45059      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
45060      */
45061     /**
45062      * @cfg {DataReader} reader
45063      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
45064      * This is optional as there is built-in support for processing JSON.
45065      */
45066     /**
45067      * @cfg {DataReader} errorReader
45068      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
45069      * This is completely optional as there is built-in support for processing JSON.
45070      */
45071     /**
45072      * @cfg {String} url
45073      * The URL to use for form actions if one isn't supplied in the action options.
45074      */
45075     /**
45076      * @cfg {Boolean} fileUpload
45077      * Set to true if this form is a file upload.
45078      */
45079      
45080     /**
45081      * @cfg {Object} baseParams
45082      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
45083      */
45084      /**
45085      
45086     /**
45087      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
45088      */
45089     timeout: 30,
45090
45091     // private
45092     activeAction : null,
45093
45094     /**
45095      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
45096      * or setValues() data instead of when the form was first created.
45097      */
45098     trackResetOnLoad : false,
45099     
45100     
45101     /**
45102      * childForms - used for multi-tab forms
45103      * @type {Array}
45104      */
45105     childForms : false,
45106     
45107     /**
45108      * allItems - full list of fields.
45109      * @type {Array}
45110      */
45111     allItems : false,
45112     
45113     /**
45114      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
45115      * element by passing it or its id or mask the form itself by passing in true.
45116      * @type Mixed
45117      */
45118     waitMsgTarget : false,
45119
45120     // private
45121     initEl : function(el){
45122         this.el = Roo.get(el);
45123         this.id = this.el.id || Roo.id();
45124         this.el.on('submit', this.onSubmit, this);
45125         this.el.addClass('x-form');
45126     },
45127
45128     // private
45129     onSubmit : function(e){
45130         e.stopEvent();
45131     },
45132
45133     /**
45134      * Returns true if client-side validation on the form is successful.
45135      * @return Boolean
45136      */
45137     isValid : function(){
45138         var valid = true;
45139         this.items.each(function(f){
45140            if(!f.validate()){
45141                valid = false;
45142            }
45143         });
45144         return valid;
45145     },
45146
45147     /**
45148      * Returns true if any fields in this form have changed since their original load.
45149      * @return Boolean
45150      */
45151     isDirty : function(){
45152         var dirty = false;
45153         this.items.each(function(f){
45154            if(f.isDirty()){
45155                dirty = true;
45156                return false;
45157            }
45158         });
45159         return dirty;
45160     },
45161
45162     /**
45163      * Performs a predefined action (submit or load) or custom actions you define on this form.
45164      * @param {String} actionName The name of the action type
45165      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
45166      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
45167      * accept other config options):
45168      * <pre>
45169 Property          Type             Description
45170 ----------------  ---------------  ----------------------------------------------------------------------------------
45171 url               String           The url for the action (defaults to the form's url)
45172 method            String           The form method to use (defaults to the form's method, or POST if not defined)
45173 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
45174 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
45175                                    validate the form on the client (defaults to false)
45176      * </pre>
45177      * @return {BasicForm} this
45178      */
45179     doAction : function(action, options){
45180         if(typeof action == 'string'){
45181             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
45182         }
45183         if(this.fireEvent('beforeaction', this, action) !== false){
45184             this.beforeAction(action);
45185             action.run.defer(100, action);
45186         }
45187         return this;
45188     },
45189
45190     /**
45191      * Shortcut to do a submit action.
45192      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45193      * @return {BasicForm} this
45194      */
45195     submit : function(options){
45196         this.doAction('submit', options);
45197         return this;
45198     },
45199
45200     /**
45201      * Shortcut to do a load action.
45202      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45203      * @return {BasicForm} this
45204      */
45205     load : function(options){
45206         this.doAction('load', options);
45207         return this;
45208     },
45209
45210     /**
45211      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
45212      * @param {Record} record The record to edit
45213      * @return {BasicForm} this
45214      */
45215     updateRecord : function(record){
45216         record.beginEdit();
45217         var fs = record.fields;
45218         fs.each(function(f){
45219             var field = this.findField(f.name);
45220             if(field){
45221                 record.set(f.name, field.getValue());
45222             }
45223         }, this);
45224         record.endEdit();
45225         return this;
45226     },
45227
45228     /**
45229      * Loads an Roo.data.Record into this form.
45230      * @param {Record} record The record to load
45231      * @return {BasicForm} this
45232      */
45233     loadRecord : function(record){
45234         this.setValues(record.data);
45235         return this;
45236     },
45237
45238     // private
45239     beforeAction : function(action){
45240         var o = action.options;
45241         
45242        
45243         if(this.waitMsgTarget === true){
45244             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
45245         }else if(this.waitMsgTarget){
45246             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
45247             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
45248         }else {
45249             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
45250         }
45251          
45252     },
45253
45254     // private
45255     afterAction : function(action, success){
45256         this.activeAction = null;
45257         var o = action.options;
45258         
45259         if(this.waitMsgTarget === true){
45260             this.el.unmask();
45261         }else if(this.waitMsgTarget){
45262             this.waitMsgTarget.unmask();
45263         }else{
45264             Roo.MessageBox.updateProgress(1);
45265             Roo.MessageBox.hide();
45266         }
45267          
45268         if(success){
45269             if(o.reset){
45270                 this.reset();
45271             }
45272             Roo.callback(o.success, o.scope, [this, action]);
45273             this.fireEvent('actioncomplete', this, action);
45274             
45275         }else{
45276             
45277             // failure condition..
45278             // we have a scenario where updates need confirming.
45279             // eg. if a locking scenario exists..
45280             // we look for { errors : { needs_confirm : true }} in the response.
45281             if (
45282                 (typeof(action.result) != 'undefined')  &&
45283                 (typeof(action.result.errors) != 'undefined')  &&
45284                 (typeof(action.result.errors.needs_confirm) != 'undefined')
45285            ){
45286                 var _t = this;
45287                 Roo.MessageBox.confirm(
45288                     "Change requires confirmation",
45289                     action.result.errorMsg,
45290                     function(r) {
45291                         if (r != 'yes') {
45292                             return;
45293                         }
45294                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
45295                     }
45296                     
45297                 );
45298                 
45299                 
45300                 
45301                 return;
45302             }
45303             
45304             Roo.callback(o.failure, o.scope, [this, action]);
45305             // show an error message if no failed handler is set..
45306             if (!this.hasListener('actionfailed')) {
45307                 Roo.MessageBox.alert("Error",
45308                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
45309                         action.result.errorMsg :
45310                         "Saving Failed, please check your entries or try again"
45311                 );
45312             }
45313             
45314             this.fireEvent('actionfailed', this, action);
45315         }
45316         
45317     },
45318
45319     /**
45320      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
45321      * @param {String} id The value to search for
45322      * @return Field
45323      */
45324     findField : function(id){
45325         var field = this.items.get(id);
45326         if(!field){
45327             this.items.each(function(f){
45328                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
45329                     field = f;
45330                     return false;
45331                 }
45332             });
45333         }
45334         return field || null;
45335     },
45336
45337     /**
45338      * Add a secondary form to this one, 
45339      * Used to provide tabbed forms. One form is primary, with hidden values 
45340      * which mirror the elements from the other forms.
45341      * 
45342      * @param {Roo.form.Form} form to add.
45343      * 
45344      */
45345     addForm : function(form)
45346     {
45347        
45348         if (this.childForms.indexOf(form) > -1) {
45349             // already added..
45350             return;
45351         }
45352         this.childForms.push(form);
45353         var n = '';
45354         Roo.each(form.allItems, function (fe) {
45355             
45356             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
45357             if (this.findField(n)) { // already added..
45358                 return;
45359             }
45360             var add = new Roo.form.Hidden({
45361                 name : n
45362             });
45363             add.render(this.el);
45364             
45365             this.add( add );
45366         }, this);
45367         
45368     },
45369     /**
45370      * Mark fields in this form invalid in bulk.
45371      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
45372      * @return {BasicForm} this
45373      */
45374     markInvalid : function(errors){
45375         if(errors instanceof Array){
45376             for(var i = 0, len = errors.length; i < len; i++){
45377                 var fieldError = errors[i];
45378                 var f = this.findField(fieldError.id);
45379                 if(f){
45380                     f.markInvalid(fieldError.msg);
45381                 }
45382             }
45383         }else{
45384             var field, id;
45385             for(id in errors){
45386                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
45387                     field.markInvalid(errors[id]);
45388                 }
45389             }
45390         }
45391         Roo.each(this.childForms || [], function (f) {
45392             f.markInvalid(errors);
45393         });
45394         
45395         return this;
45396     },
45397
45398     /**
45399      * Set values for fields in this form in bulk.
45400      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
45401      * @return {BasicForm} this
45402      */
45403     setValues : function(values){
45404         if(values instanceof Array){ // array of objects
45405             for(var i = 0, len = values.length; i < len; i++){
45406                 var v = values[i];
45407                 var f = this.findField(v.id);
45408                 if(f){
45409                     f.setValue(v.value);
45410                     if(this.trackResetOnLoad){
45411                         f.originalValue = f.getValue();
45412                     }
45413                 }
45414             }
45415         }else{ // object hash
45416             var field, id;
45417             for(id in values){
45418                 if(typeof values[id] != 'function' && (field = this.findField(id))){
45419                     
45420                     if (field.setFromData && 
45421                         field.valueField && 
45422                         field.displayField &&
45423                         // combos' with local stores can 
45424                         // be queried via setValue()
45425                         // to set their value..
45426                         (field.store && !field.store.isLocal)
45427                         ) {
45428                         // it's a combo
45429                         var sd = { };
45430                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
45431                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
45432                         field.setFromData(sd);
45433                         
45434                     } else {
45435                         field.setValue(values[id]);
45436                     }
45437                     
45438                     
45439                     if(this.trackResetOnLoad){
45440                         field.originalValue = field.getValue();
45441                     }
45442                 }
45443             }
45444         }
45445          
45446         Roo.each(this.childForms || [], function (f) {
45447             f.setValues(values);
45448         });
45449                 
45450         return this;
45451     },
45452
45453     /**
45454      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
45455      * they are returned as an array.
45456      * @param {Boolean} asString
45457      * @return {Object}
45458      */
45459     getValues : function(asString){
45460         if (this.childForms) {
45461             // copy values from the child forms
45462             Roo.each(this.childForms, function (f) {
45463                 this.setValues(f.getValues());
45464             }, this);
45465         }
45466         
45467         
45468         
45469         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
45470         if(asString === true){
45471             return fs;
45472         }
45473         return Roo.urlDecode(fs);
45474     },
45475     
45476     /**
45477      * Returns the fields in this form as an object with key/value pairs. 
45478      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
45479      * @return {Object}
45480      */
45481     getFieldValues : function(with_hidden)
45482     {
45483         if (this.childForms) {
45484             // copy values from the child forms
45485             // should this call getFieldValues - probably not as we do not currently copy
45486             // hidden fields when we generate..
45487             Roo.each(this.childForms, function (f) {
45488                 this.setValues(f.getValues());
45489             }, this);
45490         }
45491         
45492         var ret = {};
45493         this.items.each(function(f){
45494             if (!f.getName()) {
45495                 return;
45496             }
45497             var v = f.getValue();
45498             if (f.inputType =='radio') {
45499                 if (typeof(ret[f.getName()]) == 'undefined') {
45500                     ret[f.getName()] = ''; // empty..
45501                 }
45502                 
45503                 if (!f.el.dom.checked) {
45504                     return;
45505                     
45506                 }
45507                 v = f.el.dom.value;
45508                 
45509             }
45510             
45511             // not sure if this supported any more..
45512             if ((typeof(v) == 'object') && f.getRawValue) {
45513                 v = f.getRawValue() ; // dates..
45514             }
45515             // combo boxes where name != hiddenName...
45516             if (f.name != f.getName()) {
45517                 ret[f.name] = f.getRawValue();
45518             }
45519             ret[f.getName()] = v;
45520         });
45521         
45522         return ret;
45523     },
45524
45525     /**
45526      * Clears all invalid messages in this form.
45527      * @return {BasicForm} this
45528      */
45529     clearInvalid : function(){
45530         this.items.each(function(f){
45531            f.clearInvalid();
45532         });
45533         
45534         Roo.each(this.childForms || [], function (f) {
45535             f.clearInvalid();
45536         });
45537         
45538         
45539         return this;
45540     },
45541
45542     /**
45543      * Resets this form.
45544      * @return {BasicForm} this
45545      */
45546     reset : function(){
45547         this.items.each(function(f){
45548             f.reset();
45549         });
45550         
45551         Roo.each(this.childForms || [], function (f) {
45552             f.reset();
45553         });
45554        
45555         
45556         return this;
45557     },
45558
45559     /**
45560      * Add Roo.form components to this form.
45561      * @param {Field} field1
45562      * @param {Field} field2 (optional)
45563      * @param {Field} etc (optional)
45564      * @return {BasicForm} this
45565      */
45566     add : function(){
45567         this.items.addAll(Array.prototype.slice.call(arguments, 0));
45568         return this;
45569     },
45570
45571
45572     /**
45573      * Removes a field from the items collection (does NOT remove its markup).
45574      * @param {Field} field
45575      * @return {BasicForm} this
45576      */
45577     remove : function(field){
45578         this.items.remove(field);
45579         return this;
45580     },
45581
45582     /**
45583      * Looks at the fields in this form, checks them for an id attribute,
45584      * and calls applyTo on the existing dom element with that id.
45585      * @return {BasicForm} this
45586      */
45587     render : function(){
45588         this.items.each(function(f){
45589             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
45590                 f.applyTo(f.id);
45591             }
45592         });
45593         return this;
45594     },
45595
45596     /**
45597      * Calls {@link Ext#apply} for all fields in this form with the passed object.
45598      * @param {Object} values
45599      * @return {BasicForm} this
45600      */
45601     applyToFields : function(o){
45602         this.items.each(function(f){
45603            Roo.apply(f, o);
45604         });
45605         return this;
45606     },
45607
45608     /**
45609      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
45610      * @param {Object} values
45611      * @return {BasicForm} this
45612      */
45613     applyIfToFields : function(o){
45614         this.items.each(function(f){
45615            Roo.applyIf(f, o);
45616         });
45617         return this;
45618     }
45619 });
45620
45621 // back compat
45622 Roo.BasicForm = Roo.form.BasicForm;/*
45623  * Based on:
45624  * Ext JS Library 1.1.1
45625  * Copyright(c) 2006-2007, Ext JS, LLC.
45626  *
45627  * Originally Released Under LGPL - original licence link has changed is not relivant.
45628  *
45629  * Fork - LGPL
45630  * <script type="text/javascript">
45631  */
45632
45633 /**
45634  * @class Roo.form.Form
45635  * @extends Roo.form.BasicForm
45636  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
45637  * @constructor
45638  * @param {Object} config Configuration options
45639  */
45640 Roo.form.Form = function(config){
45641     var xitems =  [];
45642     if (config.items) {
45643         xitems = config.items;
45644         delete config.items;
45645     }
45646    
45647     
45648     Roo.form.Form.superclass.constructor.call(this, null, config);
45649     this.url = this.url || this.action;
45650     if(!this.root){
45651         this.root = new Roo.form.Layout(Roo.applyIf({
45652             id: Roo.id()
45653         }, config));
45654     }
45655     this.active = this.root;
45656     /**
45657      * Array of all the buttons that have been added to this form via {@link addButton}
45658      * @type Array
45659      */
45660     this.buttons = [];
45661     this.allItems = [];
45662     this.addEvents({
45663         /**
45664          * @event clientvalidation
45665          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45666          * @param {Form} this
45667          * @param {Boolean} valid true if the form has passed client-side validation
45668          */
45669         clientvalidation: true,
45670         /**
45671          * @event rendered
45672          * Fires when the form is rendered
45673          * @param {Roo.form.Form} form
45674          */
45675         rendered : true
45676     });
45677     
45678     if (this.progressUrl) {
45679             // push a hidden field onto the list of fields..
45680             this.addxtype( {
45681                     xns: Roo.form, 
45682                     xtype : 'Hidden', 
45683                     name : 'UPLOAD_IDENTIFIER' 
45684             });
45685         }
45686         
45687     
45688     Roo.each(xitems, this.addxtype, this);
45689     
45690     
45691     
45692 };
45693
45694 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45695     /**
45696      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45697      */
45698     /**
45699      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45700      */
45701     /**
45702      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45703      */
45704     buttonAlign:'center',
45705
45706     /**
45707      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45708      */
45709     minButtonWidth:75,
45710
45711     /**
45712      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45713      * This property cascades to child containers if not set.
45714      */
45715     labelAlign:'left',
45716
45717     /**
45718      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45719      * fires a looping event with that state. This is required to bind buttons to the valid
45720      * state using the config value formBind:true on the button.
45721      */
45722     monitorValid : false,
45723
45724     /**
45725      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45726      */
45727     monitorPoll : 200,
45728     
45729     /**
45730      * @cfg {String} progressUrl - Url to return progress data 
45731      */
45732     
45733     progressUrl : false,
45734   
45735     /**
45736      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45737      * fields are added and the column is closed. If no fields are passed the column remains open
45738      * until end() is called.
45739      * @param {Object} config The config to pass to the column
45740      * @param {Field} field1 (optional)
45741      * @param {Field} field2 (optional)
45742      * @param {Field} etc (optional)
45743      * @return Column The column container object
45744      */
45745     column : function(c){
45746         var col = new Roo.form.Column(c);
45747         this.start(col);
45748         if(arguments.length > 1){ // duplicate code required because of Opera
45749             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45750             this.end();
45751         }
45752         return col;
45753     },
45754
45755     /**
45756      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45757      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45758      * until end() is called.
45759      * @param {Object} config The config to pass to the fieldset
45760      * @param {Field} field1 (optional)
45761      * @param {Field} field2 (optional)
45762      * @param {Field} etc (optional)
45763      * @return FieldSet The fieldset container object
45764      */
45765     fieldset : function(c){
45766         var fs = new Roo.form.FieldSet(c);
45767         this.start(fs);
45768         if(arguments.length > 1){ // duplicate code required because of Opera
45769             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45770             this.end();
45771         }
45772         return fs;
45773     },
45774
45775     /**
45776      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45777      * fields are added and the container is closed. If no fields are passed the container remains open
45778      * until end() is called.
45779      * @param {Object} config The config to pass to the Layout
45780      * @param {Field} field1 (optional)
45781      * @param {Field} field2 (optional)
45782      * @param {Field} etc (optional)
45783      * @return Layout The container object
45784      */
45785     container : function(c){
45786         var l = new Roo.form.Layout(c);
45787         this.start(l);
45788         if(arguments.length > 1){ // duplicate code required because of Opera
45789             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45790             this.end();
45791         }
45792         return l;
45793     },
45794
45795     /**
45796      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45797      * @param {Object} container A Roo.form.Layout or subclass of Layout
45798      * @return {Form} this
45799      */
45800     start : function(c){
45801         // cascade label info
45802         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45803         this.active.stack.push(c);
45804         c.ownerCt = this.active;
45805         this.active = c;
45806         return this;
45807     },
45808
45809     /**
45810      * Closes the current open container
45811      * @return {Form} this
45812      */
45813     end : function(){
45814         if(this.active == this.root){
45815             return this;
45816         }
45817         this.active = this.active.ownerCt;
45818         return this;
45819     },
45820
45821     /**
45822      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45823      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45824      * as the label of the field.
45825      * @param {Field} field1
45826      * @param {Field} field2 (optional)
45827      * @param {Field} etc. (optional)
45828      * @return {Form} this
45829      */
45830     add : function(){
45831         this.active.stack.push.apply(this.active.stack, arguments);
45832         this.allItems.push.apply(this.allItems,arguments);
45833         var r = [];
45834         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45835             if(a[i].isFormField){
45836                 r.push(a[i]);
45837             }
45838         }
45839         if(r.length > 0){
45840             Roo.form.Form.superclass.add.apply(this, r);
45841         }
45842         return this;
45843     },
45844     
45845
45846     
45847     
45848     
45849      /**
45850      * Find any element that has been added to a form, using it's ID or name
45851      * This can include framesets, columns etc. along with regular fields..
45852      * @param {String} id - id or name to find.
45853      
45854      * @return {Element} e - or false if nothing found.
45855      */
45856     findbyId : function(id)
45857     {
45858         var ret = false;
45859         if (!id) {
45860             return ret;
45861         }
45862         Roo.each(this.allItems, function(f){
45863             if (f.id == id || f.name == id ){
45864                 ret = f;
45865                 return false;
45866             }
45867         });
45868         return ret;
45869     },
45870
45871     
45872     
45873     /**
45874      * Render this form into the passed container. This should only be called once!
45875      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45876      * @return {Form} this
45877      */
45878     render : function(ct)
45879     {
45880         
45881         
45882         
45883         ct = Roo.get(ct);
45884         var o = this.autoCreate || {
45885             tag: 'form',
45886             method : this.method || 'POST',
45887             id : this.id || Roo.id()
45888         };
45889         this.initEl(ct.createChild(o));
45890
45891         this.root.render(this.el);
45892         
45893        
45894              
45895         this.items.each(function(f){
45896             f.render('x-form-el-'+f.id);
45897         });
45898
45899         if(this.buttons.length > 0){
45900             // tables are required to maintain order and for correct IE layout
45901             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45902                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45903                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45904             }}, null, true);
45905             var tr = tb.getElementsByTagName('tr')[0];
45906             for(var i = 0, len = this.buttons.length; i < len; i++) {
45907                 var b = this.buttons[i];
45908                 var td = document.createElement('td');
45909                 td.className = 'x-form-btn-td';
45910                 b.render(tr.appendChild(td));
45911             }
45912         }
45913         if(this.monitorValid){ // initialize after render
45914             this.startMonitoring();
45915         }
45916         this.fireEvent('rendered', this);
45917         return this;
45918     },
45919
45920     /**
45921      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45922      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45923      * object or a valid Roo.DomHelper element config
45924      * @param {Function} handler The function called when the button is clicked
45925      * @param {Object} scope (optional) The scope of the handler function
45926      * @return {Roo.Button}
45927      */
45928     addButton : function(config, handler, scope){
45929         var bc = {
45930             handler: handler,
45931             scope: scope,
45932             minWidth: this.minButtonWidth,
45933             hideParent:true
45934         };
45935         if(typeof config == "string"){
45936             bc.text = config;
45937         }else{
45938             Roo.apply(bc, config);
45939         }
45940         var btn = new Roo.Button(null, bc);
45941         this.buttons.push(btn);
45942         return btn;
45943     },
45944
45945      /**
45946      * Adds a series of form elements (using the xtype property as the factory method.
45947      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45948      * @param {Object} config 
45949      */
45950     
45951     addxtype : function()
45952     {
45953         var ar = Array.prototype.slice.call(arguments, 0);
45954         var ret = false;
45955         for(var i = 0; i < ar.length; i++) {
45956             if (!ar[i]) {
45957                 continue; // skip -- if this happends something invalid got sent, we 
45958                 // should ignore it, as basically that interface element will not show up
45959                 // and that should be pretty obvious!!
45960             }
45961             
45962             if (Roo.form[ar[i].xtype]) {
45963                 ar[i].form = this;
45964                 var fe = Roo.factory(ar[i], Roo.form);
45965                 if (!ret) {
45966                     ret = fe;
45967                 }
45968                 fe.form = this;
45969                 if (fe.store) {
45970                     fe.store.form = this;
45971                 }
45972                 if (fe.isLayout) {  
45973                          
45974                     this.start(fe);
45975                     this.allItems.push(fe);
45976                     if (fe.items && fe.addxtype) {
45977                         fe.addxtype.apply(fe, fe.items);
45978                         delete fe.items;
45979                     }
45980                      this.end();
45981                     continue;
45982                 }
45983                 
45984                 
45985                  
45986                 this.add(fe);
45987               //  console.log('adding ' + ar[i].xtype);
45988             }
45989             if (ar[i].xtype == 'Button') {  
45990                 //console.log('adding button');
45991                 //console.log(ar[i]);
45992                 this.addButton(ar[i]);
45993                 this.allItems.push(fe);
45994                 continue;
45995             }
45996             
45997             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45998                 alert('end is not supported on xtype any more, use items');
45999             //    this.end();
46000             //    //console.log('adding end');
46001             }
46002             
46003         }
46004         return ret;
46005     },
46006     
46007     /**
46008      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
46009      * option "monitorValid"
46010      */
46011     startMonitoring : function(){
46012         if(!this.bound){
46013             this.bound = true;
46014             Roo.TaskMgr.start({
46015                 run : this.bindHandler,
46016                 interval : this.monitorPoll || 200,
46017                 scope: this
46018             });
46019         }
46020     },
46021
46022     /**
46023      * Stops monitoring of the valid state of this form
46024      */
46025     stopMonitoring : function(){
46026         this.bound = false;
46027     },
46028
46029     // private
46030     bindHandler : function(){
46031         if(!this.bound){
46032             return false; // stops binding
46033         }
46034         var valid = true;
46035         this.items.each(function(f){
46036             if(!f.isValid(true)){
46037                 valid = false;
46038                 return false;
46039             }
46040         });
46041         for(var i = 0, len = this.buttons.length; i < len; i++){
46042             var btn = this.buttons[i];
46043             if(btn.formBind === true && btn.disabled === valid){
46044                 btn.setDisabled(!valid);
46045             }
46046         }
46047         this.fireEvent('clientvalidation', this, valid);
46048     }
46049     
46050     
46051     
46052     
46053     
46054     
46055     
46056     
46057 });
46058
46059
46060 // back compat
46061 Roo.Form = Roo.form.Form;
46062 /*
46063  * Based on:
46064  * Ext JS Library 1.1.1
46065  * Copyright(c) 2006-2007, Ext JS, LLC.
46066  *
46067  * Originally Released Under LGPL - original licence link has changed is not relivant.
46068  *
46069  * Fork - LGPL
46070  * <script type="text/javascript">
46071  */
46072
46073 // as we use this in bootstrap.
46074 Roo.namespace('Roo.form');
46075  /**
46076  * @class Roo.form.Action
46077  * Internal Class used to handle form actions
46078  * @constructor
46079  * @param {Roo.form.BasicForm} el The form element or its id
46080  * @param {Object} config Configuration options
46081  */
46082
46083  
46084  
46085 // define the action interface
46086 Roo.form.Action = function(form, options){
46087     this.form = form;
46088     this.options = options || {};
46089 };
46090 /**
46091  * Client Validation Failed
46092  * @const 
46093  */
46094 Roo.form.Action.CLIENT_INVALID = 'client';
46095 /**
46096  * Server Validation Failed
46097  * @const 
46098  */
46099 Roo.form.Action.SERVER_INVALID = 'server';
46100  /**
46101  * Connect to Server Failed
46102  * @const 
46103  */
46104 Roo.form.Action.CONNECT_FAILURE = 'connect';
46105 /**
46106  * Reading Data from Server Failed
46107  * @const 
46108  */
46109 Roo.form.Action.LOAD_FAILURE = 'load';
46110
46111 Roo.form.Action.prototype = {
46112     type : 'default',
46113     failureType : undefined,
46114     response : undefined,
46115     result : undefined,
46116
46117     // interface method
46118     run : function(options){
46119
46120     },
46121
46122     // interface method
46123     success : function(response){
46124
46125     },
46126
46127     // interface method
46128     handleResponse : function(response){
46129
46130     },
46131
46132     // default connection failure
46133     failure : function(response){
46134         
46135         this.response = response;
46136         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46137         this.form.afterAction(this, false);
46138     },
46139
46140     processResponse : function(response){
46141         this.response = response;
46142         if(!response.responseText){
46143             return true;
46144         }
46145         this.result = this.handleResponse(response);
46146         return this.result;
46147     },
46148
46149     // utility functions used internally
46150     getUrl : function(appendParams){
46151         var url = this.options.url || this.form.url || this.form.el.dom.action;
46152         if(appendParams){
46153             var p = this.getParams();
46154             if(p){
46155                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
46156             }
46157         }
46158         return url;
46159     },
46160
46161     getMethod : function(){
46162         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
46163     },
46164
46165     getParams : function(){
46166         var bp = this.form.baseParams;
46167         var p = this.options.params;
46168         if(p){
46169             if(typeof p == "object"){
46170                 p = Roo.urlEncode(Roo.applyIf(p, bp));
46171             }else if(typeof p == 'string' && bp){
46172                 p += '&' + Roo.urlEncode(bp);
46173             }
46174         }else if(bp){
46175             p = Roo.urlEncode(bp);
46176         }
46177         return p;
46178     },
46179
46180     createCallback : function(){
46181         return {
46182             success: this.success,
46183             failure: this.failure,
46184             scope: this,
46185             timeout: (this.form.timeout*1000),
46186             upload: this.form.fileUpload ? this.success : undefined
46187         };
46188     }
46189 };
46190
46191 Roo.form.Action.Submit = function(form, options){
46192     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
46193 };
46194
46195 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
46196     type : 'submit',
46197
46198     haveProgress : false,
46199     uploadComplete : false,
46200     
46201     // uploadProgress indicator.
46202     uploadProgress : function()
46203     {
46204         if (!this.form.progressUrl) {
46205             return;
46206         }
46207         
46208         if (!this.haveProgress) {
46209             Roo.MessageBox.progress("Uploading", "Uploading");
46210         }
46211         if (this.uploadComplete) {
46212            Roo.MessageBox.hide();
46213            return;
46214         }
46215         
46216         this.haveProgress = true;
46217    
46218         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
46219         
46220         var c = new Roo.data.Connection();
46221         c.request({
46222             url : this.form.progressUrl,
46223             params: {
46224                 id : uid
46225             },
46226             method: 'GET',
46227             success : function(req){
46228                //console.log(data);
46229                 var rdata = false;
46230                 var edata;
46231                 try  {
46232                    rdata = Roo.decode(req.responseText)
46233                 } catch (e) {
46234                     Roo.log("Invalid data from server..");
46235                     Roo.log(edata);
46236                     return;
46237                 }
46238                 if (!rdata || !rdata.success) {
46239                     Roo.log(rdata);
46240                     Roo.MessageBox.alert(Roo.encode(rdata));
46241                     return;
46242                 }
46243                 var data = rdata.data;
46244                 
46245                 if (this.uploadComplete) {
46246                    Roo.MessageBox.hide();
46247                    return;
46248                 }
46249                    
46250                 if (data){
46251                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
46252                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
46253                     );
46254                 }
46255                 this.uploadProgress.defer(2000,this);
46256             },
46257        
46258             failure: function(data) {
46259                 Roo.log('progress url failed ');
46260                 Roo.log(data);
46261             },
46262             scope : this
46263         });
46264            
46265     },
46266     
46267     
46268     run : function()
46269     {
46270         // run get Values on the form, so it syncs any secondary forms.
46271         this.form.getValues();
46272         
46273         var o = this.options;
46274         var method = this.getMethod();
46275         var isPost = method == 'POST';
46276         if(o.clientValidation === false || this.form.isValid()){
46277             
46278             if (this.form.progressUrl) {
46279                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
46280                     (new Date() * 1) + '' + Math.random());
46281                     
46282             } 
46283             
46284             
46285             Roo.Ajax.request(Roo.apply(this.createCallback(), {
46286                 form:this.form.el.dom,
46287                 url:this.getUrl(!isPost),
46288                 method: method,
46289                 params:isPost ? this.getParams() : null,
46290                 isUpload: this.form.fileUpload
46291             }));
46292             
46293             this.uploadProgress();
46294
46295         }else if (o.clientValidation !== false){ // client validation failed
46296             this.failureType = Roo.form.Action.CLIENT_INVALID;
46297             this.form.afterAction(this, false);
46298         }
46299     },
46300
46301     success : function(response)
46302     {
46303         this.uploadComplete= true;
46304         if (this.haveProgress) {
46305             Roo.MessageBox.hide();
46306         }
46307         
46308         
46309         var result = this.processResponse(response);
46310         if(result === true || result.success){
46311             this.form.afterAction(this, true);
46312             return;
46313         }
46314         if(result.errors){
46315             this.form.markInvalid(result.errors);
46316             this.failureType = Roo.form.Action.SERVER_INVALID;
46317         }
46318         this.form.afterAction(this, false);
46319     },
46320     failure : function(response)
46321     {
46322         this.uploadComplete= true;
46323         if (this.haveProgress) {
46324             Roo.MessageBox.hide();
46325         }
46326         
46327         this.response = response;
46328         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46329         this.form.afterAction(this, false);
46330     },
46331     
46332     handleResponse : function(response){
46333         if(this.form.errorReader){
46334             var rs = this.form.errorReader.read(response);
46335             var errors = [];
46336             if(rs.records){
46337                 for(var i = 0, len = rs.records.length; i < len; i++) {
46338                     var r = rs.records[i];
46339                     errors[i] = r.data;
46340                 }
46341             }
46342             if(errors.length < 1){
46343                 errors = null;
46344             }
46345             return {
46346                 success : rs.success,
46347                 errors : errors
46348             };
46349         }
46350         var ret = false;
46351         try {
46352             ret = Roo.decode(response.responseText);
46353         } catch (e) {
46354             ret = {
46355                 success: false,
46356                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
46357                 errors : []
46358             };
46359         }
46360         return ret;
46361         
46362     }
46363 });
46364
46365
46366 Roo.form.Action.Load = function(form, options){
46367     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
46368     this.reader = this.form.reader;
46369 };
46370
46371 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
46372     type : 'load',
46373
46374     run : function(){
46375         
46376         Roo.Ajax.request(Roo.apply(
46377                 this.createCallback(), {
46378                     method:this.getMethod(),
46379                     url:this.getUrl(false),
46380                     params:this.getParams()
46381         }));
46382     },
46383
46384     success : function(response){
46385         
46386         var result = this.processResponse(response);
46387         if(result === true || !result.success || !result.data){
46388             this.failureType = Roo.form.Action.LOAD_FAILURE;
46389             this.form.afterAction(this, false);
46390             return;
46391         }
46392         this.form.clearInvalid();
46393         this.form.setValues(result.data);
46394         this.form.afterAction(this, true);
46395     },
46396
46397     handleResponse : function(response){
46398         if(this.form.reader){
46399             var rs = this.form.reader.read(response);
46400             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
46401             return {
46402                 success : rs.success,
46403                 data : data
46404             };
46405         }
46406         return Roo.decode(response.responseText);
46407     }
46408 });
46409
46410 Roo.form.Action.ACTION_TYPES = {
46411     'load' : Roo.form.Action.Load,
46412     'submit' : Roo.form.Action.Submit
46413 };/*
46414  * Based on:
46415  * Ext JS Library 1.1.1
46416  * Copyright(c) 2006-2007, Ext JS, LLC.
46417  *
46418  * Originally Released Under LGPL - original licence link has changed is not relivant.
46419  *
46420  * Fork - LGPL
46421  * <script type="text/javascript">
46422  */
46423  
46424 /**
46425  * @class Roo.form.Layout
46426  * @extends Roo.Component
46427  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
46428  * @constructor
46429  * @param {Object} config Configuration options
46430  */
46431 Roo.form.Layout = function(config){
46432     var xitems = [];
46433     if (config.items) {
46434         xitems = config.items;
46435         delete config.items;
46436     }
46437     Roo.form.Layout.superclass.constructor.call(this, config);
46438     this.stack = [];
46439     Roo.each(xitems, this.addxtype, this);
46440      
46441 };
46442
46443 Roo.extend(Roo.form.Layout, Roo.Component, {
46444     /**
46445      * @cfg {String/Object} autoCreate
46446      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
46447      */
46448     /**
46449      * @cfg {String/Object/Function} style
46450      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
46451      * a function which returns such a specification.
46452      */
46453     /**
46454      * @cfg {String} labelAlign
46455      * Valid values are "left," "top" and "right" (defaults to "left")
46456      */
46457     /**
46458      * @cfg {Number} labelWidth
46459      * Fixed width in pixels of all field labels (defaults to undefined)
46460      */
46461     /**
46462      * @cfg {Boolean} clear
46463      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
46464      */
46465     clear : true,
46466     /**
46467      * @cfg {String} labelSeparator
46468      * The separator to use after field labels (defaults to ':')
46469      */
46470     labelSeparator : ':',
46471     /**
46472      * @cfg {Boolean} hideLabels
46473      * True to suppress the display of field labels in this layout (defaults to false)
46474      */
46475     hideLabels : false,
46476
46477     // private
46478     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
46479     
46480     isLayout : true,
46481     
46482     // private
46483     onRender : function(ct, position){
46484         if(this.el){ // from markup
46485             this.el = Roo.get(this.el);
46486         }else {  // generate
46487             var cfg = this.getAutoCreate();
46488             this.el = ct.createChild(cfg, position);
46489         }
46490         if(this.style){
46491             this.el.applyStyles(this.style);
46492         }
46493         if(this.labelAlign){
46494             this.el.addClass('x-form-label-'+this.labelAlign);
46495         }
46496         if(this.hideLabels){
46497             this.labelStyle = "display:none";
46498             this.elementStyle = "padding-left:0;";
46499         }else{
46500             if(typeof this.labelWidth == 'number'){
46501                 this.labelStyle = "width:"+this.labelWidth+"px;";
46502                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
46503             }
46504             if(this.labelAlign == 'top'){
46505                 this.labelStyle = "width:auto;";
46506                 this.elementStyle = "padding-left:0;";
46507             }
46508         }
46509         var stack = this.stack;
46510         var slen = stack.length;
46511         if(slen > 0){
46512             if(!this.fieldTpl){
46513                 var t = new Roo.Template(
46514                     '<div class="x-form-item {5}">',
46515                         '<label for="{0}" style="{2}">{1}{4}</label>',
46516                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46517                         '</div>',
46518                     '</div><div class="x-form-clear-left"></div>'
46519                 );
46520                 t.disableFormats = true;
46521                 t.compile();
46522                 Roo.form.Layout.prototype.fieldTpl = t;
46523             }
46524             for(var i = 0; i < slen; i++) {
46525                 if(stack[i].isFormField){
46526                     this.renderField(stack[i]);
46527                 }else{
46528                     this.renderComponent(stack[i]);
46529                 }
46530             }
46531         }
46532         if(this.clear){
46533             this.el.createChild({cls:'x-form-clear'});
46534         }
46535     },
46536
46537     // private
46538     renderField : function(f){
46539         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
46540                f.id, //0
46541                f.fieldLabel, //1
46542                f.labelStyle||this.labelStyle||'', //2
46543                this.elementStyle||'', //3
46544                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
46545                f.itemCls||this.itemCls||''  //5
46546        ], true).getPrevSibling());
46547     },
46548
46549     // private
46550     renderComponent : function(c){
46551         c.render(c.isLayout ? this.el : this.el.createChild());    
46552     },
46553     /**
46554      * Adds a object form elements (using the xtype property as the factory method.)
46555      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
46556      * @param {Object} config 
46557      */
46558     addxtype : function(o)
46559     {
46560         // create the lement.
46561         o.form = this.form;
46562         var fe = Roo.factory(o, Roo.form);
46563         this.form.allItems.push(fe);
46564         this.stack.push(fe);
46565         
46566         if (fe.isFormField) {
46567             this.form.items.add(fe);
46568         }
46569          
46570         return fe;
46571     }
46572 });
46573
46574 /**
46575  * @class Roo.form.Column
46576  * @extends Roo.form.Layout
46577  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
46578  * @constructor
46579  * @param {Object} config Configuration options
46580  */
46581 Roo.form.Column = function(config){
46582     Roo.form.Column.superclass.constructor.call(this, config);
46583 };
46584
46585 Roo.extend(Roo.form.Column, Roo.form.Layout, {
46586     /**
46587      * @cfg {Number/String} width
46588      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46589      */
46590     /**
46591      * @cfg {String/Object} autoCreate
46592      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
46593      */
46594
46595     // private
46596     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
46597
46598     // private
46599     onRender : function(ct, position){
46600         Roo.form.Column.superclass.onRender.call(this, ct, position);
46601         if(this.width){
46602             this.el.setWidth(this.width);
46603         }
46604     }
46605 });
46606
46607
46608 /**
46609  * @class Roo.form.Row
46610  * @extends Roo.form.Layout
46611  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
46612  * @constructor
46613  * @param {Object} config Configuration options
46614  */
46615
46616  
46617 Roo.form.Row = function(config){
46618     Roo.form.Row.superclass.constructor.call(this, config);
46619 };
46620  
46621 Roo.extend(Roo.form.Row, Roo.form.Layout, {
46622       /**
46623      * @cfg {Number/String} width
46624      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46625      */
46626     /**
46627      * @cfg {Number/String} height
46628      * The fixed height of the column in pixels or CSS value (defaults to "auto")
46629      */
46630     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
46631     
46632     padWidth : 20,
46633     // private
46634     onRender : function(ct, position){
46635         //console.log('row render');
46636         if(!this.rowTpl){
46637             var t = new Roo.Template(
46638                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
46639                     '<label for="{0}" style="{2}">{1}{4}</label>',
46640                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46641                     '</div>',
46642                 '</div>'
46643             );
46644             t.disableFormats = true;
46645             t.compile();
46646             Roo.form.Layout.prototype.rowTpl = t;
46647         }
46648         this.fieldTpl = this.rowTpl;
46649         
46650         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46651         var labelWidth = 100;
46652         
46653         if ((this.labelAlign != 'top')) {
46654             if (typeof this.labelWidth == 'number') {
46655                 labelWidth = this.labelWidth
46656             }
46657             this.padWidth =  20 + labelWidth;
46658             
46659         }
46660         
46661         Roo.form.Column.superclass.onRender.call(this, ct, position);
46662         if(this.width){
46663             this.el.setWidth(this.width);
46664         }
46665         if(this.height){
46666             this.el.setHeight(this.height);
46667         }
46668     },
46669     
46670     // private
46671     renderField : function(f){
46672         f.fieldEl = this.fieldTpl.append(this.el, [
46673                f.id, f.fieldLabel,
46674                f.labelStyle||this.labelStyle||'',
46675                this.elementStyle||'',
46676                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46677                f.itemCls||this.itemCls||'',
46678                f.width ? f.width + this.padWidth : 160 + this.padWidth
46679        ],true);
46680     }
46681 });
46682  
46683
46684 /**
46685  * @class Roo.form.FieldSet
46686  * @extends Roo.form.Layout
46687  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46688  * @constructor
46689  * @param {Object} config Configuration options
46690  */
46691 Roo.form.FieldSet = function(config){
46692     Roo.form.FieldSet.superclass.constructor.call(this, config);
46693 };
46694
46695 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46696     /**
46697      * @cfg {String} legend
46698      * The text to display as the legend for the FieldSet (defaults to '')
46699      */
46700     /**
46701      * @cfg {String/Object} autoCreate
46702      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46703      */
46704
46705     // private
46706     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46707
46708     // private
46709     onRender : function(ct, position){
46710         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46711         if(this.legend){
46712             this.setLegend(this.legend);
46713         }
46714     },
46715
46716     // private
46717     setLegend : function(text){
46718         if(this.rendered){
46719             this.el.child('legend').update(text);
46720         }
46721     }
46722 });/*
46723  * Based on:
46724  * Ext JS Library 1.1.1
46725  * Copyright(c) 2006-2007, Ext JS, LLC.
46726  *
46727  * Originally Released Under LGPL - original licence link has changed is not relivant.
46728  *
46729  * Fork - LGPL
46730  * <script type="text/javascript">
46731  */
46732 /**
46733  * @class Roo.form.VTypes
46734  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46735  * @singleton
46736  */
46737 Roo.form.VTypes = function(){
46738     // closure these in so they are only created once.
46739     var alpha = /^[a-zA-Z_]+$/;
46740     var alphanum = /^[a-zA-Z0-9_]+$/;
46741     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46742     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46743
46744     // All these messages and functions are configurable
46745     return {
46746         /**
46747          * The function used to validate email addresses
46748          * @param {String} value The email address
46749          */
46750         'email' : function(v){
46751             return email.test(v);
46752         },
46753         /**
46754          * The error text to display when the email validation function returns false
46755          * @type String
46756          */
46757         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46758         /**
46759          * The keystroke filter mask to be applied on email input
46760          * @type RegExp
46761          */
46762         'emailMask' : /[a-z0-9_\.\-@]/i,
46763
46764         /**
46765          * The function used to validate URLs
46766          * @param {String} value The URL
46767          */
46768         'url' : function(v){
46769             return url.test(v);
46770         },
46771         /**
46772          * The error text to display when the url validation function returns false
46773          * @type String
46774          */
46775         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46776         
46777         /**
46778          * The function used to validate alpha values
46779          * @param {String} value The value
46780          */
46781         'alpha' : function(v){
46782             return alpha.test(v);
46783         },
46784         /**
46785          * The error text to display when the alpha validation function returns false
46786          * @type String
46787          */
46788         'alphaText' : 'This field should only contain letters and _',
46789         /**
46790          * The keystroke filter mask to be applied on alpha input
46791          * @type RegExp
46792          */
46793         'alphaMask' : /[a-z_]/i,
46794
46795         /**
46796          * The function used to validate alphanumeric values
46797          * @param {String} value The value
46798          */
46799         'alphanum' : function(v){
46800             return alphanum.test(v);
46801         },
46802         /**
46803          * The error text to display when the alphanumeric validation function returns false
46804          * @type String
46805          */
46806         'alphanumText' : 'This field should only contain letters, numbers and _',
46807         /**
46808          * The keystroke filter mask to be applied on alphanumeric input
46809          * @type RegExp
46810          */
46811         'alphanumMask' : /[a-z0-9_]/i
46812     };
46813 }();//<script type="text/javascript">
46814
46815 /**
46816  * @class Roo.form.FCKeditor
46817  * @extends Roo.form.TextArea
46818  * Wrapper around the FCKEditor http://www.fckeditor.net
46819  * @constructor
46820  * Creates a new FCKeditor
46821  * @param {Object} config Configuration options
46822  */
46823 Roo.form.FCKeditor = function(config){
46824     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46825     this.addEvents({
46826          /**
46827          * @event editorinit
46828          * Fired when the editor is initialized - you can add extra handlers here..
46829          * @param {FCKeditor} this
46830          * @param {Object} the FCK object.
46831          */
46832         editorinit : true
46833     });
46834     
46835     
46836 };
46837 Roo.form.FCKeditor.editors = { };
46838 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46839 {
46840     //defaultAutoCreate : {
46841     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46842     //},
46843     // private
46844     /**
46845      * @cfg {Object} fck options - see fck manual for details.
46846      */
46847     fckconfig : false,
46848     
46849     /**
46850      * @cfg {Object} fck toolbar set (Basic or Default)
46851      */
46852     toolbarSet : 'Basic',
46853     /**
46854      * @cfg {Object} fck BasePath
46855      */ 
46856     basePath : '/fckeditor/',
46857     
46858     
46859     frame : false,
46860     
46861     value : '',
46862     
46863    
46864     onRender : function(ct, position)
46865     {
46866         if(!this.el){
46867             this.defaultAutoCreate = {
46868                 tag: "textarea",
46869                 style:"width:300px;height:60px;",
46870                 autocomplete: "new-password"
46871             };
46872         }
46873         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46874         /*
46875         if(this.grow){
46876             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46877             if(this.preventScrollbars){
46878                 this.el.setStyle("overflow", "hidden");
46879             }
46880             this.el.setHeight(this.growMin);
46881         }
46882         */
46883         //console.log('onrender' + this.getId() );
46884         Roo.form.FCKeditor.editors[this.getId()] = this;
46885          
46886
46887         this.replaceTextarea() ;
46888         
46889     },
46890     
46891     getEditor : function() {
46892         return this.fckEditor;
46893     },
46894     /**
46895      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46896      * @param {Mixed} value The value to set
46897      */
46898     
46899     
46900     setValue : function(value)
46901     {
46902         //console.log('setValue: ' + value);
46903         
46904         if(typeof(value) == 'undefined') { // not sure why this is happending...
46905             return;
46906         }
46907         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46908         
46909         //if(!this.el || !this.getEditor()) {
46910         //    this.value = value;
46911             //this.setValue.defer(100,this,[value]);    
46912         //    return;
46913         //} 
46914         
46915         if(!this.getEditor()) {
46916             return;
46917         }
46918         
46919         this.getEditor().SetData(value);
46920         
46921         //
46922
46923     },
46924
46925     /**
46926      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46927      * @return {Mixed} value The field value
46928      */
46929     getValue : function()
46930     {
46931         
46932         if (this.frame && this.frame.dom.style.display == 'none') {
46933             return Roo.form.FCKeditor.superclass.getValue.call(this);
46934         }
46935         
46936         if(!this.el || !this.getEditor()) {
46937            
46938            // this.getValue.defer(100,this); 
46939             return this.value;
46940         }
46941        
46942         
46943         var value=this.getEditor().GetData();
46944         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46945         return Roo.form.FCKeditor.superclass.getValue.call(this);
46946         
46947
46948     },
46949
46950     /**
46951      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46952      * @return {Mixed} value The field value
46953      */
46954     getRawValue : function()
46955     {
46956         if (this.frame && this.frame.dom.style.display == 'none') {
46957             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46958         }
46959         
46960         if(!this.el || !this.getEditor()) {
46961             //this.getRawValue.defer(100,this); 
46962             return this.value;
46963             return;
46964         }
46965         
46966         
46967         
46968         var value=this.getEditor().GetData();
46969         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46970         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46971          
46972     },
46973     
46974     setSize : function(w,h) {
46975         
46976         
46977         
46978         //if (this.frame && this.frame.dom.style.display == 'none') {
46979         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46980         //    return;
46981         //}
46982         //if(!this.el || !this.getEditor()) {
46983         //    this.setSize.defer(100,this, [w,h]); 
46984         //    return;
46985         //}
46986         
46987         
46988         
46989         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46990         
46991         this.frame.dom.setAttribute('width', w);
46992         this.frame.dom.setAttribute('height', h);
46993         this.frame.setSize(w,h);
46994         
46995     },
46996     
46997     toggleSourceEdit : function(value) {
46998         
46999       
47000          
47001         this.el.dom.style.display = value ? '' : 'none';
47002         this.frame.dom.style.display = value ?  'none' : '';
47003         
47004     },
47005     
47006     
47007     focus: function(tag)
47008     {
47009         if (this.frame.dom.style.display == 'none') {
47010             return Roo.form.FCKeditor.superclass.focus.call(this);
47011         }
47012         if(!this.el || !this.getEditor()) {
47013             this.focus.defer(100,this, [tag]); 
47014             return;
47015         }
47016         
47017         
47018         
47019         
47020         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
47021         this.getEditor().Focus();
47022         if (tgs.length) {
47023             if (!this.getEditor().Selection.GetSelection()) {
47024                 this.focus.defer(100,this, [tag]); 
47025                 return;
47026             }
47027             
47028             
47029             var r = this.getEditor().EditorDocument.createRange();
47030             r.setStart(tgs[0],0);
47031             r.setEnd(tgs[0],0);
47032             this.getEditor().Selection.GetSelection().removeAllRanges();
47033             this.getEditor().Selection.GetSelection().addRange(r);
47034             this.getEditor().Focus();
47035         }
47036         
47037     },
47038     
47039     
47040     
47041     replaceTextarea : function()
47042     {
47043         if ( document.getElementById( this.getId() + '___Frame' ) ) {
47044             return ;
47045         }
47046         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
47047         //{
47048             // We must check the elements firstly using the Id and then the name.
47049         var oTextarea = document.getElementById( this.getId() );
47050         
47051         var colElementsByName = document.getElementsByName( this.getId() ) ;
47052          
47053         oTextarea.style.display = 'none' ;
47054
47055         if ( oTextarea.tabIndex ) {            
47056             this.TabIndex = oTextarea.tabIndex ;
47057         }
47058         
47059         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
47060         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
47061         this.frame = Roo.get(this.getId() + '___Frame')
47062     },
47063     
47064     _getConfigHtml : function()
47065     {
47066         var sConfig = '' ;
47067
47068         for ( var o in this.fckconfig ) {
47069             sConfig += sConfig.length > 0  ? '&amp;' : '';
47070             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
47071         }
47072
47073         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
47074     },
47075     
47076     
47077     _getIFrameHtml : function()
47078     {
47079         var sFile = 'fckeditor.html' ;
47080         /* no idea what this is about..
47081         try
47082         {
47083             if ( (/fcksource=true/i).test( window.top.location.search ) )
47084                 sFile = 'fckeditor.original.html' ;
47085         }
47086         catch (e) { 
47087         */
47088
47089         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
47090         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
47091         
47092         
47093         var html = '<iframe id="' + this.getId() +
47094             '___Frame" src="' + sLink +
47095             '" width="' + this.width +
47096             '" height="' + this.height + '"' +
47097             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
47098             ' frameborder="0" scrolling="no"></iframe>' ;
47099
47100         return html ;
47101     },
47102     
47103     _insertHtmlBefore : function( html, element )
47104     {
47105         if ( element.insertAdjacentHTML )       {
47106             // IE
47107             element.insertAdjacentHTML( 'beforeBegin', html ) ;
47108         } else { // Gecko
47109             var oRange = document.createRange() ;
47110             oRange.setStartBefore( element ) ;
47111             var oFragment = oRange.createContextualFragment( html );
47112             element.parentNode.insertBefore( oFragment, element ) ;
47113         }
47114     }
47115     
47116     
47117   
47118     
47119     
47120     
47121     
47122
47123 });
47124
47125 //Roo.reg('fckeditor', Roo.form.FCKeditor);
47126
47127 function FCKeditor_OnComplete(editorInstance){
47128     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
47129     f.fckEditor = editorInstance;
47130     //console.log("loaded");
47131     f.fireEvent('editorinit', f, editorInstance);
47132
47133   
47134
47135  
47136
47137
47138
47139
47140
47141
47142
47143
47144
47145
47146
47147
47148
47149
47150
47151 //<script type="text/javascript">
47152 /**
47153  * @class Roo.form.GridField
47154  * @extends Roo.form.Field
47155  * Embed a grid (or editable grid into a form)
47156  * STATUS ALPHA
47157  * 
47158  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
47159  * it needs 
47160  * xgrid.store = Roo.data.Store
47161  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
47162  * xgrid.store.reader = Roo.data.JsonReader 
47163  * 
47164  * 
47165  * @constructor
47166  * Creates a new GridField
47167  * @param {Object} config Configuration options
47168  */
47169 Roo.form.GridField = function(config){
47170     Roo.form.GridField.superclass.constructor.call(this, config);
47171      
47172 };
47173
47174 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
47175     /**
47176      * @cfg {Number} width  - used to restrict width of grid..
47177      */
47178     width : 100,
47179     /**
47180      * @cfg {Number} height - used to restrict height of grid..
47181      */
47182     height : 50,
47183      /**
47184      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
47185          * 
47186          *}
47187      */
47188     xgrid : false, 
47189     /**
47190      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47191      * {tag: "input", type: "checkbox", autocomplete: "off"})
47192      */
47193    // defaultAutoCreate : { tag: 'div' },
47194     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
47195     /**
47196      * @cfg {String} addTitle Text to include for adding a title.
47197      */
47198     addTitle : false,
47199     //
47200     onResize : function(){
47201         Roo.form.Field.superclass.onResize.apply(this, arguments);
47202     },
47203
47204     initEvents : function(){
47205         // Roo.form.Checkbox.superclass.initEvents.call(this);
47206         // has no events...
47207        
47208     },
47209
47210
47211     getResizeEl : function(){
47212         return this.wrap;
47213     },
47214
47215     getPositionEl : function(){
47216         return this.wrap;
47217     },
47218
47219     // private
47220     onRender : function(ct, position){
47221         
47222         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
47223         var style = this.style;
47224         delete this.style;
47225         
47226         Roo.form.GridField.superclass.onRender.call(this, ct, position);
47227         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
47228         this.viewEl = this.wrap.createChild({ tag: 'div' });
47229         if (style) {
47230             this.viewEl.applyStyles(style);
47231         }
47232         if (this.width) {
47233             this.viewEl.setWidth(this.width);
47234         }
47235         if (this.height) {
47236             this.viewEl.setHeight(this.height);
47237         }
47238         //if(this.inputValue !== undefined){
47239         //this.setValue(this.value);
47240         
47241         
47242         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
47243         
47244         
47245         this.grid.render();
47246         this.grid.getDataSource().on('remove', this.refreshValue, this);
47247         this.grid.getDataSource().on('update', this.refreshValue, this);
47248         this.grid.on('afteredit', this.refreshValue, this);
47249  
47250     },
47251      
47252     
47253     /**
47254      * Sets the value of the item. 
47255      * @param {String} either an object  or a string..
47256      */
47257     setValue : function(v){
47258         //this.value = v;
47259         v = v || []; // empty set..
47260         // this does not seem smart - it really only affects memoryproxy grids..
47261         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
47262             var ds = this.grid.getDataSource();
47263             // assumes a json reader..
47264             var data = {}
47265             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
47266             ds.loadData( data);
47267         }
47268         // clear selection so it does not get stale.
47269         if (this.grid.sm) { 
47270             this.grid.sm.clearSelections();
47271         }
47272         
47273         Roo.form.GridField.superclass.setValue.call(this, v);
47274         this.refreshValue();
47275         // should load data in the grid really....
47276     },
47277     
47278     // private
47279     refreshValue: function() {
47280          var val = [];
47281         this.grid.getDataSource().each(function(r) {
47282             val.push(r.data);
47283         });
47284         this.el.dom.value = Roo.encode(val);
47285     }
47286     
47287      
47288     
47289     
47290 });/*
47291  * Based on:
47292  * Ext JS Library 1.1.1
47293  * Copyright(c) 2006-2007, Ext JS, LLC.
47294  *
47295  * Originally Released Under LGPL - original licence link has changed is not relivant.
47296  *
47297  * Fork - LGPL
47298  * <script type="text/javascript">
47299  */
47300 /**
47301  * @class Roo.form.DisplayField
47302  * @extends Roo.form.Field
47303  * A generic Field to display non-editable data.
47304  * @cfg {Boolean} closable (true|false) default false
47305  * @constructor
47306  * Creates a new Display Field item.
47307  * @param {Object} config Configuration options
47308  */
47309 Roo.form.DisplayField = function(config){
47310     Roo.form.DisplayField.superclass.constructor.call(this, config);
47311     
47312     this.addEvents({
47313         /**
47314          * @event close
47315          * Fires after the click the close btn
47316              * @param {Roo.form.DisplayField} this
47317              */
47318         close : true
47319     });
47320 };
47321
47322 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
47323     inputType:      'hidden',
47324     allowBlank:     true,
47325     readOnly:         true,
47326     
47327  
47328     /**
47329      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47330      */
47331     focusClass : undefined,
47332     /**
47333      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47334      */
47335     fieldClass: 'x-form-field',
47336     
47337      /**
47338      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
47339      */
47340     valueRenderer: undefined,
47341     
47342     width: 100,
47343     /**
47344      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47345      * {tag: "input", type: "checkbox", autocomplete: "off"})
47346      */
47347      
47348  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
47349  
47350     closable : false,
47351     
47352     onResize : function(){
47353         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
47354         
47355     },
47356
47357     initEvents : function(){
47358         // Roo.form.Checkbox.superclass.initEvents.call(this);
47359         // has no events...
47360         
47361         if(this.closable){
47362             this.closeEl.on('click', this.onClose, this);
47363         }
47364        
47365     },
47366
47367
47368     getResizeEl : function(){
47369         return this.wrap;
47370     },
47371
47372     getPositionEl : function(){
47373         return this.wrap;
47374     },
47375
47376     // private
47377     onRender : function(ct, position){
47378         
47379         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
47380         //if(this.inputValue !== undefined){
47381         this.wrap = this.el.wrap();
47382         
47383         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
47384         
47385         if(this.closable){
47386             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
47387         }
47388         
47389         if (this.bodyStyle) {
47390             this.viewEl.applyStyles(this.bodyStyle);
47391         }
47392         //this.viewEl.setStyle('padding', '2px');
47393         
47394         this.setValue(this.value);
47395         
47396     },
47397 /*
47398     // private
47399     initValue : Roo.emptyFn,
47400
47401   */
47402
47403         // private
47404     onClick : function(){
47405         
47406     },
47407
47408     /**
47409      * Sets the checked state of the checkbox.
47410      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
47411      */
47412     setValue : function(v){
47413         this.value = v;
47414         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
47415         // this might be called before we have a dom element..
47416         if (!this.viewEl) {
47417             return;
47418         }
47419         this.viewEl.dom.innerHTML = html;
47420         Roo.form.DisplayField.superclass.setValue.call(this, v);
47421
47422     },
47423     
47424     onClose : function(e)
47425     {
47426         e.preventDefault();
47427         
47428         this.fireEvent('close', this);
47429     }
47430 });/*
47431  * 
47432  * Licence- LGPL
47433  * 
47434  */
47435
47436 /**
47437  * @class Roo.form.DayPicker
47438  * @extends Roo.form.Field
47439  * A Day picker show [M] [T] [W] ....
47440  * @constructor
47441  * Creates a new Day Picker
47442  * @param {Object} config Configuration options
47443  */
47444 Roo.form.DayPicker= function(config){
47445     Roo.form.DayPicker.superclass.constructor.call(this, config);
47446      
47447 };
47448
47449 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
47450     /**
47451      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47452      */
47453     focusClass : undefined,
47454     /**
47455      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47456      */
47457     fieldClass: "x-form-field",
47458    
47459     /**
47460      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47461      * {tag: "input", type: "checkbox", autocomplete: "off"})
47462      */
47463     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
47464     
47465    
47466     actionMode : 'viewEl', 
47467     //
47468     // private
47469  
47470     inputType : 'hidden',
47471     
47472      
47473     inputElement: false, // real input element?
47474     basedOn: false, // ????
47475     
47476     isFormField: true, // not sure where this is needed!!!!
47477
47478     onResize : function(){
47479         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
47480         if(!this.boxLabel){
47481             this.el.alignTo(this.wrap, 'c-c');
47482         }
47483     },
47484
47485     initEvents : function(){
47486         Roo.form.Checkbox.superclass.initEvents.call(this);
47487         this.el.on("click", this.onClick,  this);
47488         this.el.on("change", this.onClick,  this);
47489     },
47490
47491
47492     getResizeEl : function(){
47493         return this.wrap;
47494     },
47495
47496     getPositionEl : function(){
47497         return this.wrap;
47498     },
47499
47500     
47501     // private
47502     onRender : function(ct, position){
47503         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
47504        
47505         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
47506         
47507         var r1 = '<table><tr>';
47508         var r2 = '<tr class="x-form-daypick-icons">';
47509         for (var i=0; i < 7; i++) {
47510             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
47511             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
47512         }
47513         
47514         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
47515         viewEl.select('img').on('click', this.onClick, this);
47516         this.viewEl = viewEl;   
47517         
47518         
47519         // this will not work on Chrome!!!
47520         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
47521         this.el.on('propertychange', this.setFromHidden,  this);  //ie
47522         
47523         
47524           
47525
47526     },
47527
47528     // private
47529     initValue : Roo.emptyFn,
47530
47531     /**
47532      * Returns the checked state of the checkbox.
47533      * @return {Boolean} True if checked, else false
47534      */
47535     getValue : function(){
47536         return this.el.dom.value;
47537         
47538     },
47539
47540         // private
47541     onClick : function(e){ 
47542         //this.setChecked(!this.checked);
47543         Roo.get(e.target).toggleClass('x-menu-item-checked');
47544         this.refreshValue();
47545         //if(this.el.dom.checked != this.checked){
47546         //    this.setValue(this.el.dom.checked);
47547        // }
47548     },
47549     
47550     // private
47551     refreshValue : function()
47552     {
47553         var val = '';
47554         this.viewEl.select('img',true).each(function(e,i,n)  {
47555             val += e.is(".x-menu-item-checked") ? String(n) : '';
47556         });
47557         this.setValue(val, true);
47558     },
47559
47560     /**
47561      * Sets the checked state of the checkbox.
47562      * On is always based on a string comparison between inputValue and the param.
47563      * @param {Boolean/String} value - the value to set 
47564      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47565      */
47566     setValue : function(v,suppressEvent){
47567         if (!this.el.dom) {
47568             return;
47569         }
47570         var old = this.el.dom.value ;
47571         this.el.dom.value = v;
47572         if (suppressEvent) {
47573             return ;
47574         }
47575          
47576         // update display..
47577         this.viewEl.select('img',true).each(function(e,i,n)  {
47578             
47579             var on = e.is(".x-menu-item-checked");
47580             var newv = v.indexOf(String(n)) > -1;
47581             if (on != newv) {
47582                 e.toggleClass('x-menu-item-checked');
47583             }
47584             
47585         });
47586         
47587         
47588         this.fireEvent('change', this, v, old);
47589         
47590         
47591     },
47592    
47593     // handle setting of hidden value by some other method!!?!?
47594     setFromHidden: function()
47595     {
47596         if(!this.el){
47597             return;
47598         }
47599         //console.log("SET FROM HIDDEN");
47600         //alert('setFrom hidden');
47601         this.setValue(this.el.dom.value);
47602     },
47603     
47604     onDestroy : function()
47605     {
47606         if(this.viewEl){
47607             Roo.get(this.viewEl).remove();
47608         }
47609          
47610         Roo.form.DayPicker.superclass.onDestroy.call(this);
47611     }
47612
47613 });/*
47614  * RooJS Library 1.1.1
47615  * Copyright(c) 2008-2011  Alan Knowles
47616  *
47617  * License - LGPL
47618  */
47619  
47620
47621 /**
47622  * @class Roo.form.ComboCheck
47623  * @extends Roo.form.ComboBox
47624  * A combobox for multiple select items.
47625  *
47626  * FIXME - could do with a reset button..
47627  * 
47628  * @constructor
47629  * Create a new ComboCheck
47630  * @param {Object} config Configuration options
47631  */
47632 Roo.form.ComboCheck = function(config){
47633     Roo.form.ComboCheck.superclass.constructor.call(this, config);
47634     // should verify some data...
47635     // like
47636     // hiddenName = required..
47637     // displayField = required
47638     // valudField == required
47639     var req= [ 'hiddenName', 'displayField', 'valueField' ];
47640     var _t = this;
47641     Roo.each(req, function(e) {
47642         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
47643             throw "Roo.form.ComboCheck : missing value for: " + e;
47644         }
47645     });
47646     
47647     
47648 };
47649
47650 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
47651      
47652      
47653     editable : false,
47654      
47655     selectedClass: 'x-menu-item-checked', 
47656     
47657     // private
47658     onRender : function(ct, position){
47659         var _t = this;
47660         
47661         
47662         
47663         if(!this.tpl){
47664             var cls = 'x-combo-list';
47665
47666             
47667             this.tpl =  new Roo.Template({
47668                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
47669                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47670                    '<span>{' + this.displayField + '}</span>' +
47671                     '</div>' 
47672                 
47673             });
47674         }
47675  
47676         
47677         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47678         this.view.singleSelect = false;
47679         this.view.multiSelect = true;
47680         this.view.toggleSelect = true;
47681         this.pageTb.add(new Roo.Toolbar.Fill(), {
47682             
47683             text: 'Done',
47684             handler: function()
47685             {
47686                 _t.collapse();
47687             }
47688         });
47689     },
47690     
47691     onViewOver : function(e, t){
47692         // do nothing...
47693         return;
47694         
47695     },
47696     
47697     onViewClick : function(doFocus,index){
47698         return;
47699         
47700     },
47701     select: function () {
47702         //Roo.log("SELECT CALLED");
47703     },
47704      
47705     selectByValue : function(xv, scrollIntoView){
47706         var ar = this.getValueArray();
47707         var sels = [];
47708         
47709         Roo.each(ar, function(v) {
47710             if(v === undefined || v === null){
47711                 return;
47712             }
47713             var r = this.findRecord(this.valueField, v);
47714             if(r){
47715                 sels.push(this.store.indexOf(r))
47716                 
47717             }
47718         },this);
47719         this.view.select(sels);
47720         return false;
47721     },
47722     
47723     
47724     
47725     onSelect : function(record, index){
47726        // Roo.log("onselect Called");
47727        // this is only called by the clear button now..
47728         this.view.clearSelections();
47729         this.setValue('[]');
47730         if (this.value != this.valueBefore) {
47731             this.fireEvent('change', this, this.value, this.valueBefore);
47732             this.valueBefore = this.value;
47733         }
47734     },
47735     getValueArray : function()
47736     {
47737         var ar = [] ;
47738         
47739         try {
47740             //Roo.log(this.value);
47741             if (typeof(this.value) == 'undefined') {
47742                 return [];
47743             }
47744             var ar = Roo.decode(this.value);
47745             return  ar instanceof Array ? ar : []; //?? valid?
47746             
47747         } catch(e) {
47748             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47749             return [];
47750         }
47751          
47752     },
47753     expand : function ()
47754     {
47755         
47756         Roo.form.ComboCheck.superclass.expand.call(this);
47757         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47758         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47759         
47760
47761     },
47762     
47763     collapse : function(){
47764         Roo.form.ComboCheck.superclass.collapse.call(this);
47765         var sl = this.view.getSelectedIndexes();
47766         var st = this.store;
47767         var nv = [];
47768         var tv = [];
47769         var r;
47770         Roo.each(sl, function(i) {
47771             r = st.getAt(i);
47772             nv.push(r.get(this.valueField));
47773         },this);
47774         this.setValue(Roo.encode(nv));
47775         if (this.value != this.valueBefore) {
47776
47777             this.fireEvent('change', this, this.value, this.valueBefore);
47778             this.valueBefore = this.value;
47779         }
47780         
47781     },
47782     
47783     setValue : function(v){
47784         // Roo.log(v);
47785         this.value = v;
47786         
47787         var vals = this.getValueArray();
47788         var tv = [];
47789         Roo.each(vals, function(k) {
47790             var r = this.findRecord(this.valueField, k);
47791             if(r){
47792                 tv.push(r.data[this.displayField]);
47793             }else if(this.valueNotFoundText !== undefined){
47794                 tv.push( this.valueNotFoundText );
47795             }
47796         },this);
47797        // Roo.log(tv);
47798         
47799         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47800         this.hiddenField.value = v;
47801         this.value = v;
47802     }
47803     
47804 });/*
47805  * Based on:
47806  * Ext JS Library 1.1.1
47807  * Copyright(c) 2006-2007, Ext JS, LLC.
47808  *
47809  * Originally Released Under LGPL - original licence link has changed is not relivant.
47810  *
47811  * Fork - LGPL
47812  * <script type="text/javascript">
47813  */
47814  
47815 /**
47816  * @class Roo.form.Signature
47817  * @extends Roo.form.Field
47818  * Signature field.  
47819  * @constructor
47820  * 
47821  * @param {Object} config Configuration options
47822  */
47823
47824 Roo.form.Signature = function(config){
47825     Roo.form.Signature.superclass.constructor.call(this, config);
47826     
47827     this.addEvents({// not in used??
47828          /**
47829          * @event confirm
47830          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47831              * @param {Roo.form.Signature} combo This combo box
47832              */
47833         'confirm' : true,
47834         /**
47835          * @event reset
47836          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47837              * @param {Roo.form.ComboBox} combo This combo box
47838              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47839              */
47840         'reset' : true
47841     });
47842 };
47843
47844 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47845     /**
47846      * @cfg {Object} labels Label to use when rendering a form.
47847      * defaults to 
47848      * labels : { 
47849      *      clear : "Clear",
47850      *      confirm : "Confirm"
47851      *  }
47852      */
47853     labels : { 
47854         clear : "Clear",
47855         confirm : "Confirm"
47856     },
47857     /**
47858      * @cfg {Number} width The signature panel width (defaults to 300)
47859      */
47860     width: 300,
47861     /**
47862      * @cfg {Number} height The signature panel height (defaults to 100)
47863      */
47864     height : 100,
47865     /**
47866      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47867      */
47868     allowBlank : false,
47869     
47870     //private
47871     // {Object} signPanel The signature SVG panel element (defaults to {})
47872     signPanel : {},
47873     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47874     isMouseDown : false,
47875     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47876     isConfirmed : false,
47877     // {String} signatureTmp SVG mapping string (defaults to empty string)
47878     signatureTmp : '',
47879     
47880     
47881     defaultAutoCreate : { // modified by initCompnoent..
47882         tag: "input",
47883         type:"hidden"
47884     },
47885
47886     // private
47887     onRender : function(ct, position){
47888         
47889         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47890         
47891         this.wrap = this.el.wrap({
47892             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47893         });
47894         
47895         this.createToolbar(this);
47896         this.signPanel = this.wrap.createChild({
47897                 tag: 'div',
47898                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47899             }, this.el
47900         );
47901             
47902         this.svgID = Roo.id();
47903         this.svgEl = this.signPanel.createChild({
47904               xmlns : 'http://www.w3.org/2000/svg',
47905               tag : 'svg',
47906               id : this.svgID + "-svg",
47907               width: this.width,
47908               height: this.height,
47909               viewBox: '0 0 '+this.width+' '+this.height,
47910               cn : [
47911                 {
47912                     tag: "rect",
47913                     id: this.svgID + "-svg-r",
47914                     width: this.width,
47915                     height: this.height,
47916                     fill: "#ffa"
47917                 },
47918                 {
47919                     tag: "line",
47920                     id: this.svgID + "-svg-l",
47921                     x1: "0", // start
47922                     y1: (this.height*0.8), // start set the line in 80% of height
47923                     x2: this.width, // end
47924                     y2: (this.height*0.8), // end set the line in 80% of height
47925                     'stroke': "#666",
47926                     'stroke-width': "1",
47927                     'stroke-dasharray': "3",
47928                     'shape-rendering': "crispEdges",
47929                     'pointer-events': "none"
47930                 },
47931                 {
47932                     tag: "path",
47933                     id: this.svgID + "-svg-p",
47934                     'stroke': "navy",
47935                     'stroke-width': "3",
47936                     'fill': "none",
47937                     'pointer-events': 'none'
47938                 }
47939               ]
47940         });
47941         this.createSVG();
47942         this.svgBox = this.svgEl.dom.getScreenCTM();
47943     },
47944     createSVG : function(){ 
47945         var svg = this.signPanel;
47946         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47947         var t = this;
47948
47949         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47950         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47951         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47952         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47953         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47954         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47955         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47956         
47957     },
47958     isTouchEvent : function(e){
47959         return e.type.match(/^touch/);
47960     },
47961     getCoords : function (e) {
47962         var pt    = this.svgEl.dom.createSVGPoint();
47963         pt.x = e.clientX; 
47964         pt.y = e.clientY;
47965         if (this.isTouchEvent(e)) {
47966             pt.x =  e.targetTouches[0].clientX;
47967             pt.y = e.targetTouches[0].clientY;
47968         }
47969         var a = this.svgEl.dom.getScreenCTM();
47970         var b = a.inverse();
47971         var mx = pt.matrixTransform(b);
47972         return mx.x + ',' + mx.y;
47973     },
47974     //mouse event headler 
47975     down : function (e) {
47976         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47977         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47978         
47979         this.isMouseDown = true;
47980         
47981         e.preventDefault();
47982     },
47983     move : function (e) {
47984         if (this.isMouseDown) {
47985             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
47986             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
47987         }
47988         
47989         e.preventDefault();
47990     },
47991     up : function (e) {
47992         this.isMouseDown = false;
47993         var sp = this.signatureTmp.split(' ');
47994         
47995         if(sp.length > 1){
47996             if(!sp[sp.length-2].match(/^L/)){
47997                 sp.pop();
47998                 sp.pop();
47999                 sp.push("");
48000                 this.signatureTmp = sp.join(" ");
48001             }
48002         }
48003         if(this.getValue() != this.signatureTmp){
48004             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48005             this.isConfirmed = false;
48006         }
48007         e.preventDefault();
48008     },
48009     
48010     /**
48011      * Protected method that will not generally be called directly. It
48012      * is called when the editor creates its toolbar. Override this method if you need to
48013      * add custom toolbar buttons.
48014      * @param {HtmlEditor} editor
48015      */
48016     createToolbar : function(editor){
48017          function btn(id, toggle, handler){
48018             var xid = fid + '-'+ id ;
48019             return {
48020                 id : xid,
48021                 cmd : id,
48022                 cls : 'x-btn-icon x-edit-'+id,
48023                 enableToggle:toggle !== false,
48024                 scope: editor, // was editor...
48025                 handler:handler||editor.relayBtnCmd,
48026                 clickEvent:'mousedown',
48027                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
48028                 tabIndex:-1
48029             };
48030         }
48031         
48032         
48033         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
48034         this.tb = tb;
48035         this.tb.add(
48036            {
48037                 cls : ' x-signature-btn x-signature-'+id,
48038                 scope: editor, // was editor...
48039                 handler: this.reset,
48040                 clickEvent:'mousedown',
48041                 text: this.labels.clear
48042             },
48043             {
48044                  xtype : 'Fill',
48045                  xns: Roo.Toolbar
48046             }, 
48047             {
48048                 cls : '  x-signature-btn x-signature-'+id,
48049                 scope: editor, // was editor...
48050                 handler: this.confirmHandler,
48051                 clickEvent:'mousedown',
48052                 text: this.labels.confirm
48053             }
48054         );
48055     
48056     },
48057     //public
48058     /**
48059      * when user is clicked confirm then show this image.....
48060      * 
48061      * @return {String} Image Data URI
48062      */
48063     getImageDataURI : function(){
48064         var svg = this.svgEl.dom.parentNode.innerHTML;
48065         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
48066         return src; 
48067     },
48068     /**
48069      * 
48070      * @return {Boolean} this.isConfirmed
48071      */
48072     getConfirmed : function(){
48073         return this.isConfirmed;
48074     },
48075     /**
48076      * 
48077      * @return {Number} this.width
48078      */
48079     getWidth : function(){
48080         return this.width;
48081     },
48082     /**
48083      * 
48084      * @return {Number} this.height
48085      */
48086     getHeight : function(){
48087         return this.height;
48088     },
48089     // private
48090     getSignature : function(){
48091         return this.signatureTmp;
48092     },
48093     // private
48094     reset : function(){
48095         this.signatureTmp = '';
48096         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48097         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
48098         this.isConfirmed = false;
48099         Roo.form.Signature.superclass.reset.call(this);
48100     },
48101     setSignature : function(s){
48102         this.signatureTmp = s;
48103         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48104         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
48105         this.setValue(s);
48106         this.isConfirmed = false;
48107         Roo.form.Signature.superclass.reset.call(this);
48108     }, 
48109     test : function(){
48110 //        Roo.log(this.signPanel.dom.contentWindow.up())
48111     },
48112     //private
48113     setConfirmed : function(){
48114         
48115         
48116         
48117 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
48118     },
48119     // private
48120     confirmHandler : function(){
48121         if(!this.getSignature()){
48122             return;
48123         }
48124         
48125         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
48126         this.setValue(this.getSignature());
48127         this.isConfirmed = true;
48128         
48129         this.fireEvent('confirm', this);
48130     },
48131     // private
48132     // Subclasses should provide the validation implementation by overriding this
48133     validateValue : function(value){
48134         if(this.allowBlank){
48135             return true;
48136         }
48137         
48138         if(this.isConfirmed){
48139             return true;
48140         }
48141         return false;
48142     }
48143 });/*
48144  * Based on:
48145  * Ext JS Library 1.1.1
48146  * Copyright(c) 2006-2007, Ext JS, LLC.
48147  *
48148  * Originally Released Under LGPL - original licence link has changed is not relivant.
48149  *
48150  * Fork - LGPL
48151  * <script type="text/javascript">
48152  */
48153  
48154
48155 /**
48156  * @class Roo.form.ComboBox
48157  * @extends Roo.form.TriggerField
48158  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
48159  * @constructor
48160  * Create a new ComboBox.
48161  * @param {Object} config Configuration options
48162  */
48163 Roo.form.Select = function(config){
48164     Roo.form.Select.superclass.constructor.call(this, config);
48165      
48166 };
48167
48168 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
48169     /**
48170      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
48171      */
48172     /**
48173      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
48174      * rendering into an Roo.Editor, defaults to false)
48175      */
48176     /**
48177      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
48178      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
48179      */
48180     /**
48181      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
48182      */
48183     /**
48184      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
48185      * the dropdown list (defaults to undefined, with no header element)
48186      */
48187
48188      /**
48189      * @cfg {String/Roo.Template} tpl The template to use to render the output
48190      */
48191      
48192     // private
48193     defaultAutoCreate : {tag: "select"  },
48194     /**
48195      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
48196      */
48197     listWidth: undefined,
48198     /**
48199      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
48200      * mode = 'remote' or 'text' if mode = 'local')
48201      */
48202     displayField: undefined,
48203     /**
48204      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
48205      * mode = 'remote' or 'value' if mode = 'local'). 
48206      * Note: use of a valueField requires the user make a selection
48207      * in order for a value to be mapped.
48208      */
48209     valueField: undefined,
48210     
48211     
48212     /**
48213      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
48214      * field's data value (defaults to the underlying DOM element's name)
48215      */
48216     hiddenName: undefined,
48217     /**
48218      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
48219      */
48220     listClass: '',
48221     /**
48222      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
48223      */
48224     selectedClass: 'x-combo-selected',
48225     /**
48226      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
48227      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
48228      * which displays a downward arrow icon).
48229      */
48230     triggerClass : 'x-form-arrow-trigger',
48231     /**
48232      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
48233      */
48234     shadow:'sides',
48235     /**
48236      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
48237      * anchor positions (defaults to 'tl-bl')
48238      */
48239     listAlign: 'tl-bl?',
48240     /**
48241      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
48242      */
48243     maxHeight: 300,
48244     /**
48245      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
48246      * query specified by the allQuery config option (defaults to 'query')
48247      */
48248     triggerAction: 'query',
48249     /**
48250      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
48251      * (defaults to 4, does not apply if editable = false)
48252      */
48253     minChars : 4,
48254     /**
48255      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
48256      * delay (typeAheadDelay) if it matches a known value (defaults to false)
48257      */
48258     typeAhead: false,
48259     /**
48260      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
48261      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
48262      */
48263     queryDelay: 500,
48264     /**
48265      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
48266      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
48267      */
48268     pageSize: 0,
48269     /**
48270      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
48271      * when editable = true (defaults to false)
48272      */
48273     selectOnFocus:false,
48274     /**
48275      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
48276      */
48277     queryParam: 'query',
48278     /**
48279      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
48280      * when mode = 'remote' (defaults to 'Loading...')
48281      */
48282     loadingText: 'Loading...',
48283     /**
48284      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
48285      */
48286     resizable: false,
48287     /**
48288      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
48289      */
48290     handleHeight : 8,
48291     /**
48292      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
48293      * traditional select (defaults to true)
48294      */
48295     editable: true,
48296     /**
48297      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
48298      */
48299     allQuery: '',
48300     /**
48301      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
48302      */
48303     mode: 'remote',
48304     /**
48305      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
48306      * listWidth has a higher value)
48307      */
48308     minListWidth : 70,
48309     /**
48310      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
48311      * allow the user to set arbitrary text into the field (defaults to false)
48312      */
48313     forceSelection:false,
48314     /**
48315      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
48316      * if typeAhead = true (defaults to 250)
48317      */
48318     typeAheadDelay : 250,
48319     /**
48320      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
48321      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
48322      */
48323     valueNotFoundText : undefined,
48324     
48325     /**
48326      * @cfg {String} defaultValue The value displayed after loading the store.
48327      */
48328     defaultValue: '',
48329     
48330     /**
48331      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
48332      */
48333     blockFocus : false,
48334     
48335     /**
48336      * @cfg {Boolean} disableClear Disable showing of clear button.
48337      */
48338     disableClear : false,
48339     /**
48340      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
48341      */
48342     alwaysQuery : false,
48343     
48344     //private
48345     addicon : false,
48346     editicon: false,
48347     
48348     // element that contains real text value.. (when hidden is used..)
48349      
48350     // private
48351     onRender : function(ct, position){
48352         Roo.form.Field.prototype.onRender.call(this, ct, position);
48353         
48354         if(this.store){
48355             this.store.on('beforeload', this.onBeforeLoad, this);
48356             this.store.on('load', this.onLoad, this);
48357             this.store.on('loadexception', this.onLoadException, this);
48358             this.store.load({});
48359         }
48360         
48361         
48362         
48363     },
48364
48365     // private
48366     initEvents : function(){
48367         //Roo.form.ComboBox.superclass.initEvents.call(this);
48368  
48369     },
48370
48371     onDestroy : function(){
48372        
48373         if(this.store){
48374             this.store.un('beforeload', this.onBeforeLoad, this);
48375             this.store.un('load', this.onLoad, this);
48376             this.store.un('loadexception', this.onLoadException, this);
48377         }
48378         //Roo.form.ComboBox.superclass.onDestroy.call(this);
48379     },
48380
48381     // private
48382     fireKey : function(e){
48383         if(e.isNavKeyPress() && !this.list.isVisible()){
48384             this.fireEvent("specialkey", this, e);
48385         }
48386     },
48387
48388     // private
48389     onResize: function(w, h){
48390         
48391         return; 
48392     
48393         
48394     },
48395
48396     /**
48397      * Allow or prevent the user from directly editing the field text.  If false is passed,
48398      * the user will only be able to select from the items defined in the dropdown list.  This method
48399      * is the runtime equivalent of setting the 'editable' config option at config time.
48400      * @param {Boolean} value True to allow the user to directly edit the field text
48401      */
48402     setEditable : function(value){
48403          
48404     },
48405
48406     // private
48407     onBeforeLoad : function(){
48408         
48409         Roo.log("Select before load");
48410         return;
48411     
48412         this.innerList.update(this.loadingText ?
48413                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
48414         //this.restrictHeight();
48415         this.selectedIndex = -1;
48416     },
48417
48418     // private
48419     onLoad : function(){
48420
48421     
48422         var dom = this.el.dom;
48423         dom.innerHTML = '';
48424          var od = dom.ownerDocument;
48425          
48426         if (this.emptyText) {
48427             var op = od.createElement('option');
48428             op.setAttribute('value', '');
48429             op.innerHTML = String.format('{0}', this.emptyText);
48430             dom.appendChild(op);
48431         }
48432         if(this.store.getCount() > 0){
48433            
48434             var vf = this.valueField;
48435             var df = this.displayField;
48436             this.store.data.each(function(r) {
48437                 // which colmsn to use... testing - cdoe / title..
48438                 var op = od.createElement('option');
48439                 op.setAttribute('value', r.data[vf]);
48440                 op.innerHTML = String.format('{0}', r.data[df]);
48441                 dom.appendChild(op);
48442             });
48443             if (typeof(this.defaultValue != 'undefined')) {
48444                 this.setValue(this.defaultValue);
48445             }
48446             
48447              
48448         }else{
48449             //this.onEmptyResults();
48450         }
48451         //this.el.focus();
48452     },
48453     // private
48454     onLoadException : function()
48455     {
48456         dom.innerHTML = '';
48457             
48458         Roo.log("Select on load exception");
48459         return;
48460     
48461         this.collapse();
48462         Roo.log(this.store.reader.jsonData);
48463         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
48464             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
48465         }
48466         
48467         
48468     },
48469     // private
48470     onTypeAhead : function(){
48471          
48472     },
48473
48474     // private
48475     onSelect : function(record, index){
48476         Roo.log('on select?');
48477         return;
48478         if(this.fireEvent('beforeselect', this, record, index) !== false){
48479             this.setFromData(index > -1 ? record.data : false);
48480             this.collapse();
48481             this.fireEvent('select', this, record, index);
48482         }
48483     },
48484
48485     /**
48486      * Returns the currently selected field value or empty string if no value is set.
48487      * @return {String} value The selected value
48488      */
48489     getValue : function(){
48490         var dom = this.el.dom;
48491         this.value = dom.options[dom.selectedIndex].value;
48492         return this.value;
48493         
48494     },
48495
48496     /**
48497      * Clears any text/value currently set in the field
48498      */
48499     clearValue : function(){
48500         this.value = '';
48501         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
48502         
48503     },
48504
48505     /**
48506      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
48507      * will be displayed in the field.  If the value does not match the data value of an existing item,
48508      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
48509      * Otherwise the field will be blank (although the value will still be set).
48510      * @param {String} value The value to match
48511      */
48512     setValue : function(v){
48513         var d = this.el.dom;
48514         for (var i =0; i < d.options.length;i++) {
48515             if (v == d.options[i].value) {
48516                 d.selectedIndex = i;
48517                 this.value = v;
48518                 return;
48519             }
48520         }
48521         this.clearValue();
48522     },
48523     /**
48524      * @property {Object} the last set data for the element
48525      */
48526     
48527     lastData : false,
48528     /**
48529      * Sets the value of the field based on a object which is related to the record format for the store.
48530      * @param {Object} value the value to set as. or false on reset?
48531      */
48532     setFromData : function(o){
48533         Roo.log('setfrom data?');
48534          
48535         
48536         
48537     },
48538     // private
48539     reset : function(){
48540         this.clearValue();
48541     },
48542     // private
48543     findRecord : function(prop, value){
48544         
48545         return false;
48546     
48547         var record;
48548         if(this.store.getCount() > 0){
48549             this.store.each(function(r){
48550                 if(r.data[prop] == value){
48551                     record = r;
48552                     return false;
48553                 }
48554                 return true;
48555             });
48556         }
48557         return record;
48558     },
48559     
48560     getName: function()
48561     {
48562         // returns hidden if it's set..
48563         if (!this.rendered) {return ''};
48564         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
48565         
48566     },
48567      
48568
48569     
48570
48571     // private
48572     onEmptyResults : function(){
48573         Roo.log('empty results');
48574         //this.collapse();
48575     },
48576
48577     /**
48578      * Returns true if the dropdown list is expanded, else false.
48579      */
48580     isExpanded : function(){
48581         return false;
48582     },
48583
48584     /**
48585      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
48586      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48587      * @param {String} value The data value of the item to select
48588      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48589      * selected item if it is not currently in view (defaults to true)
48590      * @return {Boolean} True if the value matched an item in the list, else false
48591      */
48592     selectByValue : function(v, scrollIntoView){
48593         Roo.log('select By Value');
48594         return false;
48595     
48596         if(v !== undefined && v !== null){
48597             var r = this.findRecord(this.valueField || this.displayField, v);
48598             if(r){
48599                 this.select(this.store.indexOf(r), scrollIntoView);
48600                 return true;
48601             }
48602         }
48603         return false;
48604     },
48605
48606     /**
48607      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
48608      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48609      * @param {Number} index The zero-based index of the list item to select
48610      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48611      * selected item if it is not currently in view (defaults to true)
48612      */
48613     select : function(index, scrollIntoView){
48614         Roo.log('select ');
48615         return  ;
48616         
48617         this.selectedIndex = index;
48618         this.view.select(index);
48619         if(scrollIntoView !== false){
48620             var el = this.view.getNode(index);
48621             if(el){
48622                 this.innerList.scrollChildIntoView(el, false);
48623             }
48624         }
48625     },
48626
48627       
48628
48629     // private
48630     validateBlur : function(){
48631         
48632         return;
48633         
48634     },
48635
48636     // private
48637     initQuery : function(){
48638         this.doQuery(this.getRawValue());
48639     },
48640
48641     // private
48642     doForce : function(){
48643         if(this.el.dom.value.length > 0){
48644             this.el.dom.value =
48645                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
48646              
48647         }
48648     },
48649
48650     /**
48651      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
48652      * query allowing the query action to be canceled if needed.
48653      * @param {String} query The SQL query to execute
48654      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
48655      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
48656      * saved in the current store (defaults to false)
48657      */
48658     doQuery : function(q, forceAll){
48659         
48660         Roo.log('doQuery?');
48661         if(q === undefined || q === null){
48662             q = '';
48663         }
48664         var qe = {
48665             query: q,
48666             forceAll: forceAll,
48667             combo: this,
48668             cancel:false
48669         };
48670         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48671             return false;
48672         }
48673         q = qe.query;
48674         forceAll = qe.forceAll;
48675         if(forceAll === true || (q.length >= this.minChars)){
48676             if(this.lastQuery != q || this.alwaysQuery){
48677                 this.lastQuery = q;
48678                 if(this.mode == 'local'){
48679                     this.selectedIndex = -1;
48680                     if(forceAll){
48681                         this.store.clearFilter();
48682                     }else{
48683                         this.store.filter(this.displayField, q);
48684                     }
48685                     this.onLoad();
48686                 }else{
48687                     this.store.baseParams[this.queryParam] = q;
48688                     this.store.load({
48689                         params: this.getParams(q)
48690                     });
48691                     this.expand();
48692                 }
48693             }else{
48694                 this.selectedIndex = -1;
48695                 this.onLoad();   
48696             }
48697         }
48698     },
48699
48700     // private
48701     getParams : function(q){
48702         var p = {};
48703         //p[this.queryParam] = q;
48704         if(this.pageSize){
48705             p.start = 0;
48706             p.limit = this.pageSize;
48707         }
48708         return p;
48709     },
48710
48711     /**
48712      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48713      */
48714     collapse : function(){
48715         
48716     },
48717
48718     // private
48719     collapseIf : function(e){
48720         
48721     },
48722
48723     /**
48724      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48725      */
48726     expand : function(){
48727         
48728     } ,
48729
48730     // private
48731      
48732
48733     /** 
48734     * @cfg {Boolean} grow 
48735     * @hide 
48736     */
48737     /** 
48738     * @cfg {Number} growMin 
48739     * @hide 
48740     */
48741     /** 
48742     * @cfg {Number} growMax 
48743     * @hide 
48744     */
48745     /**
48746      * @hide
48747      * @method autoSize
48748      */
48749     
48750     setWidth : function()
48751     {
48752         
48753     },
48754     getResizeEl : function(){
48755         return this.el;
48756     }
48757 });//<script type="text/javasscript">
48758  
48759
48760 /**
48761  * @class Roo.DDView
48762  * A DnD enabled version of Roo.View.
48763  * @param {Element/String} container The Element in which to create the View.
48764  * @param {String} tpl The template string used to create the markup for each element of the View
48765  * @param {Object} config The configuration properties. These include all the config options of
48766  * {@link Roo.View} plus some specific to this class.<br>
48767  * <p>
48768  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48769  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48770  * <p>
48771  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48772 .x-view-drag-insert-above {
48773         border-top:1px dotted #3366cc;
48774 }
48775 .x-view-drag-insert-below {
48776         border-bottom:1px dotted #3366cc;
48777 }
48778 </code></pre>
48779  * 
48780  */
48781  
48782 Roo.DDView = function(container, tpl, config) {
48783     Roo.DDView.superclass.constructor.apply(this, arguments);
48784     this.getEl().setStyle("outline", "0px none");
48785     this.getEl().unselectable();
48786     if (this.dragGroup) {
48787                 this.setDraggable(this.dragGroup.split(","));
48788     }
48789     if (this.dropGroup) {
48790                 this.setDroppable(this.dropGroup.split(","));
48791     }
48792     if (this.deletable) {
48793         this.setDeletable();
48794     }
48795     this.isDirtyFlag = false;
48796         this.addEvents({
48797                 "drop" : true
48798         });
48799 };
48800
48801 Roo.extend(Roo.DDView, Roo.View, {
48802 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48803 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48804 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48805 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48806
48807         isFormField: true,
48808
48809         reset: Roo.emptyFn,
48810         
48811         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48812
48813         validate: function() {
48814                 return true;
48815         },
48816         
48817         destroy: function() {
48818                 this.purgeListeners();
48819                 this.getEl.removeAllListeners();
48820                 this.getEl().remove();
48821                 if (this.dragZone) {
48822                         if (this.dragZone.destroy) {
48823                                 this.dragZone.destroy();
48824                         }
48825                 }
48826                 if (this.dropZone) {
48827                         if (this.dropZone.destroy) {
48828                                 this.dropZone.destroy();
48829                         }
48830                 }
48831         },
48832
48833 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48834         getName: function() {
48835                 return this.name;
48836         },
48837
48838 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48839         setValue: function(v) {
48840                 if (!this.store) {
48841                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48842                 }
48843                 var data = {};
48844                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48845                 this.store.proxy = new Roo.data.MemoryProxy(data);
48846                 this.store.load();
48847         },
48848
48849 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48850         getValue: function() {
48851                 var result = '(';
48852                 this.store.each(function(rec) {
48853                         result += rec.id + ',';
48854                 });
48855                 return result.substr(0, result.length - 1) + ')';
48856         },
48857         
48858         getIds: function() {
48859                 var i = 0, result = new Array(this.store.getCount());
48860                 this.store.each(function(rec) {
48861                         result[i++] = rec.id;
48862                 });
48863                 return result;
48864         },
48865         
48866         isDirty: function() {
48867                 return this.isDirtyFlag;
48868         },
48869
48870 /**
48871  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48872  *      whole Element becomes the target, and this causes the drop gesture to append.
48873  */
48874     getTargetFromEvent : function(e) {
48875                 var target = e.getTarget();
48876                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48877                 target = target.parentNode;
48878                 }
48879                 if (!target) {
48880                         target = this.el.dom.lastChild || this.el.dom;
48881                 }
48882                 return target;
48883     },
48884
48885 /**
48886  *      Create the drag data which consists of an object which has the property "ddel" as
48887  *      the drag proxy element. 
48888  */
48889     getDragData : function(e) {
48890         var target = this.findItemFromChild(e.getTarget());
48891                 if(target) {
48892                         this.handleSelection(e);
48893                         var selNodes = this.getSelectedNodes();
48894             var dragData = {
48895                 source: this,
48896                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48897                 nodes: selNodes,
48898                 records: []
48899                         };
48900                         var selectedIndices = this.getSelectedIndexes();
48901                         for (var i = 0; i < selectedIndices.length; i++) {
48902                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48903                         }
48904                         if (selNodes.length == 1) {
48905                                 dragData.ddel = target.cloneNode(true); // the div element
48906                         } else {
48907                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48908                                 div.className = 'multi-proxy';
48909                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48910                                         div.appendChild(selNodes[i].cloneNode(true));
48911                                 }
48912                                 dragData.ddel = div;
48913                         }
48914             //console.log(dragData)
48915             //console.log(dragData.ddel.innerHTML)
48916                         return dragData;
48917                 }
48918         //console.log('nodragData')
48919                 return false;
48920     },
48921     
48922 /**     Specify to which ddGroup items in this DDView may be dragged. */
48923     setDraggable: function(ddGroup) {
48924         if (ddGroup instanceof Array) {
48925                 Roo.each(ddGroup, this.setDraggable, this);
48926                 return;
48927         }
48928         if (this.dragZone) {
48929                 this.dragZone.addToGroup(ddGroup);
48930         } else {
48931                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48932                                 containerScroll: true,
48933                                 ddGroup: ddGroup 
48934
48935                         });
48936 //                      Draggability implies selection. DragZone's mousedown selects the element.
48937                         if (!this.multiSelect) { this.singleSelect = true; }
48938
48939 //                      Wire the DragZone's handlers up to methods in *this*
48940                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48941                 }
48942     },
48943
48944 /**     Specify from which ddGroup this DDView accepts drops. */
48945     setDroppable: function(ddGroup) {
48946         if (ddGroup instanceof Array) {
48947                 Roo.each(ddGroup, this.setDroppable, this);
48948                 return;
48949         }
48950         if (this.dropZone) {
48951                 this.dropZone.addToGroup(ddGroup);
48952         } else {
48953                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48954                                 containerScroll: true,
48955                                 ddGroup: ddGroup
48956                         });
48957
48958 //                      Wire the DropZone's handlers up to methods in *this*
48959                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48960                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48961                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48962                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48963                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48964                 }
48965     },
48966
48967 /**     Decide whether to drop above or below a View node. */
48968     getDropPoint : function(e, n, dd){
48969         if (n == this.el.dom) { return "above"; }
48970                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48971                 var c = t + (b - t) / 2;
48972                 var y = Roo.lib.Event.getPageY(e);
48973                 if(y <= c) {
48974                         return "above";
48975                 }else{
48976                         return "below";
48977                 }
48978     },
48979
48980     onNodeEnter : function(n, dd, e, data){
48981                 return false;
48982     },
48983     
48984     onNodeOver : function(n, dd, e, data){
48985                 var pt = this.getDropPoint(e, n, dd);
48986                 // set the insert point style on the target node
48987                 var dragElClass = this.dropNotAllowed;
48988                 if (pt) {
48989                         var targetElClass;
48990                         if (pt == "above"){
48991                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
48992                                 targetElClass = "x-view-drag-insert-above";
48993                         } else {
48994                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
48995                                 targetElClass = "x-view-drag-insert-below";
48996                         }
48997                         if (this.lastInsertClass != targetElClass){
48998                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
48999                                 this.lastInsertClass = targetElClass;
49000                         }
49001                 }
49002                 return dragElClass;
49003         },
49004
49005     onNodeOut : function(n, dd, e, data){
49006                 this.removeDropIndicators(n);
49007     },
49008
49009     onNodeDrop : function(n, dd, e, data){
49010         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
49011                 return false;
49012         }
49013         var pt = this.getDropPoint(e, n, dd);
49014                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
49015                 if (pt == "below") { insertAt++; }
49016                 for (var i = 0; i < data.records.length; i++) {
49017                         var r = data.records[i];
49018                         var dup = this.store.getById(r.id);
49019                         if (dup && (dd != this.dragZone)) {
49020                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
49021                         } else {
49022                                 if (data.copy) {
49023                                         this.store.insert(insertAt++, r.copy());
49024                                 } else {
49025                                         data.source.isDirtyFlag = true;
49026                                         r.store.remove(r);
49027                                         this.store.insert(insertAt++, r);
49028                                 }
49029                                 this.isDirtyFlag = true;
49030                         }
49031                 }
49032                 this.dragZone.cachedTarget = null;
49033                 return true;
49034     },
49035
49036     removeDropIndicators : function(n){
49037                 if(n){
49038                         Roo.fly(n).removeClass([
49039                                 "x-view-drag-insert-above",
49040                                 "x-view-drag-insert-below"]);
49041                         this.lastInsertClass = "_noclass";
49042                 }
49043     },
49044
49045 /**
49046  *      Utility method. Add a delete option to the DDView's context menu.
49047  *      @param {String} imageUrl The URL of the "delete" icon image.
49048  */
49049         setDeletable: function(imageUrl) {
49050                 if (!this.singleSelect && !this.multiSelect) {
49051                         this.singleSelect = true;
49052                 }
49053                 var c = this.getContextMenu();
49054                 this.contextMenu.on("itemclick", function(item) {
49055                         switch (item.id) {
49056                                 case "delete":
49057                                         this.remove(this.getSelectedIndexes());
49058                                         break;
49059                         }
49060                 }, this);
49061                 this.contextMenu.add({
49062                         icon: imageUrl,
49063                         id: "delete",
49064                         text: 'Delete'
49065                 });
49066         },
49067         
49068 /**     Return the context menu for this DDView. */
49069         getContextMenu: function() {
49070                 if (!this.contextMenu) {
49071 //                      Create the View's context menu
49072                         this.contextMenu = new Roo.menu.Menu({
49073                                 id: this.id + "-contextmenu"
49074                         });
49075                         this.el.on("contextmenu", this.showContextMenu, this);
49076                 }
49077                 return this.contextMenu;
49078         },
49079         
49080         disableContextMenu: function() {
49081                 if (this.contextMenu) {
49082                         this.el.un("contextmenu", this.showContextMenu, this);
49083                 }
49084         },
49085
49086         showContextMenu: function(e, item) {
49087         item = this.findItemFromChild(e.getTarget());
49088                 if (item) {
49089                         e.stopEvent();
49090                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
49091                         this.contextMenu.showAt(e.getXY());
49092             }
49093     },
49094
49095 /**
49096  *      Remove {@link Roo.data.Record}s at the specified indices.
49097  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
49098  */
49099     remove: function(selectedIndices) {
49100                 selectedIndices = [].concat(selectedIndices);
49101                 for (var i = 0; i < selectedIndices.length; i++) {
49102                         var rec = this.store.getAt(selectedIndices[i]);
49103                         this.store.remove(rec);
49104                 }
49105     },
49106
49107 /**
49108  *      Double click fires the event, but also, if this is draggable, and there is only one other
49109  *      related DropZone, it transfers the selected node.
49110  */
49111     onDblClick : function(e){
49112         var item = this.findItemFromChild(e.getTarget());
49113         if(item){
49114             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
49115                 return false;
49116             }
49117             if (this.dragGroup) {
49118                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
49119                     while (targets.indexOf(this.dropZone) > -1) {
49120                             targets.remove(this.dropZone);
49121                                 }
49122                     if (targets.length == 1) {
49123                                         this.dragZone.cachedTarget = null;
49124                         var el = Roo.get(targets[0].getEl());
49125                         var box = el.getBox(true);
49126                         targets[0].onNodeDrop(el.dom, {
49127                                 target: el.dom,
49128                                 xy: [box.x, box.y + box.height - 1]
49129                         }, null, this.getDragData(e));
49130                     }
49131                 }
49132         }
49133     },
49134     
49135     handleSelection: function(e) {
49136                 this.dragZone.cachedTarget = null;
49137         var item = this.findItemFromChild(e.getTarget());
49138         if (!item) {
49139                 this.clearSelections(true);
49140                 return;
49141         }
49142                 if (item && (this.multiSelect || this.singleSelect)){
49143                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
49144                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
49145                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
49146                                 this.unselect(item);
49147                         } else {
49148                                 this.select(item, this.multiSelect && e.ctrlKey);
49149                                 this.lastSelection = item;
49150                         }
49151                 }
49152     },
49153
49154     onItemClick : function(item, index, e){
49155                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
49156                         return false;
49157                 }
49158                 return true;
49159     },
49160
49161     unselect : function(nodeInfo, suppressEvent){
49162                 var node = this.getNode(nodeInfo);
49163                 if(node && this.isSelected(node)){
49164                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
49165                                 Roo.fly(node).removeClass(this.selectedClass);
49166                                 this.selections.remove(node);
49167                                 if(!suppressEvent){
49168                                         this.fireEvent("selectionchange", this, this.selections);
49169                                 }
49170                         }
49171                 }
49172     }
49173 });
49174 /*
49175  * Based on:
49176  * Ext JS Library 1.1.1
49177  * Copyright(c) 2006-2007, Ext JS, LLC.
49178  *
49179  * Originally Released Under LGPL - original licence link has changed is not relivant.
49180  *
49181  * Fork - LGPL
49182  * <script type="text/javascript">
49183  */
49184  
49185 /**
49186  * @class Roo.LayoutManager
49187  * @extends Roo.util.Observable
49188  * Base class for layout managers.
49189  */
49190 Roo.LayoutManager = function(container, config){
49191     Roo.LayoutManager.superclass.constructor.call(this);
49192     this.el = Roo.get(container);
49193     // ie scrollbar fix
49194     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
49195         document.body.scroll = "no";
49196     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
49197         this.el.position('relative');
49198     }
49199     this.id = this.el.id;
49200     this.el.addClass("x-layout-container");
49201     /** false to disable window resize monitoring @type Boolean */
49202     this.monitorWindowResize = true;
49203     this.regions = {};
49204     this.addEvents({
49205         /**
49206          * @event layout
49207          * Fires when a layout is performed. 
49208          * @param {Roo.LayoutManager} this
49209          */
49210         "layout" : true,
49211         /**
49212          * @event regionresized
49213          * Fires when the user resizes a region. 
49214          * @param {Roo.LayoutRegion} region The resized region
49215          * @param {Number} newSize The new size (width for east/west, height for north/south)
49216          */
49217         "regionresized" : true,
49218         /**
49219          * @event regioncollapsed
49220          * Fires when a region is collapsed. 
49221          * @param {Roo.LayoutRegion} region The collapsed region
49222          */
49223         "regioncollapsed" : true,
49224         /**
49225          * @event regionexpanded
49226          * Fires when a region is expanded.  
49227          * @param {Roo.LayoutRegion} region The expanded region
49228          */
49229         "regionexpanded" : true
49230     });
49231     this.updating = false;
49232     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49233 };
49234
49235 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
49236     /**
49237      * Returns true if this layout is currently being updated
49238      * @return {Boolean}
49239      */
49240     isUpdating : function(){
49241         return this.updating; 
49242     },
49243     
49244     /**
49245      * Suspend the LayoutManager from doing auto-layouts while
49246      * making multiple add or remove calls
49247      */
49248     beginUpdate : function(){
49249         this.updating = true;    
49250     },
49251     
49252     /**
49253      * Restore auto-layouts and optionally disable the manager from performing a layout
49254      * @param {Boolean} noLayout true to disable a layout update 
49255      */
49256     endUpdate : function(noLayout){
49257         this.updating = false;
49258         if(!noLayout){
49259             this.layout();
49260         }    
49261     },
49262     
49263     layout: function(){
49264         
49265     },
49266     
49267     onRegionResized : function(region, newSize){
49268         this.fireEvent("regionresized", region, newSize);
49269         this.layout();
49270     },
49271     
49272     onRegionCollapsed : function(region){
49273         this.fireEvent("regioncollapsed", region);
49274     },
49275     
49276     onRegionExpanded : function(region){
49277         this.fireEvent("regionexpanded", region);
49278     },
49279         
49280     /**
49281      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
49282      * performs box-model adjustments.
49283      * @return {Object} The size as an object {width: (the width), height: (the height)}
49284      */
49285     getViewSize : function(){
49286         var size;
49287         if(this.el.dom != document.body){
49288             size = this.el.getSize();
49289         }else{
49290             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
49291         }
49292         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
49293         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
49294         return size;
49295     },
49296     
49297     /**
49298      * Returns the Element this layout is bound to.
49299      * @return {Roo.Element}
49300      */
49301     getEl : function(){
49302         return this.el;
49303     },
49304     
49305     /**
49306      * Returns the specified region.
49307      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
49308      * @return {Roo.LayoutRegion}
49309      */
49310     getRegion : function(target){
49311         return this.regions[target.toLowerCase()];
49312     },
49313     
49314     onWindowResize : function(){
49315         if(this.monitorWindowResize){
49316             this.layout();
49317         }
49318     }
49319 });/*
49320  * Based on:
49321  * Ext JS Library 1.1.1
49322  * Copyright(c) 2006-2007, Ext JS, LLC.
49323  *
49324  * Originally Released Under LGPL - original licence link has changed is not relivant.
49325  *
49326  * Fork - LGPL
49327  * <script type="text/javascript">
49328  */
49329 /**
49330  * @class Roo.BorderLayout
49331  * @extends Roo.LayoutManager
49332  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
49333  * please see: <br><br>
49334  * <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>
49335  * <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>
49336  * Example:
49337  <pre><code>
49338  var layout = new Roo.BorderLayout(document.body, {
49339     north: {
49340         initialSize: 25,
49341         titlebar: false
49342     },
49343     west: {
49344         split:true,
49345         initialSize: 200,
49346         minSize: 175,
49347         maxSize: 400,
49348         titlebar: true,
49349         collapsible: true
49350     },
49351     east: {
49352         split:true,
49353         initialSize: 202,
49354         minSize: 175,
49355         maxSize: 400,
49356         titlebar: true,
49357         collapsible: true
49358     },
49359     south: {
49360         split:true,
49361         initialSize: 100,
49362         minSize: 100,
49363         maxSize: 200,
49364         titlebar: true,
49365         collapsible: true
49366     },
49367     center: {
49368         titlebar: true,
49369         autoScroll:true,
49370         resizeTabs: true,
49371         minTabWidth: 50,
49372         preferredTabWidth: 150
49373     }
49374 });
49375
49376 // shorthand
49377 var CP = Roo.ContentPanel;
49378
49379 layout.beginUpdate();
49380 layout.add("north", new CP("north", "North"));
49381 layout.add("south", new CP("south", {title: "South", closable: true}));
49382 layout.add("west", new CP("west", {title: "West"}));
49383 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
49384 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
49385 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
49386 layout.getRegion("center").showPanel("center1");
49387 layout.endUpdate();
49388 </code></pre>
49389
49390 <b>The container the layout is rendered into can be either the body element or any other element.
49391 If it is not the body element, the container needs to either be an absolute positioned element,
49392 or you will need to add "position:relative" to the css of the container.  You will also need to specify
49393 the container size if it is not the body element.</b>
49394
49395 * @constructor
49396 * Create a new BorderLayout
49397 * @param {String/HTMLElement/Element} container The container this layout is bound to
49398 * @param {Object} config Configuration options
49399  */
49400 Roo.BorderLayout = function(container, config){
49401     config = config || {};
49402     Roo.BorderLayout.superclass.constructor.call(this, container, config);
49403     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
49404     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
49405         var target = this.factory.validRegions[i];
49406         if(config[target]){
49407             this.addRegion(target, config[target]);
49408         }
49409     }
49410 };
49411
49412 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
49413     /**
49414      * Creates and adds a new region if it doesn't already exist.
49415      * @param {String} target The target region key (north, south, east, west or center).
49416      * @param {Object} config The regions config object
49417      * @return {BorderLayoutRegion} The new region
49418      */
49419     addRegion : function(target, config){
49420         if(!this.regions[target]){
49421             var r = this.factory.create(target, this, config);
49422             this.bindRegion(target, r);
49423         }
49424         return this.regions[target];
49425     },
49426
49427     // private (kinda)
49428     bindRegion : function(name, r){
49429         this.regions[name] = r;
49430         r.on("visibilitychange", this.layout, this);
49431         r.on("paneladded", this.layout, this);
49432         r.on("panelremoved", this.layout, this);
49433         r.on("invalidated", this.layout, this);
49434         r.on("resized", this.onRegionResized, this);
49435         r.on("collapsed", this.onRegionCollapsed, this);
49436         r.on("expanded", this.onRegionExpanded, this);
49437     },
49438
49439     /**
49440      * Performs a layout update.
49441      */
49442     layout : function(){
49443         if(this.updating) {
49444             return;
49445         }
49446         var size = this.getViewSize();
49447         var w = size.width;
49448         var h = size.height;
49449         var centerW = w;
49450         var centerH = h;
49451         var centerY = 0;
49452         var centerX = 0;
49453         //var x = 0, y = 0;
49454
49455         var rs = this.regions;
49456         var north = rs["north"];
49457         var south = rs["south"]; 
49458         var west = rs["west"];
49459         var east = rs["east"];
49460         var center = rs["center"];
49461         //if(this.hideOnLayout){ // not supported anymore
49462             //c.el.setStyle("display", "none");
49463         //}
49464         if(north && north.isVisible()){
49465             var b = north.getBox();
49466             var m = north.getMargins();
49467             b.width = w - (m.left+m.right);
49468             b.x = m.left;
49469             b.y = m.top;
49470             centerY = b.height + b.y + m.bottom;
49471             centerH -= centerY;
49472             north.updateBox(this.safeBox(b));
49473         }
49474         if(south && south.isVisible()){
49475             var b = south.getBox();
49476             var m = south.getMargins();
49477             b.width = w - (m.left+m.right);
49478             b.x = m.left;
49479             var totalHeight = (b.height + m.top + m.bottom);
49480             b.y = h - totalHeight + m.top;
49481             centerH -= totalHeight;
49482             south.updateBox(this.safeBox(b));
49483         }
49484         if(west && west.isVisible()){
49485             var b = west.getBox();
49486             var m = west.getMargins();
49487             b.height = centerH - (m.top+m.bottom);
49488             b.x = m.left;
49489             b.y = centerY + m.top;
49490             var totalWidth = (b.width + m.left + m.right);
49491             centerX += totalWidth;
49492             centerW -= totalWidth;
49493             west.updateBox(this.safeBox(b));
49494         }
49495         if(east && east.isVisible()){
49496             var b = east.getBox();
49497             var m = east.getMargins();
49498             b.height = centerH - (m.top+m.bottom);
49499             var totalWidth = (b.width + m.left + m.right);
49500             b.x = w - totalWidth + m.left;
49501             b.y = centerY + m.top;
49502             centerW -= totalWidth;
49503             east.updateBox(this.safeBox(b));
49504         }
49505         if(center){
49506             var m = center.getMargins();
49507             var centerBox = {
49508                 x: centerX + m.left,
49509                 y: centerY + m.top,
49510                 width: centerW - (m.left+m.right),
49511                 height: centerH - (m.top+m.bottom)
49512             };
49513             //if(this.hideOnLayout){
49514                 //center.el.setStyle("display", "block");
49515             //}
49516             center.updateBox(this.safeBox(centerBox));
49517         }
49518         this.el.repaint();
49519         this.fireEvent("layout", this);
49520     },
49521
49522     // private
49523     safeBox : function(box){
49524         box.width = Math.max(0, box.width);
49525         box.height = Math.max(0, box.height);
49526         return box;
49527     },
49528
49529     /**
49530      * Adds a ContentPanel (or subclass) to this layout.
49531      * @param {String} target The target region key (north, south, east, west or center).
49532      * @param {Roo.ContentPanel} panel The panel to add
49533      * @return {Roo.ContentPanel} The added panel
49534      */
49535     add : function(target, panel){
49536          
49537         target = target.toLowerCase();
49538         return this.regions[target].add(panel);
49539     },
49540
49541     /**
49542      * Remove a ContentPanel (or subclass) to this layout.
49543      * @param {String} target The target region key (north, south, east, west or center).
49544      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
49545      * @return {Roo.ContentPanel} The removed panel
49546      */
49547     remove : function(target, panel){
49548         target = target.toLowerCase();
49549         return this.regions[target].remove(panel);
49550     },
49551
49552     /**
49553      * Searches all regions for a panel with the specified id
49554      * @param {String} panelId
49555      * @return {Roo.ContentPanel} The panel or null if it wasn't found
49556      */
49557     findPanel : function(panelId){
49558         var rs = this.regions;
49559         for(var target in rs){
49560             if(typeof rs[target] != "function"){
49561                 var p = rs[target].getPanel(panelId);
49562                 if(p){
49563                     return p;
49564                 }
49565             }
49566         }
49567         return null;
49568     },
49569
49570     /**
49571      * Searches all regions for a panel with the specified id and activates (shows) it.
49572      * @param {String/ContentPanel} panelId The panels id or the panel itself
49573      * @return {Roo.ContentPanel} The shown panel or null
49574      */
49575     showPanel : function(panelId) {
49576       var rs = this.regions;
49577       for(var target in rs){
49578          var r = rs[target];
49579          if(typeof r != "function"){
49580             if(r.hasPanel(panelId)){
49581                return r.showPanel(panelId);
49582             }
49583          }
49584       }
49585       return null;
49586    },
49587
49588    /**
49589      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
49590      * @param {Roo.state.Provider} provider (optional) An alternate state provider
49591      */
49592     restoreState : function(provider){
49593         if(!provider){
49594             provider = Roo.state.Manager;
49595         }
49596         var sm = new Roo.LayoutStateManager();
49597         sm.init(this, provider);
49598     },
49599
49600     /**
49601      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
49602      * object should contain properties for each region to add ContentPanels to, and each property's value should be
49603      * a valid ContentPanel config object.  Example:
49604      * <pre><code>
49605 // Create the main layout
49606 var layout = new Roo.BorderLayout('main-ct', {
49607     west: {
49608         split:true,
49609         minSize: 175,
49610         titlebar: true
49611     },
49612     center: {
49613         title:'Components'
49614     }
49615 }, 'main-ct');
49616
49617 // Create and add multiple ContentPanels at once via configs
49618 layout.batchAdd({
49619    west: {
49620        id: 'source-files',
49621        autoCreate:true,
49622        title:'Ext Source Files',
49623        autoScroll:true,
49624        fitToFrame:true
49625    },
49626    center : {
49627        el: cview,
49628        autoScroll:true,
49629        fitToFrame:true,
49630        toolbar: tb,
49631        resizeEl:'cbody'
49632    }
49633 });
49634 </code></pre>
49635      * @param {Object} regions An object containing ContentPanel configs by region name
49636      */
49637     batchAdd : function(regions){
49638         this.beginUpdate();
49639         for(var rname in regions){
49640             var lr = this.regions[rname];
49641             if(lr){
49642                 this.addTypedPanels(lr, regions[rname]);
49643             }
49644         }
49645         this.endUpdate();
49646     },
49647
49648     // private
49649     addTypedPanels : function(lr, ps){
49650         if(typeof ps == 'string'){
49651             lr.add(new Roo.ContentPanel(ps));
49652         }
49653         else if(ps instanceof Array){
49654             for(var i =0, len = ps.length; i < len; i++){
49655                 this.addTypedPanels(lr, ps[i]);
49656             }
49657         }
49658         else if(!ps.events){ // raw config?
49659             var el = ps.el;
49660             delete ps.el; // prevent conflict
49661             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
49662         }
49663         else {  // panel object assumed!
49664             lr.add(ps);
49665         }
49666     },
49667     /**
49668      * Adds a xtype elements to the layout.
49669      * <pre><code>
49670
49671 layout.addxtype({
49672        xtype : 'ContentPanel',
49673        region: 'west',
49674        items: [ .... ]
49675    }
49676 );
49677
49678 layout.addxtype({
49679         xtype : 'NestedLayoutPanel',
49680         region: 'west',
49681         layout: {
49682            center: { },
49683            west: { }   
49684         },
49685         items : [ ... list of content panels or nested layout panels.. ]
49686    }
49687 );
49688 </code></pre>
49689      * @param {Object} cfg Xtype definition of item to add.
49690      */
49691     addxtype : function(cfg)
49692     {
49693         // basically accepts a pannel...
49694         // can accept a layout region..!?!?
49695         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49696         
49697         if (!cfg.xtype.match(/Panel$/)) {
49698             return false;
49699         }
49700         var ret = false;
49701         
49702         if (typeof(cfg.region) == 'undefined') {
49703             Roo.log("Failed to add Panel, region was not set");
49704             Roo.log(cfg);
49705             return false;
49706         }
49707         var region = cfg.region;
49708         delete cfg.region;
49709         
49710           
49711         var xitems = [];
49712         if (cfg.items) {
49713             xitems = cfg.items;
49714             delete cfg.items;
49715         }
49716         var nb = false;
49717         
49718         switch(cfg.xtype) 
49719         {
49720             case 'ContentPanel':  // ContentPanel (el, cfg)
49721             case 'ScrollPanel':  // ContentPanel (el, cfg)
49722             case 'ViewPanel': 
49723                 if(cfg.autoCreate) {
49724                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49725                 } else {
49726                     var el = this.el.createChild();
49727                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49728                 }
49729                 
49730                 this.add(region, ret);
49731                 break;
49732             
49733             
49734             case 'TreePanel': // our new panel!
49735                 cfg.el = this.el.createChild();
49736                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49737                 this.add(region, ret);
49738                 break;
49739             
49740             case 'NestedLayoutPanel': 
49741                 // create a new Layout (which is  a Border Layout...
49742                 var el = this.el.createChild();
49743                 var clayout = cfg.layout;
49744                 delete cfg.layout;
49745                 clayout.items   = clayout.items  || [];
49746                 // replace this exitems with the clayout ones..
49747                 xitems = clayout.items;
49748                  
49749                 
49750                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49751                     cfg.background = false;
49752                 }
49753                 var layout = new Roo.BorderLayout(el, clayout);
49754                 
49755                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49756                 //console.log('adding nested layout panel '  + cfg.toSource());
49757                 this.add(region, ret);
49758                 nb = {}; /// find first...
49759                 break;
49760                 
49761             case 'GridPanel': 
49762             
49763                 // needs grid and region
49764                 
49765                 //var el = this.getRegion(region).el.createChild();
49766                 var el = this.el.createChild();
49767                 // create the grid first...
49768                 
49769                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49770                 delete cfg.grid;
49771                 if (region == 'center' && this.active ) {
49772                     cfg.background = false;
49773                 }
49774                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49775                 
49776                 this.add(region, ret);
49777                 if (cfg.background) {
49778                     ret.on('activate', function(gp) {
49779                         if (!gp.grid.rendered) {
49780                             gp.grid.render();
49781                         }
49782                     });
49783                 } else {
49784                     grid.render();
49785                 }
49786                 break;
49787            
49788            
49789            
49790                 
49791                 
49792                 
49793             default:
49794                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49795                     
49796                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49797                     this.add(region, ret);
49798                 } else {
49799                 
49800                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49801                     return null;
49802                 }
49803                 
49804              // GridPanel (grid, cfg)
49805             
49806         }
49807         this.beginUpdate();
49808         // add children..
49809         var region = '';
49810         var abn = {};
49811         Roo.each(xitems, function(i)  {
49812             region = nb && i.region ? i.region : false;
49813             
49814             var add = ret.addxtype(i);
49815            
49816             if (region) {
49817                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49818                 if (!i.background) {
49819                     abn[region] = nb[region] ;
49820                 }
49821             }
49822             
49823         });
49824         this.endUpdate();
49825
49826         // make the last non-background panel active..
49827         //if (nb) { Roo.log(abn); }
49828         if (nb) {
49829             
49830             for(var r in abn) {
49831                 region = this.getRegion(r);
49832                 if (region) {
49833                     // tried using nb[r], but it does not work..
49834                      
49835                     region.showPanel(abn[r]);
49836                    
49837                 }
49838             }
49839         }
49840         return ret;
49841         
49842     }
49843 });
49844
49845 /**
49846  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49847  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49848  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49849  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49850  * <pre><code>
49851 // shorthand
49852 var CP = Roo.ContentPanel;
49853
49854 var layout = Roo.BorderLayout.create({
49855     north: {
49856         initialSize: 25,
49857         titlebar: false,
49858         panels: [new CP("north", "North")]
49859     },
49860     west: {
49861         split:true,
49862         initialSize: 200,
49863         minSize: 175,
49864         maxSize: 400,
49865         titlebar: true,
49866         collapsible: true,
49867         panels: [new CP("west", {title: "West"})]
49868     },
49869     east: {
49870         split:true,
49871         initialSize: 202,
49872         minSize: 175,
49873         maxSize: 400,
49874         titlebar: true,
49875         collapsible: true,
49876         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49877     },
49878     south: {
49879         split:true,
49880         initialSize: 100,
49881         minSize: 100,
49882         maxSize: 200,
49883         titlebar: true,
49884         collapsible: true,
49885         panels: [new CP("south", {title: "South", closable: true})]
49886     },
49887     center: {
49888         titlebar: true,
49889         autoScroll:true,
49890         resizeTabs: true,
49891         minTabWidth: 50,
49892         preferredTabWidth: 150,
49893         panels: [
49894             new CP("center1", {title: "Close Me", closable: true}),
49895             new CP("center2", {title: "Center Panel", closable: false})
49896         ]
49897     }
49898 }, document.body);
49899
49900 layout.getRegion("center").showPanel("center1");
49901 </code></pre>
49902  * @param config
49903  * @param targetEl
49904  */
49905 Roo.BorderLayout.create = function(config, targetEl){
49906     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49907     layout.beginUpdate();
49908     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49909     for(var j = 0, jlen = regions.length; j < jlen; j++){
49910         var lr = regions[j];
49911         if(layout.regions[lr] && config[lr].panels){
49912             var r = layout.regions[lr];
49913             var ps = config[lr].panels;
49914             layout.addTypedPanels(r, ps);
49915         }
49916     }
49917     layout.endUpdate();
49918     return layout;
49919 };
49920
49921 // private
49922 Roo.BorderLayout.RegionFactory = {
49923     // private
49924     validRegions : ["north","south","east","west","center"],
49925
49926     // private
49927     create : function(target, mgr, config){
49928         target = target.toLowerCase();
49929         if(config.lightweight || config.basic){
49930             return new Roo.BasicLayoutRegion(mgr, config, target);
49931         }
49932         switch(target){
49933             case "north":
49934                 return new Roo.NorthLayoutRegion(mgr, config);
49935             case "south":
49936                 return new Roo.SouthLayoutRegion(mgr, config);
49937             case "east":
49938                 return new Roo.EastLayoutRegion(mgr, config);
49939             case "west":
49940                 return new Roo.WestLayoutRegion(mgr, config);
49941             case "center":
49942                 return new Roo.CenterLayoutRegion(mgr, config);
49943         }
49944         throw 'Layout region "'+target+'" not supported.';
49945     }
49946 };/*
49947  * Based on:
49948  * Ext JS Library 1.1.1
49949  * Copyright(c) 2006-2007, Ext JS, LLC.
49950  *
49951  * Originally Released Under LGPL - original licence link has changed is not relivant.
49952  *
49953  * Fork - LGPL
49954  * <script type="text/javascript">
49955  */
49956  
49957 /**
49958  * @class Roo.BasicLayoutRegion
49959  * @extends Roo.util.Observable
49960  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49961  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49962  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49963  */
49964 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49965     this.mgr = mgr;
49966     this.position  = pos;
49967     this.events = {
49968         /**
49969          * @scope Roo.BasicLayoutRegion
49970          */
49971         
49972         /**
49973          * @event beforeremove
49974          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49975          * @param {Roo.LayoutRegion} this
49976          * @param {Roo.ContentPanel} panel The panel
49977          * @param {Object} e The cancel event object
49978          */
49979         "beforeremove" : true,
49980         /**
49981          * @event invalidated
49982          * Fires when the layout for this region is changed.
49983          * @param {Roo.LayoutRegion} this
49984          */
49985         "invalidated" : true,
49986         /**
49987          * @event visibilitychange
49988          * Fires when this region is shown or hidden 
49989          * @param {Roo.LayoutRegion} this
49990          * @param {Boolean} visibility true or false
49991          */
49992         "visibilitychange" : true,
49993         /**
49994          * @event paneladded
49995          * Fires when a panel is added. 
49996          * @param {Roo.LayoutRegion} this
49997          * @param {Roo.ContentPanel} panel The panel
49998          */
49999         "paneladded" : true,
50000         /**
50001          * @event panelremoved
50002          * Fires when a panel is removed. 
50003          * @param {Roo.LayoutRegion} this
50004          * @param {Roo.ContentPanel} panel The panel
50005          */
50006         "panelremoved" : true,
50007         /**
50008          * @event collapsed
50009          * Fires when this region is collapsed.
50010          * @param {Roo.LayoutRegion} this
50011          */
50012         "collapsed" : true,
50013         /**
50014          * @event expanded
50015          * Fires when this region is expanded.
50016          * @param {Roo.LayoutRegion} this
50017          */
50018         "expanded" : true,
50019         /**
50020          * @event slideshow
50021          * Fires when this region is slid into view.
50022          * @param {Roo.LayoutRegion} this
50023          */
50024         "slideshow" : true,
50025         /**
50026          * @event slidehide
50027          * Fires when this region slides out of view. 
50028          * @param {Roo.LayoutRegion} this
50029          */
50030         "slidehide" : true,
50031         /**
50032          * @event panelactivated
50033          * Fires when a panel is activated. 
50034          * @param {Roo.LayoutRegion} this
50035          * @param {Roo.ContentPanel} panel The activated panel
50036          */
50037         "panelactivated" : true,
50038         /**
50039          * @event resized
50040          * Fires when the user resizes this region. 
50041          * @param {Roo.LayoutRegion} this
50042          * @param {Number} newSize The new size (width for east/west, height for north/south)
50043          */
50044         "resized" : true
50045     };
50046     /** A collection of panels in this region. @type Roo.util.MixedCollection */
50047     this.panels = new Roo.util.MixedCollection();
50048     this.panels.getKey = this.getPanelId.createDelegate(this);
50049     this.box = null;
50050     this.activePanel = null;
50051     // ensure listeners are added...
50052     
50053     if (config.listeners || config.events) {
50054         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
50055             listeners : config.listeners || {},
50056             events : config.events || {}
50057         });
50058     }
50059     
50060     if(skipConfig !== true){
50061         this.applyConfig(config);
50062     }
50063 };
50064
50065 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
50066     getPanelId : function(p){
50067         return p.getId();
50068     },
50069     
50070     applyConfig : function(config){
50071         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50072         this.config = config;
50073         
50074     },
50075     
50076     /**
50077      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
50078      * the width, for horizontal (north, south) the height.
50079      * @param {Number} newSize The new width or height
50080      */
50081     resizeTo : function(newSize){
50082         var el = this.el ? this.el :
50083                  (this.activePanel ? this.activePanel.getEl() : null);
50084         if(el){
50085             switch(this.position){
50086                 case "east":
50087                 case "west":
50088                     el.setWidth(newSize);
50089                     this.fireEvent("resized", this, newSize);
50090                 break;
50091                 case "north":
50092                 case "south":
50093                     el.setHeight(newSize);
50094                     this.fireEvent("resized", this, newSize);
50095                 break;                
50096             }
50097         }
50098     },
50099     
50100     getBox : function(){
50101         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
50102     },
50103     
50104     getMargins : function(){
50105         return this.margins;
50106     },
50107     
50108     updateBox : function(box){
50109         this.box = box;
50110         var el = this.activePanel.getEl();
50111         el.dom.style.left = box.x + "px";
50112         el.dom.style.top = box.y + "px";
50113         this.activePanel.setSize(box.width, box.height);
50114     },
50115     
50116     /**
50117      * Returns the container element for this region.
50118      * @return {Roo.Element}
50119      */
50120     getEl : function(){
50121         return this.activePanel;
50122     },
50123     
50124     /**
50125      * Returns true if this region is currently visible.
50126      * @return {Boolean}
50127      */
50128     isVisible : function(){
50129         return this.activePanel ? true : false;
50130     },
50131     
50132     setActivePanel : function(panel){
50133         panel = this.getPanel(panel);
50134         if(this.activePanel && this.activePanel != panel){
50135             this.activePanel.setActiveState(false);
50136             this.activePanel.getEl().setLeftTop(-10000,-10000);
50137         }
50138         this.activePanel = panel;
50139         panel.setActiveState(true);
50140         if(this.box){
50141             panel.setSize(this.box.width, this.box.height);
50142         }
50143         this.fireEvent("panelactivated", this, panel);
50144         this.fireEvent("invalidated");
50145     },
50146     
50147     /**
50148      * Show the specified panel.
50149      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
50150      * @return {Roo.ContentPanel} The shown panel or null
50151      */
50152     showPanel : function(panel){
50153         if(panel = this.getPanel(panel)){
50154             this.setActivePanel(panel);
50155         }
50156         return panel;
50157     },
50158     
50159     /**
50160      * Get the active panel for this region.
50161      * @return {Roo.ContentPanel} The active panel or null
50162      */
50163     getActivePanel : function(){
50164         return this.activePanel;
50165     },
50166     
50167     /**
50168      * Add the passed ContentPanel(s)
50169      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50170      * @return {Roo.ContentPanel} The panel added (if only one was added)
50171      */
50172     add : function(panel){
50173         if(arguments.length > 1){
50174             for(var i = 0, len = arguments.length; i < len; i++) {
50175                 this.add(arguments[i]);
50176             }
50177             return null;
50178         }
50179         if(this.hasPanel(panel)){
50180             this.showPanel(panel);
50181             return panel;
50182         }
50183         var el = panel.getEl();
50184         if(el.dom.parentNode != this.mgr.el.dom){
50185             this.mgr.el.dom.appendChild(el.dom);
50186         }
50187         if(panel.setRegion){
50188             panel.setRegion(this);
50189         }
50190         this.panels.add(panel);
50191         el.setStyle("position", "absolute");
50192         if(!panel.background){
50193             this.setActivePanel(panel);
50194             if(this.config.initialSize && this.panels.getCount()==1){
50195                 this.resizeTo(this.config.initialSize);
50196             }
50197         }
50198         this.fireEvent("paneladded", this, panel);
50199         return panel;
50200     },
50201     
50202     /**
50203      * Returns true if the panel is in this region.
50204      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50205      * @return {Boolean}
50206      */
50207     hasPanel : function(panel){
50208         if(typeof panel == "object"){ // must be panel obj
50209             panel = panel.getId();
50210         }
50211         return this.getPanel(panel) ? true : false;
50212     },
50213     
50214     /**
50215      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50216      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50217      * @param {Boolean} preservePanel Overrides the config preservePanel option
50218      * @return {Roo.ContentPanel} The panel that was removed
50219      */
50220     remove : function(panel, preservePanel){
50221         panel = this.getPanel(panel);
50222         if(!panel){
50223             return null;
50224         }
50225         var e = {};
50226         this.fireEvent("beforeremove", this, panel, e);
50227         if(e.cancel === true){
50228             return null;
50229         }
50230         var panelId = panel.getId();
50231         this.panels.removeKey(panelId);
50232         return panel;
50233     },
50234     
50235     /**
50236      * Returns the panel specified or null if it's not in this region.
50237      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50238      * @return {Roo.ContentPanel}
50239      */
50240     getPanel : function(id){
50241         if(typeof id == "object"){ // must be panel obj
50242             return id;
50243         }
50244         return this.panels.get(id);
50245     },
50246     
50247     /**
50248      * Returns this regions position (north/south/east/west/center).
50249      * @return {String} 
50250      */
50251     getPosition: function(){
50252         return this.position;    
50253     }
50254 });/*
50255  * Based on:
50256  * Ext JS Library 1.1.1
50257  * Copyright(c) 2006-2007, Ext JS, LLC.
50258  *
50259  * Originally Released Under LGPL - original licence link has changed is not relivant.
50260  *
50261  * Fork - LGPL
50262  * <script type="text/javascript">
50263  */
50264  
50265 /**
50266  * @class Roo.LayoutRegion
50267  * @extends Roo.BasicLayoutRegion
50268  * This class represents a region in a layout manager.
50269  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
50270  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
50271  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
50272  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
50273  * @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})
50274  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
50275  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
50276  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
50277  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
50278  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
50279  * @cfg {String}    title           The title for the region (overrides panel titles)
50280  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
50281  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
50282  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
50283  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
50284  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
50285  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
50286  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
50287  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
50288  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
50289  * @cfg {Boolean}   showPin         True to show a pin button
50290  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
50291  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
50292  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
50293  * @cfg {Number}    width           For East/West panels
50294  * @cfg {Number}    height          For North/South panels
50295  * @cfg {Boolean}   split           To show the splitter
50296  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
50297  */
50298 Roo.LayoutRegion = function(mgr, config, pos){
50299     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
50300     var dh = Roo.DomHelper;
50301     /** This region's container element 
50302     * @type Roo.Element */
50303     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
50304     /** This region's title element 
50305     * @type Roo.Element */
50306
50307     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
50308         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
50309         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
50310     ]}, true);
50311     this.titleEl.enableDisplayMode();
50312     /** This region's title text element 
50313     * @type HTMLElement */
50314     this.titleTextEl = this.titleEl.dom.firstChild;
50315     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
50316     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
50317     this.closeBtn.enableDisplayMode();
50318     this.closeBtn.on("click", this.closeClicked, this);
50319     this.closeBtn.hide();
50320
50321     this.createBody(config);
50322     this.visible = true;
50323     this.collapsed = false;
50324
50325     if(config.hideWhenEmpty){
50326         this.hide();
50327         this.on("paneladded", this.validateVisibility, this);
50328         this.on("panelremoved", this.validateVisibility, this);
50329     }
50330     this.applyConfig(config);
50331 };
50332
50333 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
50334
50335     createBody : function(){
50336         /** This region's body element 
50337         * @type Roo.Element */
50338         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
50339     },
50340
50341     applyConfig : function(c){
50342         if(c.collapsible && this.position != "center" && !this.collapsedEl){
50343             var dh = Roo.DomHelper;
50344             if(c.titlebar !== false){
50345                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
50346                 this.collapseBtn.on("click", this.collapse, this);
50347                 this.collapseBtn.enableDisplayMode();
50348
50349                 if(c.showPin === true || this.showPin){
50350                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
50351                     this.stickBtn.enableDisplayMode();
50352                     this.stickBtn.on("click", this.expand, this);
50353                     this.stickBtn.hide();
50354                 }
50355             }
50356             /** This region's collapsed element
50357             * @type Roo.Element */
50358             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
50359                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
50360             ]}, true);
50361             if(c.floatable !== false){
50362                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
50363                this.collapsedEl.on("click", this.collapseClick, this);
50364             }
50365
50366             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
50367                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
50368                    id: "message", unselectable: "on", style:{"float":"left"}});
50369                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
50370              }
50371             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
50372             this.expandBtn.on("click", this.expand, this);
50373         }
50374         if(this.collapseBtn){
50375             this.collapseBtn.setVisible(c.collapsible == true);
50376         }
50377         this.cmargins = c.cmargins || this.cmargins ||
50378                          (this.position == "west" || this.position == "east" ?
50379                              {top: 0, left: 2, right:2, bottom: 0} :
50380                              {top: 2, left: 0, right:0, bottom: 2});
50381         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50382         this.bottomTabs = c.tabPosition != "top";
50383         this.autoScroll = c.autoScroll || false;
50384         if(this.autoScroll){
50385             this.bodyEl.setStyle("overflow", "auto");
50386         }else{
50387             this.bodyEl.setStyle("overflow", "hidden");
50388         }
50389         //if(c.titlebar !== false){
50390             if((!c.titlebar && !c.title) || c.titlebar === false){
50391                 this.titleEl.hide();
50392             }else{
50393                 this.titleEl.show();
50394                 if(c.title){
50395                     this.titleTextEl.innerHTML = c.title;
50396                 }
50397             }
50398         //}
50399         this.duration = c.duration || .30;
50400         this.slideDuration = c.slideDuration || .45;
50401         this.config = c;
50402         if(c.collapsed){
50403             this.collapse(true);
50404         }
50405         if(c.hidden){
50406             this.hide();
50407         }
50408     },
50409     /**
50410      * Returns true if this region is currently visible.
50411      * @return {Boolean}
50412      */
50413     isVisible : function(){
50414         return this.visible;
50415     },
50416
50417     /**
50418      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
50419      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
50420      */
50421     setCollapsedTitle : function(title){
50422         title = title || "&#160;";
50423         if(this.collapsedTitleTextEl){
50424             this.collapsedTitleTextEl.innerHTML = title;
50425         }
50426     },
50427
50428     getBox : function(){
50429         var b;
50430         if(!this.collapsed){
50431             b = this.el.getBox(false, true);
50432         }else{
50433             b = this.collapsedEl.getBox(false, true);
50434         }
50435         return b;
50436     },
50437
50438     getMargins : function(){
50439         return this.collapsed ? this.cmargins : this.margins;
50440     },
50441
50442     highlight : function(){
50443         this.el.addClass("x-layout-panel-dragover");
50444     },
50445
50446     unhighlight : function(){
50447         this.el.removeClass("x-layout-panel-dragover");
50448     },
50449
50450     updateBox : function(box){
50451         this.box = box;
50452         if(!this.collapsed){
50453             this.el.dom.style.left = box.x + "px";
50454             this.el.dom.style.top = box.y + "px";
50455             this.updateBody(box.width, box.height);
50456         }else{
50457             this.collapsedEl.dom.style.left = box.x + "px";
50458             this.collapsedEl.dom.style.top = box.y + "px";
50459             this.collapsedEl.setSize(box.width, box.height);
50460         }
50461         if(this.tabs){
50462             this.tabs.autoSizeTabs();
50463         }
50464     },
50465
50466     updateBody : function(w, h){
50467         if(w !== null){
50468             this.el.setWidth(w);
50469             w -= this.el.getBorderWidth("rl");
50470             if(this.config.adjustments){
50471                 w += this.config.adjustments[0];
50472             }
50473         }
50474         if(h !== null){
50475             this.el.setHeight(h);
50476             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
50477             h -= this.el.getBorderWidth("tb");
50478             if(this.config.adjustments){
50479                 h += this.config.adjustments[1];
50480             }
50481             this.bodyEl.setHeight(h);
50482             if(this.tabs){
50483                 h = this.tabs.syncHeight(h);
50484             }
50485         }
50486         if(this.panelSize){
50487             w = w !== null ? w : this.panelSize.width;
50488             h = h !== null ? h : this.panelSize.height;
50489         }
50490         if(this.activePanel){
50491             var el = this.activePanel.getEl();
50492             w = w !== null ? w : el.getWidth();
50493             h = h !== null ? h : el.getHeight();
50494             this.panelSize = {width: w, height: h};
50495             this.activePanel.setSize(w, h);
50496         }
50497         if(Roo.isIE && this.tabs){
50498             this.tabs.el.repaint();
50499         }
50500     },
50501
50502     /**
50503      * Returns the container element for this region.
50504      * @return {Roo.Element}
50505      */
50506     getEl : function(){
50507         return this.el;
50508     },
50509
50510     /**
50511      * Hides this region.
50512      */
50513     hide : function(){
50514         if(!this.collapsed){
50515             this.el.dom.style.left = "-2000px";
50516             this.el.hide();
50517         }else{
50518             this.collapsedEl.dom.style.left = "-2000px";
50519             this.collapsedEl.hide();
50520         }
50521         this.visible = false;
50522         this.fireEvent("visibilitychange", this, false);
50523     },
50524
50525     /**
50526      * Shows this region if it was previously hidden.
50527      */
50528     show : function(){
50529         if(!this.collapsed){
50530             this.el.show();
50531         }else{
50532             this.collapsedEl.show();
50533         }
50534         this.visible = true;
50535         this.fireEvent("visibilitychange", this, true);
50536     },
50537
50538     closeClicked : function(){
50539         if(this.activePanel){
50540             this.remove(this.activePanel);
50541         }
50542     },
50543
50544     collapseClick : function(e){
50545         if(this.isSlid){
50546            e.stopPropagation();
50547            this.slideIn();
50548         }else{
50549            e.stopPropagation();
50550            this.slideOut();
50551         }
50552     },
50553
50554     /**
50555      * Collapses this region.
50556      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
50557      */
50558     collapse : function(skipAnim){
50559         if(this.collapsed) {
50560             return;
50561         }
50562         this.collapsed = true;
50563         if(this.split){
50564             this.split.el.hide();
50565         }
50566         if(this.config.animate && skipAnim !== true){
50567             this.fireEvent("invalidated", this);
50568             this.animateCollapse();
50569         }else{
50570             this.el.setLocation(-20000,-20000);
50571             this.el.hide();
50572             this.collapsedEl.show();
50573             this.fireEvent("collapsed", this);
50574             this.fireEvent("invalidated", this);
50575         }
50576     },
50577
50578     animateCollapse : function(){
50579         // overridden
50580     },
50581
50582     /**
50583      * Expands this region if it was previously collapsed.
50584      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
50585      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
50586      */
50587     expand : function(e, skipAnim){
50588         if(e) {
50589             e.stopPropagation();
50590         }
50591         if(!this.collapsed || this.el.hasActiveFx()) {
50592             return;
50593         }
50594         if(this.isSlid){
50595             this.afterSlideIn();
50596             skipAnim = true;
50597         }
50598         this.collapsed = false;
50599         if(this.config.animate && skipAnim !== true){
50600             this.animateExpand();
50601         }else{
50602             this.el.show();
50603             if(this.split){
50604                 this.split.el.show();
50605             }
50606             this.collapsedEl.setLocation(-2000,-2000);
50607             this.collapsedEl.hide();
50608             this.fireEvent("invalidated", this);
50609             this.fireEvent("expanded", this);
50610         }
50611     },
50612
50613     animateExpand : function(){
50614         // overridden
50615     },
50616
50617     initTabs : function()
50618     {
50619         this.bodyEl.setStyle("overflow", "hidden");
50620         var ts = new Roo.TabPanel(
50621                 this.bodyEl.dom,
50622                 {
50623                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
50624                     disableTooltips: this.config.disableTabTips,
50625                     toolbar : this.config.toolbar
50626                 }
50627         );
50628         if(this.config.hideTabs){
50629             ts.stripWrap.setDisplayed(false);
50630         }
50631         this.tabs = ts;
50632         ts.resizeTabs = this.config.resizeTabs === true;
50633         ts.minTabWidth = this.config.minTabWidth || 40;
50634         ts.maxTabWidth = this.config.maxTabWidth || 250;
50635         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
50636         ts.monitorResize = false;
50637         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50638         ts.bodyEl.addClass('x-layout-tabs-body');
50639         this.panels.each(this.initPanelAsTab, this);
50640     },
50641
50642     initPanelAsTab : function(panel){
50643         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
50644                     this.config.closeOnTab && panel.isClosable());
50645         if(panel.tabTip !== undefined){
50646             ti.setTooltip(panel.tabTip);
50647         }
50648         ti.on("activate", function(){
50649               this.setActivePanel(panel);
50650         }, this);
50651         if(this.config.closeOnTab){
50652             ti.on("beforeclose", function(t, e){
50653                 e.cancel = true;
50654                 this.remove(panel);
50655             }, this);
50656         }
50657         return ti;
50658     },
50659
50660     updatePanelTitle : function(panel, title){
50661         if(this.activePanel == panel){
50662             this.updateTitle(title);
50663         }
50664         if(this.tabs){
50665             var ti = this.tabs.getTab(panel.getEl().id);
50666             ti.setText(title);
50667             if(panel.tabTip !== undefined){
50668                 ti.setTooltip(panel.tabTip);
50669             }
50670         }
50671     },
50672
50673     updateTitle : function(title){
50674         if(this.titleTextEl && !this.config.title){
50675             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
50676         }
50677     },
50678
50679     setActivePanel : function(panel){
50680         panel = this.getPanel(panel);
50681         if(this.activePanel && this.activePanel != panel){
50682             this.activePanel.setActiveState(false);
50683         }
50684         this.activePanel = panel;
50685         panel.setActiveState(true);
50686         if(this.panelSize){
50687             panel.setSize(this.panelSize.width, this.panelSize.height);
50688         }
50689         if(this.closeBtn){
50690             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50691         }
50692         this.updateTitle(panel.getTitle());
50693         if(this.tabs){
50694             this.fireEvent("invalidated", this);
50695         }
50696         this.fireEvent("panelactivated", this, panel);
50697     },
50698
50699     /**
50700      * Shows the specified panel.
50701      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50702      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50703      */
50704     showPanel : function(panel)
50705     {
50706         panel = this.getPanel(panel);
50707         if(panel){
50708             if(this.tabs){
50709                 var tab = this.tabs.getTab(panel.getEl().id);
50710                 if(tab.isHidden()){
50711                     this.tabs.unhideTab(tab.id);
50712                 }
50713                 tab.activate();
50714             }else{
50715                 this.setActivePanel(panel);
50716             }
50717         }
50718         return panel;
50719     },
50720
50721     /**
50722      * Get the active panel for this region.
50723      * @return {Roo.ContentPanel} The active panel or null
50724      */
50725     getActivePanel : function(){
50726         return this.activePanel;
50727     },
50728
50729     validateVisibility : function(){
50730         if(this.panels.getCount() < 1){
50731             this.updateTitle("&#160;");
50732             this.closeBtn.hide();
50733             this.hide();
50734         }else{
50735             if(!this.isVisible()){
50736                 this.show();
50737             }
50738         }
50739     },
50740
50741     /**
50742      * Adds the passed ContentPanel(s) to this region.
50743      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50744      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50745      */
50746     add : function(panel){
50747         if(arguments.length > 1){
50748             for(var i = 0, len = arguments.length; i < len; i++) {
50749                 this.add(arguments[i]);
50750             }
50751             return null;
50752         }
50753         if(this.hasPanel(panel)){
50754             this.showPanel(panel);
50755             return panel;
50756         }
50757         panel.setRegion(this);
50758         this.panels.add(panel);
50759         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50760             this.bodyEl.dom.appendChild(panel.getEl().dom);
50761             if(panel.background !== true){
50762                 this.setActivePanel(panel);
50763             }
50764             this.fireEvent("paneladded", this, panel);
50765             return panel;
50766         }
50767         if(!this.tabs){
50768             this.initTabs();
50769         }else{
50770             this.initPanelAsTab(panel);
50771         }
50772         if(panel.background !== true){
50773             this.tabs.activate(panel.getEl().id);
50774         }
50775         this.fireEvent("paneladded", this, panel);
50776         return panel;
50777     },
50778
50779     /**
50780      * Hides the tab for the specified panel.
50781      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50782      */
50783     hidePanel : function(panel){
50784         if(this.tabs && (panel = this.getPanel(panel))){
50785             this.tabs.hideTab(panel.getEl().id);
50786         }
50787     },
50788
50789     /**
50790      * Unhides the tab for a previously hidden panel.
50791      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50792      */
50793     unhidePanel : function(panel){
50794         if(this.tabs && (panel = this.getPanel(panel))){
50795             this.tabs.unhideTab(panel.getEl().id);
50796         }
50797     },
50798
50799     clearPanels : function(){
50800         while(this.panels.getCount() > 0){
50801              this.remove(this.panels.first());
50802         }
50803     },
50804
50805     /**
50806      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50807      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50808      * @param {Boolean} preservePanel Overrides the config preservePanel option
50809      * @return {Roo.ContentPanel} The panel that was removed
50810      */
50811     remove : function(panel, preservePanel){
50812         panel = this.getPanel(panel);
50813         if(!panel){
50814             return null;
50815         }
50816         var e = {};
50817         this.fireEvent("beforeremove", this, panel, e);
50818         if(e.cancel === true){
50819             return null;
50820         }
50821         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50822         var panelId = panel.getId();
50823         this.panels.removeKey(panelId);
50824         if(preservePanel){
50825             document.body.appendChild(panel.getEl().dom);
50826         }
50827         if(this.tabs){
50828             this.tabs.removeTab(panel.getEl().id);
50829         }else if (!preservePanel){
50830             this.bodyEl.dom.removeChild(panel.getEl().dom);
50831         }
50832         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50833             var p = this.panels.first();
50834             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50835             tempEl.appendChild(p.getEl().dom);
50836             this.bodyEl.update("");
50837             this.bodyEl.dom.appendChild(p.getEl().dom);
50838             tempEl = null;
50839             this.updateTitle(p.getTitle());
50840             this.tabs = null;
50841             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50842             this.setActivePanel(p);
50843         }
50844         panel.setRegion(null);
50845         if(this.activePanel == panel){
50846             this.activePanel = null;
50847         }
50848         if(this.config.autoDestroy !== false && preservePanel !== true){
50849             try{panel.destroy();}catch(e){}
50850         }
50851         this.fireEvent("panelremoved", this, panel);
50852         return panel;
50853     },
50854
50855     /**
50856      * Returns the TabPanel component used by this region
50857      * @return {Roo.TabPanel}
50858      */
50859     getTabs : function(){
50860         return this.tabs;
50861     },
50862
50863     createTool : function(parentEl, className){
50864         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50865             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50866         btn.addClassOnOver("x-layout-tools-button-over");
50867         return btn;
50868     }
50869 });/*
50870  * Based on:
50871  * Ext JS Library 1.1.1
50872  * Copyright(c) 2006-2007, Ext JS, LLC.
50873  *
50874  * Originally Released Under LGPL - original licence link has changed is not relivant.
50875  *
50876  * Fork - LGPL
50877  * <script type="text/javascript">
50878  */
50879  
50880
50881
50882 /**
50883  * @class Roo.SplitLayoutRegion
50884  * @extends Roo.LayoutRegion
50885  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50886  */
50887 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50888     this.cursor = cursor;
50889     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50890 };
50891
50892 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50893     splitTip : "Drag to resize.",
50894     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50895     useSplitTips : false,
50896
50897     applyConfig : function(config){
50898         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50899         if(config.split){
50900             if(!this.split){
50901                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50902                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50903                 /** The SplitBar for this region 
50904                 * @type Roo.SplitBar */
50905                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50906                 this.split.on("moved", this.onSplitMove, this);
50907                 this.split.useShim = config.useShim === true;
50908                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50909                 if(this.useSplitTips){
50910                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50911                 }
50912                 if(config.collapsible){
50913                     this.split.el.on("dblclick", this.collapse,  this);
50914                 }
50915             }
50916             if(typeof config.minSize != "undefined"){
50917                 this.split.minSize = config.minSize;
50918             }
50919             if(typeof config.maxSize != "undefined"){
50920                 this.split.maxSize = config.maxSize;
50921             }
50922             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50923                 this.hideSplitter();
50924             }
50925         }
50926     },
50927
50928     getHMaxSize : function(){
50929          var cmax = this.config.maxSize || 10000;
50930          var center = this.mgr.getRegion("center");
50931          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50932     },
50933
50934     getVMaxSize : function(){
50935          var cmax = this.config.maxSize || 10000;
50936          var center = this.mgr.getRegion("center");
50937          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50938     },
50939
50940     onSplitMove : function(split, newSize){
50941         this.fireEvent("resized", this, newSize);
50942     },
50943     
50944     /** 
50945      * Returns the {@link Roo.SplitBar} for this region.
50946      * @return {Roo.SplitBar}
50947      */
50948     getSplitBar : function(){
50949         return this.split;
50950     },
50951     
50952     hide : function(){
50953         this.hideSplitter();
50954         Roo.SplitLayoutRegion.superclass.hide.call(this);
50955     },
50956
50957     hideSplitter : function(){
50958         if(this.split){
50959             this.split.el.setLocation(-2000,-2000);
50960             this.split.el.hide();
50961         }
50962     },
50963
50964     show : function(){
50965         if(this.split){
50966             this.split.el.show();
50967         }
50968         Roo.SplitLayoutRegion.superclass.show.call(this);
50969     },
50970     
50971     beforeSlide: function(){
50972         if(Roo.isGecko){// firefox overflow auto bug workaround
50973             this.bodyEl.clip();
50974             if(this.tabs) {
50975                 this.tabs.bodyEl.clip();
50976             }
50977             if(this.activePanel){
50978                 this.activePanel.getEl().clip();
50979                 
50980                 if(this.activePanel.beforeSlide){
50981                     this.activePanel.beforeSlide();
50982                 }
50983             }
50984         }
50985     },
50986     
50987     afterSlide : function(){
50988         if(Roo.isGecko){// firefox overflow auto bug workaround
50989             this.bodyEl.unclip();
50990             if(this.tabs) {
50991                 this.tabs.bodyEl.unclip();
50992             }
50993             if(this.activePanel){
50994                 this.activePanel.getEl().unclip();
50995                 if(this.activePanel.afterSlide){
50996                     this.activePanel.afterSlide();
50997                 }
50998             }
50999         }
51000     },
51001
51002     initAutoHide : function(){
51003         if(this.autoHide !== false){
51004             if(!this.autoHideHd){
51005                 var st = new Roo.util.DelayedTask(this.slideIn, this);
51006                 this.autoHideHd = {
51007                     "mouseout": function(e){
51008                         if(!e.within(this.el, true)){
51009                             st.delay(500);
51010                         }
51011                     },
51012                     "mouseover" : function(e){
51013                         st.cancel();
51014                     },
51015                     scope : this
51016                 };
51017             }
51018             this.el.on(this.autoHideHd);
51019         }
51020     },
51021
51022     clearAutoHide : function(){
51023         if(this.autoHide !== false){
51024             this.el.un("mouseout", this.autoHideHd.mouseout);
51025             this.el.un("mouseover", this.autoHideHd.mouseover);
51026         }
51027     },
51028
51029     clearMonitor : function(){
51030         Roo.get(document).un("click", this.slideInIf, this);
51031     },
51032
51033     // these names are backwards but not changed for compat
51034     slideOut : function(){
51035         if(this.isSlid || this.el.hasActiveFx()){
51036             return;
51037         }
51038         this.isSlid = true;
51039         if(this.collapseBtn){
51040             this.collapseBtn.hide();
51041         }
51042         this.closeBtnState = this.closeBtn.getStyle('display');
51043         this.closeBtn.hide();
51044         if(this.stickBtn){
51045             this.stickBtn.show();
51046         }
51047         this.el.show();
51048         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
51049         this.beforeSlide();
51050         this.el.setStyle("z-index", 10001);
51051         this.el.slideIn(this.getSlideAnchor(), {
51052             callback: function(){
51053                 this.afterSlide();
51054                 this.initAutoHide();
51055                 Roo.get(document).on("click", this.slideInIf, this);
51056                 this.fireEvent("slideshow", this);
51057             },
51058             scope: this,
51059             block: true
51060         });
51061     },
51062
51063     afterSlideIn : function(){
51064         this.clearAutoHide();
51065         this.isSlid = false;
51066         this.clearMonitor();
51067         this.el.setStyle("z-index", "");
51068         if(this.collapseBtn){
51069             this.collapseBtn.show();
51070         }
51071         this.closeBtn.setStyle('display', this.closeBtnState);
51072         if(this.stickBtn){
51073             this.stickBtn.hide();
51074         }
51075         this.fireEvent("slidehide", this);
51076     },
51077
51078     slideIn : function(cb){
51079         if(!this.isSlid || this.el.hasActiveFx()){
51080             Roo.callback(cb);
51081             return;
51082         }
51083         this.isSlid = false;
51084         this.beforeSlide();
51085         this.el.slideOut(this.getSlideAnchor(), {
51086             callback: function(){
51087                 this.el.setLeftTop(-10000, -10000);
51088                 this.afterSlide();
51089                 this.afterSlideIn();
51090                 Roo.callback(cb);
51091             },
51092             scope: this,
51093             block: true
51094         });
51095     },
51096     
51097     slideInIf : function(e){
51098         if(!e.within(this.el)){
51099             this.slideIn();
51100         }
51101     },
51102
51103     animateCollapse : function(){
51104         this.beforeSlide();
51105         this.el.setStyle("z-index", 20000);
51106         var anchor = this.getSlideAnchor();
51107         this.el.slideOut(anchor, {
51108             callback : function(){
51109                 this.el.setStyle("z-index", "");
51110                 this.collapsedEl.slideIn(anchor, {duration:.3});
51111                 this.afterSlide();
51112                 this.el.setLocation(-10000,-10000);
51113                 this.el.hide();
51114                 this.fireEvent("collapsed", this);
51115             },
51116             scope: this,
51117             block: true
51118         });
51119     },
51120
51121     animateExpand : function(){
51122         this.beforeSlide();
51123         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
51124         this.el.setStyle("z-index", 20000);
51125         this.collapsedEl.hide({
51126             duration:.1
51127         });
51128         this.el.slideIn(this.getSlideAnchor(), {
51129             callback : function(){
51130                 this.el.setStyle("z-index", "");
51131                 this.afterSlide();
51132                 if(this.split){
51133                     this.split.el.show();
51134                 }
51135                 this.fireEvent("invalidated", this);
51136                 this.fireEvent("expanded", this);
51137             },
51138             scope: this,
51139             block: true
51140         });
51141     },
51142
51143     anchors : {
51144         "west" : "left",
51145         "east" : "right",
51146         "north" : "top",
51147         "south" : "bottom"
51148     },
51149
51150     sanchors : {
51151         "west" : "l",
51152         "east" : "r",
51153         "north" : "t",
51154         "south" : "b"
51155     },
51156
51157     canchors : {
51158         "west" : "tl-tr",
51159         "east" : "tr-tl",
51160         "north" : "tl-bl",
51161         "south" : "bl-tl"
51162     },
51163
51164     getAnchor : function(){
51165         return this.anchors[this.position];
51166     },
51167
51168     getCollapseAnchor : function(){
51169         return this.canchors[this.position];
51170     },
51171
51172     getSlideAnchor : function(){
51173         return this.sanchors[this.position];
51174     },
51175
51176     getAlignAdj : function(){
51177         var cm = this.cmargins;
51178         switch(this.position){
51179             case "west":
51180                 return [0, 0];
51181             break;
51182             case "east":
51183                 return [0, 0];
51184             break;
51185             case "north":
51186                 return [0, 0];
51187             break;
51188             case "south":
51189                 return [0, 0];
51190             break;
51191         }
51192     },
51193
51194     getExpandAdj : function(){
51195         var c = this.collapsedEl, cm = this.cmargins;
51196         switch(this.position){
51197             case "west":
51198                 return [-(cm.right+c.getWidth()+cm.left), 0];
51199             break;
51200             case "east":
51201                 return [cm.right+c.getWidth()+cm.left, 0];
51202             break;
51203             case "north":
51204                 return [0, -(cm.top+cm.bottom+c.getHeight())];
51205             break;
51206             case "south":
51207                 return [0, cm.top+cm.bottom+c.getHeight()];
51208             break;
51209         }
51210     }
51211 });/*
51212  * Based on:
51213  * Ext JS Library 1.1.1
51214  * Copyright(c) 2006-2007, Ext JS, LLC.
51215  *
51216  * Originally Released Under LGPL - original licence link has changed is not relivant.
51217  *
51218  * Fork - LGPL
51219  * <script type="text/javascript">
51220  */
51221 /*
51222  * These classes are private internal classes
51223  */
51224 Roo.CenterLayoutRegion = function(mgr, config){
51225     Roo.LayoutRegion.call(this, mgr, config, "center");
51226     this.visible = true;
51227     this.minWidth = config.minWidth || 20;
51228     this.minHeight = config.minHeight || 20;
51229 };
51230
51231 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
51232     hide : function(){
51233         // center panel can't be hidden
51234     },
51235     
51236     show : function(){
51237         // center panel can't be hidden
51238     },
51239     
51240     getMinWidth: function(){
51241         return this.minWidth;
51242     },
51243     
51244     getMinHeight: function(){
51245         return this.minHeight;
51246     }
51247 });
51248
51249
51250 Roo.NorthLayoutRegion = function(mgr, config){
51251     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
51252     if(this.split){
51253         this.split.placement = Roo.SplitBar.TOP;
51254         this.split.orientation = Roo.SplitBar.VERTICAL;
51255         this.split.el.addClass("x-layout-split-v");
51256     }
51257     var size = config.initialSize || config.height;
51258     if(typeof size != "undefined"){
51259         this.el.setHeight(size);
51260     }
51261 };
51262 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
51263     orientation: Roo.SplitBar.VERTICAL,
51264     getBox : function(){
51265         if(this.collapsed){
51266             return this.collapsedEl.getBox();
51267         }
51268         var box = this.el.getBox();
51269         if(this.split){
51270             box.height += this.split.el.getHeight();
51271         }
51272         return box;
51273     },
51274     
51275     updateBox : function(box){
51276         if(this.split && !this.collapsed){
51277             box.height -= this.split.el.getHeight();
51278             this.split.el.setLeft(box.x);
51279             this.split.el.setTop(box.y+box.height);
51280             this.split.el.setWidth(box.width);
51281         }
51282         if(this.collapsed){
51283             this.updateBody(box.width, null);
51284         }
51285         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51286     }
51287 });
51288
51289 Roo.SouthLayoutRegion = function(mgr, config){
51290     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
51291     if(this.split){
51292         this.split.placement = Roo.SplitBar.BOTTOM;
51293         this.split.orientation = Roo.SplitBar.VERTICAL;
51294         this.split.el.addClass("x-layout-split-v");
51295     }
51296     var size = config.initialSize || config.height;
51297     if(typeof size != "undefined"){
51298         this.el.setHeight(size);
51299     }
51300 };
51301 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
51302     orientation: Roo.SplitBar.VERTICAL,
51303     getBox : function(){
51304         if(this.collapsed){
51305             return this.collapsedEl.getBox();
51306         }
51307         var box = this.el.getBox();
51308         if(this.split){
51309             var sh = this.split.el.getHeight();
51310             box.height += sh;
51311             box.y -= sh;
51312         }
51313         return box;
51314     },
51315     
51316     updateBox : function(box){
51317         if(this.split && !this.collapsed){
51318             var sh = this.split.el.getHeight();
51319             box.height -= sh;
51320             box.y += sh;
51321             this.split.el.setLeft(box.x);
51322             this.split.el.setTop(box.y-sh);
51323             this.split.el.setWidth(box.width);
51324         }
51325         if(this.collapsed){
51326             this.updateBody(box.width, null);
51327         }
51328         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51329     }
51330 });
51331
51332 Roo.EastLayoutRegion = function(mgr, config){
51333     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
51334     if(this.split){
51335         this.split.placement = Roo.SplitBar.RIGHT;
51336         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51337         this.split.el.addClass("x-layout-split-h");
51338     }
51339     var size = config.initialSize || config.width;
51340     if(typeof size != "undefined"){
51341         this.el.setWidth(size);
51342     }
51343 };
51344 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
51345     orientation: Roo.SplitBar.HORIZONTAL,
51346     getBox : function(){
51347         if(this.collapsed){
51348             return this.collapsedEl.getBox();
51349         }
51350         var box = this.el.getBox();
51351         if(this.split){
51352             var sw = this.split.el.getWidth();
51353             box.width += sw;
51354             box.x -= sw;
51355         }
51356         return box;
51357     },
51358
51359     updateBox : function(box){
51360         if(this.split && !this.collapsed){
51361             var sw = this.split.el.getWidth();
51362             box.width -= sw;
51363             this.split.el.setLeft(box.x);
51364             this.split.el.setTop(box.y);
51365             this.split.el.setHeight(box.height);
51366             box.x += sw;
51367         }
51368         if(this.collapsed){
51369             this.updateBody(null, box.height);
51370         }
51371         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51372     }
51373 });
51374
51375 Roo.WestLayoutRegion = function(mgr, config){
51376     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
51377     if(this.split){
51378         this.split.placement = Roo.SplitBar.LEFT;
51379         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51380         this.split.el.addClass("x-layout-split-h");
51381     }
51382     var size = config.initialSize || config.width;
51383     if(typeof size != "undefined"){
51384         this.el.setWidth(size);
51385     }
51386 };
51387 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
51388     orientation: Roo.SplitBar.HORIZONTAL,
51389     getBox : function(){
51390         if(this.collapsed){
51391             return this.collapsedEl.getBox();
51392         }
51393         var box = this.el.getBox();
51394         if(this.split){
51395             box.width += this.split.el.getWidth();
51396         }
51397         return box;
51398     },
51399     
51400     updateBox : function(box){
51401         if(this.split && !this.collapsed){
51402             var sw = this.split.el.getWidth();
51403             box.width -= sw;
51404             this.split.el.setLeft(box.x+box.width);
51405             this.split.el.setTop(box.y);
51406             this.split.el.setHeight(box.height);
51407         }
51408         if(this.collapsed){
51409             this.updateBody(null, box.height);
51410         }
51411         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51412     }
51413 });
51414 /*
51415  * Based on:
51416  * Ext JS Library 1.1.1
51417  * Copyright(c) 2006-2007, Ext JS, LLC.
51418  *
51419  * Originally Released Under LGPL - original licence link has changed is not relivant.
51420  *
51421  * Fork - LGPL
51422  * <script type="text/javascript">
51423  */
51424  
51425  
51426 /*
51427  * Private internal class for reading and applying state
51428  */
51429 Roo.LayoutStateManager = function(layout){
51430      // default empty state
51431      this.state = {
51432         north: {},
51433         south: {},
51434         east: {},
51435         west: {}       
51436     };
51437 };
51438
51439 Roo.LayoutStateManager.prototype = {
51440     init : function(layout, provider){
51441         this.provider = provider;
51442         var state = provider.get(layout.id+"-layout-state");
51443         if(state){
51444             var wasUpdating = layout.isUpdating();
51445             if(!wasUpdating){
51446                 layout.beginUpdate();
51447             }
51448             for(var key in state){
51449                 if(typeof state[key] != "function"){
51450                     var rstate = state[key];
51451                     var r = layout.getRegion(key);
51452                     if(r && rstate){
51453                         if(rstate.size){
51454                             r.resizeTo(rstate.size);
51455                         }
51456                         if(rstate.collapsed == true){
51457                             r.collapse(true);
51458                         }else{
51459                             r.expand(null, true);
51460                         }
51461                     }
51462                 }
51463             }
51464             if(!wasUpdating){
51465                 layout.endUpdate();
51466             }
51467             this.state = state; 
51468         }
51469         this.layout = layout;
51470         layout.on("regionresized", this.onRegionResized, this);
51471         layout.on("regioncollapsed", this.onRegionCollapsed, this);
51472         layout.on("regionexpanded", this.onRegionExpanded, this);
51473     },
51474     
51475     storeState : function(){
51476         this.provider.set(this.layout.id+"-layout-state", this.state);
51477     },
51478     
51479     onRegionResized : function(region, newSize){
51480         this.state[region.getPosition()].size = newSize;
51481         this.storeState();
51482     },
51483     
51484     onRegionCollapsed : function(region){
51485         this.state[region.getPosition()].collapsed = true;
51486         this.storeState();
51487     },
51488     
51489     onRegionExpanded : function(region){
51490         this.state[region.getPosition()].collapsed = false;
51491         this.storeState();
51492     }
51493 };/*
51494  * Based on:
51495  * Ext JS Library 1.1.1
51496  * Copyright(c) 2006-2007, Ext JS, LLC.
51497  *
51498  * Originally Released Under LGPL - original licence link has changed is not relivant.
51499  *
51500  * Fork - LGPL
51501  * <script type="text/javascript">
51502  */
51503 /**
51504  * @class Roo.ContentPanel
51505  * @extends Roo.util.Observable
51506  * A basic ContentPanel element.
51507  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
51508  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
51509  * @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
51510  * @cfg {Boolean}   closable      True if the panel can be closed/removed
51511  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
51512  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
51513  * @cfg {Toolbar}   toolbar       A toolbar for this panel
51514  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
51515  * @cfg {String} title          The title for this panel
51516  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
51517  * @cfg {String} url            Calls {@link #setUrl} with this value
51518  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
51519  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
51520  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
51521  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
51522
51523  * @constructor
51524  * Create a new ContentPanel.
51525  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
51526  * @param {String/Object} config A string to set only the title or a config object
51527  * @param {String} content (optional) Set the HTML content for this panel
51528  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
51529  */
51530 Roo.ContentPanel = function(el, config, content){
51531     
51532      
51533     /*
51534     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
51535         config = el;
51536         el = Roo.id();
51537     }
51538     if (config && config.parentLayout) { 
51539         el = config.parentLayout.el.createChild(); 
51540     }
51541     */
51542     if(el.autoCreate){ // xtype is available if this is called from factory
51543         config = el;
51544         el = Roo.id();
51545     }
51546     this.el = Roo.get(el);
51547     if(!this.el && config && config.autoCreate){
51548         if(typeof config.autoCreate == "object"){
51549             if(!config.autoCreate.id){
51550                 config.autoCreate.id = config.id||el;
51551             }
51552             this.el = Roo.DomHelper.append(document.body,
51553                         config.autoCreate, true);
51554         }else{
51555             this.el = Roo.DomHelper.append(document.body,
51556                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
51557         }
51558     }
51559     this.closable = false;
51560     this.loaded = false;
51561     this.active = false;
51562     if(typeof config == "string"){
51563         this.title = config;
51564     }else{
51565         Roo.apply(this, config);
51566     }
51567     
51568     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
51569         this.wrapEl = this.el.wrap();
51570         this.toolbar.container = this.el.insertSibling(false, 'before');
51571         this.toolbar = new Roo.Toolbar(this.toolbar);
51572     }
51573     
51574     // xtype created footer. - not sure if will work as we normally have to render first..
51575     if (this.footer && !this.footer.el && this.footer.xtype) {
51576         if (!this.wrapEl) {
51577             this.wrapEl = this.el.wrap();
51578         }
51579     
51580         this.footer.container = this.wrapEl.createChild();
51581          
51582         this.footer = Roo.factory(this.footer, Roo);
51583         
51584     }
51585     
51586     if(this.resizeEl){
51587         this.resizeEl = Roo.get(this.resizeEl, true);
51588     }else{
51589         this.resizeEl = this.el;
51590     }
51591     // handle view.xtype
51592     
51593  
51594     
51595     
51596     this.addEvents({
51597         /**
51598          * @event activate
51599          * Fires when this panel is activated. 
51600          * @param {Roo.ContentPanel} this
51601          */
51602         "activate" : true,
51603         /**
51604          * @event deactivate
51605          * Fires when this panel is activated. 
51606          * @param {Roo.ContentPanel} this
51607          */
51608         "deactivate" : true,
51609
51610         /**
51611          * @event resize
51612          * Fires when this panel is resized if fitToFrame is true.
51613          * @param {Roo.ContentPanel} this
51614          * @param {Number} width The width after any component adjustments
51615          * @param {Number} height The height after any component adjustments
51616          */
51617         "resize" : true,
51618         
51619          /**
51620          * @event render
51621          * Fires when this tab is created
51622          * @param {Roo.ContentPanel} this
51623          */
51624         "render" : true
51625         
51626         
51627         
51628     });
51629     
51630
51631     
51632     
51633     if(this.autoScroll){
51634         this.resizeEl.setStyle("overflow", "auto");
51635     } else {
51636         // fix randome scrolling
51637         this.el.on('scroll', function() {
51638             Roo.log('fix random scolling');
51639             this.scrollTo('top',0); 
51640         });
51641     }
51642     content = content || this.content;
51643     if(content){
51644         this.setContent(content);
51645     }
51646     if(config && config.url){
51647         this.setUrl(this.url, this.params, this.loadOnce);
51648     }
51649     
51650     
51651     
51652     Roo.ContentPanel.superclass.constructor.call(this);
51653     
51654     if (this.view && typeof(this.view.xtype) != 'undefined') {
51655         this.view.el = this.el.appendChild(document.createElement("div"));
51656         this.view = Roo.factory(this.view); 
51657         this.view.render  &&  this.view.render(false, '');  
51658     }
51659     
51660     
51661     this.fireEvent('render', this);
51662 };
51663
51664 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
51665     tabTip:'',
51666     setRegion : function(region){
51667         this.region = region;
51668         if(region){
51669            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
51670         }else{
51671            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
51672         } 
51673     },
51674     
51675     /**
51676      * Returns the toolbar for this Panel if one was configured. 
51677      * @return {Roo.Toolbar} 
51678      */
51679     getToolbar : function(){
51680         return this.toolbar;
51681     },
51682     
51683     setActiveState : function(active){
51684         this.active = active;
51685         if(!active){
51686             this.fireEvent("deactivate", this);
51687         }else{
51688             this.fireEvent("activate", this);
51689         }
51690     },
51691     /**
51692      * Updates this panel's element
51693      * @param {String} content The new content
51694      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51695     */
51696     setContent : function(content, loadScripts){
51697         this.el.update(content, loadScripts);
51698     },
51699
51700     ignoreResize : function(w, h){
51701         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51702             return true;
51703         }else{
51704             this.lastSize = {width: w, height: h};
51705             return false;
51706         }
51707     },
51708     /**
51709      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51710      * @return {Roo.UpdateManager} The UpdateManager
51711      */
51712     getUpdateManager : function(){
51713         return this.el.getUpdateManager();
51714     },
51715      /**
51716      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51717      * @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:
51718 <pre><code>
51719 panel.load({
51720     url: "your-url.php",
51721     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51722     callback: yourFunction,
51723     scope: yourObject, //(optional scope)
51724     discardUrl: false,
51725     nocache: false,
51726     text: "Loading...",
51727     timeout: 30,
51728     scripts: false
51729 });
51730 </code></pre>
51731      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51732      * 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.
51733      * @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}
51734      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51735      * @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.
51736      * @return {Roo.ContentPanel} this
51737      */
51738     load : function(){
51739         var um = this.el.getUpdateManager();
51740         um.update.apply(um, arguments);
51741         return this;
51742     },
51743
51744
51745     /**
51746      * 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.
51747      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51748      * @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)
51749      * @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)
51750      * @return {Roo.UpdateManager} The UpdateManager
51751      */
51752     setUrl : function(url, params, loadOnce){
51753         if(this.refreshDelegate){
51754             this.removeListener("activate", this.refreshDelegate);
51755         }
51756         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51757         this.on("activate", this.refreshDelegate);
51758         return this.el.getUpdateManager();
51759     },
51760     
51761     _handleRefresh : function(url, params, loadOnce){
51762         if(!loadOnce || !this.loaded){
51763             var updater = this.el.getUpdateManager();
51764             updater.update(url, params, this._setLoaded.createDelegate(this));
51765         }
51766     },
51767     
51768     _setLoaded : function(){
51769         this.loaded = true;
51770     }, 
51771     
51772     /**
51773      * Returns this panel's id
51774      * @return {String} 
51775      */
51776     getId : function(){
51777         return this.el.id;
51778     },
51779     
51780     /** 
51781      * Returns this panel's element - used by regiosn to add.
51782      * @return {Roo.Element} 
51783      */
51784     getEl : function(){
51785         return this.wrapEl || this.el;
51786     },
51787     
51788     adjustForComponents : function(width, height)
51789     {
51790         //Roo.log('adjustForComponents ');
51791         if(this.resizeEl != this.el){
51792             width -= this.el.getFrameWidth('lr');
51793             height -= this.el.getFrameWidth('tb');
51794         }
51795         if(this.toolbar){
51796             var te = this.toolbar.getEl();
51797             height -= te.getHeight();
51798             te.setWidth(width);
51799         }
51800         if(this.footer){
51801             var te = this.footer.getEl();
51802             Roo.log("footer:" + te.getHeight());
51803             
51804             height -= te.getHeight();
51805             te.setWidth(width);
51806         }
51807         
51808         
51809         if(this.adjustments){
51810             width += this.adjustments[0];
51811             height += this.adjustments[1];
51812         }
51813         return {"width": width, "height": height};
51814     },
51815     
51816     setSize : function(width, height){
51817         if(this.fitToFrame && !this.ignoreResize(width, height)){
51818             if(this.fitContainer && this.resizeEl != this.el){
51819                 this.el.setSize(width, height);
51820             }
51821             var size = this.adjustForComponents(width, height);
51822             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51823             this.fireEvent('resize', this, size.width, size.height);
51824         }
51825     },
51826     
51827     /**
51828      * Returns this panel's title
51829      * @return {String} 
51830      */
51831     getTitle : function(){
51832         return this.title;
51833     },
51834     
51835     /**
51836      * Set this panel's title
51837      * @param {String} title
51838      */
51839     setTitle : function(title){
51840         this.title = title;
51841         if(this.region){
51842             this.region.updatePanelTitle(this, title);
51843         }
51844     },
51845     
51846     /**
51847      * Returns true is this panel was configured to be closable
51848      * @return {Boolean} 
51849      */
51850     isClosable : function(){
51851         return this.closable;
51852     },
51853     
51854     beforeSlide : function(){
51855         this.el.clip();
51856         this.resizeEl.clip();
51857     },
51858     
51859     afterSlide : function(){
51860         this.el.unclip();
51861         this.resizeEl.unclip();
51862     },
51863     
51864     /**
51865      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51866      *   Will fail silently if the {@link #setUrl} method has not been called.
51867      *   This does not activate the panel, just updates its content.
51868      */
51869     refresh : function(){
51870         if(this.refreshDelegate){
51871            this.loaded = false;
51872            this.refreshDelegate();
51873         }
51874     },
51875     
51876     /**
51877      * Destroys this panel
51878      */
51879     destroy : function(){
51880         this.el.removeAllListeners();
51881         var tempEl = document.createElement("span");
51882         tempEl.appendChild(this.el.dom);
51883         tempEl.innerHTML = "";
51884         this.el.remove();
51885         this.el = null;
51886     },
51887     
51888     /**
51889      * form - if the content panel contains a form - this is a reference to it.
51890      * @type {Roo.form.Form}
51891      */
51892     form : false,
51893     /**
51894      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51895      *    This contains a reference to it.
51896      * @type {Roo.View}
51897      */
51898     view : false,
51899     
51900       /**
51901      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51902      * <pre><code>
51903
51904 layout.addxtype({
51905        xtype : 'Form',
51906        items: [ .... ]
51907    }
51908 );
51909
51910 </code></pre>
51911      * @param {Object} cfg Xtype definition of item to add.
51912      */
51913     
51914     addxtype : function(cfg) {
51915         // add form..
51916         if (cfg.xtype.match(/^Form$/)) {
51917             
51918             var el;
51919             //if (this.footer) {
51920             //    el = this.footer.container.insertSibling(false, 'before');
51921             //} else {
51922                 el = this.el.createChild();
51923             //}
51924
51925             this.form = new  Roo.form.Form(cfg);
51926             
51927             
51928             if ( this.form.allItems.length) {
51929                 this.form.render(el.dom);
51930             }
51931             return this.form;
51932         }
51933         // should only have one of theses..
51934         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51935             // views.. should not be just added - used named prop 'view''
51936             
51937             cfg.el = this.el.appendChild(document.createElement("div"));
51938             // factory?
51939             
51940             var ret = new Roo.factory(cfg);
51941              
51942              ret.render && ret.render(false, ''); // render blank..
51943             this.view = ret;
51944             return ret;
51945         }
51946         return false;
51947     }
51948 });
51949
51950 /**
51951  * @class Roo.GridPanel
51952  * @extends Roo.ContentPanel
51953  * @constructor
51954  * Create a new GridPanel.
51955  * @param {Roo.grid.Grid} grid The grid for this panel
51956  * @param {String/Object} config A string to set only the panel's title, or a config object
51957  */
51958 Roo.GridPanel = function(grid, config){
51959     
51960   
51961     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51962         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51963         
51964     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51965     
51966     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51967     
51968     if(this.toolbar){
51969         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51970     }
51971     // xtype created footer. - not sure if will work as we normally have to render first..
51972     if (this.footer && !this.footer.el && this.footer.xtype) {
51973         
51974         this.footer.container = this.grid.getView().getFooterPanel(true);
51975         this.footer.dataSource = this.grid.dataSource;
51976         this.footer = Roo.factory(this.footer, Roo);
51977         
51978     }
51979     
51980     grid.monitorWindowResize = false; // turn off autosizing
51981     grid.autoHeight = false;
51982     grid.autoWidth = false;
51983     this.grid = grid;
51984     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
51985 };
51986
51987 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
51988     getId : function(){
51989         return this.grid.id;
51990     },
51991     
51992     /**
51993      * Returns the grid for this panel
51994      * @return {Roo.grid.Grid} 
51995      */
51996     getGrid : function(){
51997         return this.grid;    
51998     },
51999     
52000     setSize : function(width, height){
52001         if(!this.ignoreResize(width, height)){
52002             var grid = this.grid;
52003             var size = this.adjustForComponents(width, height);
52004             grid.getGridEl().setSize(size.width, size.height);
52005             grid.autoSize();
52006         }
52007     },
52008     
52009     beforeSlide : function(){
52010         this.grid.getView().scroller.clip();
52011     },
52012     
52013     afterSlide : function(){
52014         this.grid.getView().scroller.unclip();
52015     },
52016     
52017     destroy : function(){
52018         this.grid.destroy();
52019         delete this.grid;
52020         Roo.GridPanel.superclass.destroy.call(this); 
52021     }
52022 });
52023
52024
52025 /**
52026  * @class Roo.NestedLayoutPanel
52027  * @extends Roo.ContentPanel
52028  * @constructor
52029  * Create a new NestedLayoutPanel.
52030  * 
52031  * 
52032  * @param {Roo.BorderLayout} layout The layout for this panel
52033  * @param {String/Object} config A string to set only the title or a config object
52034  */
52035 Roo.NestedLayoutPanel = function(layout, config)
52036 {
52037     // construct with only one argument..
52038     /* FIXME - implement nicer consturctors
52039     if (layout.layout) {
52040         config = layout;
52041         layout = config.layout;
52042         delete config.layout;
52043     }
52044     if (layout.xtype && !layout.getEl) {
52045         // then layout needs constructing..
52046         layout = Roo.factory(layout, Roo);
52047     }
52048     */
52049     
52050     
52051     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
52052     
52053     layout.monitorWindowResize = false; // turn off autosizing
52054     this.layout = layout;
52055     this.layout.getEl().addClass("x-layout-nested-layout");
52056     
52057     
52058     
52059     
52060 };
52061
52062 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
52063
52064     setSize : function(width, height){
52065         if(!this.ignoreResize(width, height)){
52066             var size = this.adjustForComponents(width, height);
52067             var el = this.layout.getEl();
52068             el.setSize(size.width, size.height);
52069             var touch = el.dom.offsetWidth;
52070             this.layout.layout();
52071             // ie requires a double layout on the first pass
52072             if(Roo.isIE && !this.initialized){
52073                 this.initialized = true;
52074                 this.layout.layout();
52075             }
52076         }
52077     },
52078     
52079     // activate all subpanels if not currently active..
52080     
52081     setActiveState : function(active){
52082         this.active = active;
52083         if(!active){
52084             this.fireEvent("deactivate", this);
52085             return;
52086         }
52087         
52088         this.fireEvent("activate", this);
52089         // not sure if this should happen before or after..
52090         if (!this.layout) {
52091             return; // should not happen..
52092         }
52093         var reg = false;
52094         for (var r in this.layout.regions) {
52095             reg = this.layout.getRegion(r);
52096             if (reg.getActivePanel()) {
52097                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
52098                 reg.setActivePanel(reg.getActivePanel());
52099                 continue;
52100             }
52101             if (!reg.panels.length) {
52102                 continue;
52103             }
52104             reg.showPanel(reg.getPanel(0));
52105         }
52106         
52107         
52108         
52109         
52110     },
52111     
52112     /**
52113      * Returns the nested BorderLayout for this panel
52114      * @return {Roo.BorderLayout} 
52115      */
52116     getLayout : function(){
52117         return this.layout;
52118     },
52119     
52120      /**
52121      * Adds a xtype elements to the layout of the nested panel
52122      * <pre><code>
52123
52124 panel.addxtype({
52125        xtype : 'ContentPanel',
52126        region: 'west',
52127        items: [ .... ]
52128    }
52129 );
52130
52131 panel.addxtype({
52132         xtype : 'NestedLayoutPanel',
52133         region: 'west',
52134         layout: {
52135            center: { },
52136            west: { }   
52137         },
52138         items : [ ... list of content panels or nested layout panels.. ]
52139    }
52140 );
52141 </code></pre>
52142      * @param {Object} cfg Xtype definition of item to add.
52143      */
52144     addxtype : function(cfg) {
52145         return this.layout.addxtype(cfg);
52146     
52147     }
52148 });
52149
52150 Roo.ScrollPanel = function(el, config, content){
52151     config = config || {};
52152     config.fitToFrame = true;
52153     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
52154     
52155     this.el.dom.style.overflow = "hidden";
52156     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
52157     this.el.removeClass("x-layout-inactive-content");
52158     this.el.on("mousewheel", this.onWheel, this);
52159
52160     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
52161     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
52162     up.unselectable(); down.unselectable();
52163     up.on("click", this.scrollUp, this);
52164     down.on("click", this.scrollDown, this);
52165     up.addClassOnOver("x-scroller-btn-over");
52166     down.addClassOnOver("x-scroller-btn-over");
52167     up.addClassOnClick("x-scroller-btn-click");
52168     down.addClassOnClick("x-scroller-btn-click");
52169     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
52170
52171     this.resizeEl = this.el;
52172     this.el = wrap; this.up = up; this.down = down;
52173 };
52174
52175 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
52176     increment : 100,
52177     wheelIncrement : 5,
52178     scrollUp : function(){
52179         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
52180     },
52181
52182     scrollDown : function(){
52183         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
52184     },
52185
52186     afterScroll : function(){
52187         var el = this.resizeEl;
52188         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
52189         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52190         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52191     },
52192
52193     setSize : function(){
52194         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
52195         this.afterScroll();
52196     },
52197
52198     onWheel : function(e){
52199         var d = e.getWheelDelta();
52200         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
52201         this.afterScroll();
52202         e.stopEvent();
52203     },
52204
52205     setContent : function(content, loadScripts){
52206         this.resizeEl.update(content, loadScripts);
52207     }
52208
52209 });
52210
52211
52212
52213
52214
52215
52216
52217
52218
52219 /**
52220  * @class Roo.TreePanel
52221  * @extends Roo.ContentPanel
52222  * @constructor
52223  * Create a new TreePanel. - defaults to fit/scoll contents.
52224  * @param {String/Object} config A string to set only the panel's title, or a config object
52225  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
52226  */
52227 Roo.TreePanel = function(config){
52228     var el = config.el;
52229     var tree = config.tree;
52230     delete config.tree; 
52231     delete config.el; // hopefull!
52232     
52233     // wrapper for IE7 strict & safari scroll issue
52234     
52235     var treeEl = el.createChild();
52236     config.resizeEl = treeEl;
52237     
52238     
52239     
52240     Roo.TreePanel.superclass.constructor.call(this, el, config);
52241  
52242  
52243     this.tree = new Roo.tree.TreePanel(treeEl , tree);
52244     //console.log(tree);
52245     this.on('activate', function()
52246     {
52247         if (this.tree.rendered) {
52248             return;
52249         }
52250         //console.log('render tree');
52251         this.tree.render();
52252     });
52253     // this should not be needed.. - it's actually the 'el' that resizes?
52254     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
52255     
52256     //this.on('resize',  function (cp, w, h) {
52257     //        this.tree.innerCt.setWidth(w);
52258     //        this.tree.innerCt.setHeight(h);
52259     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
52260     //});
52261
52262         
52263     
52264 };
52265
52266 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
52267     fitToFrame : true,
52268     autoScroll : true
52269 });
52270
52271
52272
52273
52274
52275
52276
52277
52278
52279
52280
52281 /*
52282  * Based on:
52283  * Ext JS Library 1.1.1
52284  * Copyright(c) 2006-2007, Ext JS, LLC.
52285  *
52286  * Originally Released Under LGPL - original licence link has changed is not relivant.
52287  *
52288  * Fork - LGPL
52289  * <script type="text/javascript">
52290  */
52291  
52292
52293 /**
52294  * @class Roo.ReaderLayout
52295  * @extends Roo.BorderLayout
52296  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
52297  * center region containing two nested regions (a top one for a list view and one for item preview below),
52298  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
52299  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
52300  * expedites the setup of the overall layout and regions for this common application style.
52301  * Example:
52302  <pre><code>
52303 var reader = new Roo.ReaderLayout();
52304 var CP = Roo.ContentPanel;  // shortcut for adding
52305
52306 reader.beginUpdate();
52307 reader.add("north", new CP("north", "North"));
52308 reader.add("west", new CP("west", {title: "West"}));
52309 reader.add("east", new CP("east", {title: "East"}));
52310
52311 reader.regions.listView.add(new CP("listView", "List"));
52312 reader.regions.preview.add(new CP("preview", "Preview"));
52313 reader.endUpdate();
52314 </code></pre>
52315 * @constructor
52316 * Create a new ReaderLayout
52317 * @param {Object} config Configuration options
52318 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
52319 * document.body if omitted)
52320 */
52321 Roo.ReaderLayout = function(config, renderTo){
52322     var c = config || {size:{}};
52323     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
52324         north: c.north !== false ? Roo.apply({
52325             split:false,
52326             initialSize: 32,
52327             titlebar: false
52328         }, c.north) : false,
52329         west: c.west !== false ? Roo.apply({
52330             split:true,
52331             initialSize: 200,
52332             minSize: 175,
52333             maxSize: 400,
52334             titlebar: true,
52335             collapsible: true,
52336             animate: true,
52337             margins:{left:5,right:0,bottom:5,top:5},
52338             cmargins:{left:5,right:5,bottom:5,top:5}
52339         }, c.west) : false,
52340         east: c.east !== false ? Roo.apply({
52341             split:true,
52342             initialSize: 200,
52343             minSize: 175,
52344             maxSize: 400,
52345             titlebar: true,
52346             collapsible: true,
52347             animate: true,
52348             margins:{left:0,right:5,bottom:5,top:5},
52349             cmargins:{left:5,right:5,bottom:5,top:5}
52350         }, c.east) : false,
52351         center: Roo.apply({
52352             tabPosition: 'top',
52353             autoScroll:false,
52354             closeOnTab: true,
52355             titlebar:false,
52356             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
52357         }, c.center)
52358     });
52359
52360     this.el.addClass('x-reader');
52361
52362     this.beginUpdate();
52363
52364     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
52365         south: c.preview !== false ? Roo.apply({
52366             split:true,
52367             initialSize: 200,
52368             minSize: 100,
52369             autoScroll:true,
52370             collapsible:true,
52371             titlebar: true,
52372             cmargins:{top:5,left:0, right:0, bottom:0}
52373         }, c.preview) : false,
52374         center: Roo.apply({
52375             autoScroll:false,
52376             titlebar:false,
52377             minHeight:200
52378         }, c.listView)
52379     });
52380     this.add('center', new Roo.NestedLayoutPanel(inner,
52381             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
52382
52383     this.endUpdate();
52384
52385     this.regions.preview = inner.getRegion('south');
52386     this.regions.listView = inner.getRegion('center');
52387 };
52388
52389 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
52390  * Based on:
52391  * Ext JS Library 1.1.1
52392  * Copyright(c) 2006-2007, Ext JS, LLC.
52393  *
52394  * Originally Released Under LGPL - original licence link has changed is not relivant.
52395  *
52396  * Fork - LGPL
52397  * <script type="text/javascript">
52398  */
52399  
52400 /**
52401  * @class Roo.grid.Grid
52402  * @extends Roo.util.Observable
52403  * This class represents the primary interface of a component based grid control.
52404  * <br><br>Usage:<pre><code>
52405  var grid = new Roo.grid.Grid("my-container-id", {
52406      ds: myDataStore,
52407      cm: myColModel,
52408      selModel: mySelectionModel,
52409      autoSizeColumns: true,
52410      monitorWindowResize: false,
52411      trackMouseOver: true
52412  });
52413  // set any options
52414  grid.render();
52415  * </code></pre>
52416  * <b>Common Problems:</b><br/>
52417  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
52418  * element will correct this<br/>
52419  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
52420  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
52421  * are unpredictable.<br/>
52422  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
52423  * grid to calculate dimensions/offsets.<br/>
52424   * @constructor
52425  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
52426  * The container MUST have some type of size defined for the grid to fill. The container will be
52427  * automatically set to position relative if it isn't already.
52428  * @param {Object} config A config object that sets properties on this grid.
52429  */
52430 Roo.grid.Grid = function(container, config){
52431         // initialize the container
52432         this.container = Roo.get(container);
52433         this.container.update("");
52434         this.container.setStyle("overflow", "hidden");
52435     this.container.addClass('x-grid-container');
52436
52437     this.id = this.container.id;
52438
52439     Roo.apply(this, config);
52440     // check and correct shorthanded configs
52441     if(this.ds){
52442         this.dataSource = this.ds;
52443         delete this.ds;
52444     }
52445     if(this.cm){
52446         this.colModel = this.cm;
52447         delete this.cm;
52448     }
52449     if(this.sm){
52450         this.selModel = this.sm;
52451         delete this.sm;
52452     }
52453
52454     if (this.selModel) {
52455         this.selModel = Roo.factory(this.selModel, Roo.grid);
52456         this.sm = this.selModel;
52457         this.sm.xmodule = this.xmodule || false;
52458     }
52459     if (typeof(this.colModel.config) == 'undefined') {
52460         this.colModel = new Roo.grid.ColumnModel(this.colModel);
52461         this.cm = this.colModel;
52462         this.cm.xmodule = this.xmodule || false;
52463     }
52464     if (this.dataSource) {
52465         this.dataSource= Roo.factory(this.dataSource, Roo.data);
52466         this.ds = this.dataSource;
52467         this.ds.xmodule = this.xmodule || false;
52468          
52469     }
52470     
52471     
52472     
52473     if(this.width){
52474         this.container.setWidth(this.width);
52475     }
52476
52477     if(this.height){
52478         this.container.setHeight(this.height);
52479     }
52480     /** @private */
52481         this.addEvents({
52482         // raw events
52483         /**
52484          * @event click
52485          * The raw click event for the entire grid.
52486          * @param {Roo.EventObject} e
52487          */
52488         "click" : true,
52489         /**
52490          * @event dblclick
52491          * The raw dblclick event for the entire grid.
52492          * @param {Roo.EventObject} e
52493          */
52494         "dblclick" : true,
52495         /**
52496          * @event contextmenu
52497          * The raw contextmenu event for the entire grid.
52498          * @param {Roo.EventObject} e
52499          */
52500         "contextmenu" : true,
52501         /**
52502          * @event mousedown
52503          * The raw mousedown event for the entire grid.
52504          * @param {Roo.EventObject} e
52505          */
52506         "mousedown" : true,
52507         /**
52508          * @event mouseup
52509          * The raw mouseup event for the entire grid.
52510          * @param {Roo.EventObject} e
52511          */
52512         "mouseup" : true,
52513         /**
52514          * @event mouseover
52515          * The raw mouseover event for the entire grid.
52516          * @param {Roo.EventObject} e
52517          */
52518         "mouseover" : true,
52519         /**
52520          * @event mouseout
52521          * The raw mouseout event for the entire grid.
52522          * @param {Roo.EventObject} e
52523          */
52524         "mouseout" : true,
52525         /**
52526          * @event keypress
52527          * The raw keypress event for the entire grid.
52528          * @param {Roo.EventObject} e
52529          */
52530         "keypress" : true,
52531         /**
52532          * @event keydown
52533          * The raw keydown event for the entire grid.
52534          * @param {Roo.EventObject} e
52535          */
52536         "keydown" : true,
52537
52538         // custom events
52539
52540         /**
52541          * @event cellclick
52542          * Fires when a cell is clicked
52543          * @param {Grid} this
52544          * @param {Number} rowIndex
52545          * @param {Number} columnIndex
52546          * @param {Roo.EventObject} e
52547          */
52548         "cellclick" : true,
52549         /**
52550          * @event celldblclick
52551          * Fires when a cell is double clicked
52552          * @param {Grid} this
52553          * @param {Number} rowIndex
52554          * @param {Number} columnIndex
52555          * @param {Roo.EventObject} e
52556          */
52557         "celldblclick" : true,
52558         /**
52559          * @event rowclick
52560          * Fires when a row is clicked
52561          * @param {Grid} this
52562          * @param {Number} rowIndex
52563          * @param {Roo.EventObject} e
52564          */
52565         "rowclick" : true,
52566         /**
52567          * @event rowdblclick
52568          * Fires when a row is double clicked
52569          * @param {Grid} this
52570          * @param {Number} rowIndex
52571          * @param {Roo.EventObject} e
52572          */
52573         "rowdblclick" : true,
52574         /**
52575          * @event headerclick
52576          * Fires when a header is clicked
52577          * @param {Grid} this
52578          * @param {Number} columnIndex
52579          * @param {Roo.EventObject} e
52580          */
52581         "headerclick" : true,
52582         /**
52583          * @event headerdblclick
52584          * Fires when a header cell is double clicked
52585          * @param {Grid} this
52586          * @param {Number} columnIndex
52587          * @param {Roo.EventObject} e
52588          */
52589         "headerdblclick" : true,
52590         /**
52591          * @event rowcontextmenu
52592          * Fires when a row is right clicked
52593          * @param {Grid} this
52594          * @param {Number} rowIndex
52595          * @param {Roo.EventObject} e
52596          */
52597         "rowcontextmenu" : true,
52598         /**
52599          * @event cellcontextmenu
52600          * Fires when a cell is right clicked
52601          * @param {Grid} this
52602          * @param {Number} rowIndex
52603          * @param {Number} cellIndex
52604          * @param {Roo.EventObject} e
52605          */
52606          "cellcontextmenu" : true,
52607         /**
52608          * @event headercontextmenu
52609          * Fires when a header is right clicked
52610          * @param {Grid} this
52611          * @param {Number} columnIndex
52612          * @param {Roo.EventObject} e
52613          */
52614         "headercontextmenu" : true,
52615         /**
52616          * @event bodyscroll
52617          * Fires when the body element is scrolled
52618          * @param {Number} scrollLeft
52619          * @param {Number} scrollTop
52620          */
52621         "bodyscroll" : true,
52622         /**
52623          * @event columnresize
52624          * Fires when the user resizes a column
52625          * @param {Number} columnIndex
52626          * @param {Number} newSize
52627          */
52628         "columnresize" : true,
52629         /**
52630          * @event columnmove
52631          * Fires when the user moves a column
52632          * @param {Number} oldIndex
52633          * @param {Number} newIndex
52634          */
52635         "columnmove" : true,
52636         /**
52637          * @event startdrag
52638          * Fires when row(s) start being dragged
52639          * @param {Grid} this
52640          * @param {Roo.GridDD} dd The drag drop object
52641          * @param {event} e The raw browser event
52642          */
52643         "startdrag" : true,
52644         /**
52645          * @event enddrag
52646          * Fires when a drag operation is complete
52647          * @param {Grid} this
52648          * @param {Roo.GridDD} dd The drag drop object
52649          * @param {event} e The raw browser event
52650          */
52651         "enddrag" : true,
52652         /**
52653          * @event dragdrop
52654          * Fires when dragged row(s) are dropped on a valid DD target
52655          * @param {Grid} this
52656          * @param {Roo.GridDD} dd The drag drop object
52657          * @param {String} targetId The target drag drop object
52658          * @param {event} e The raw browser event
52659          */
52660         "dragdrop" : true,
52661         /**
52662          * @event dragover
52663          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
52664          * @param {Grid} this
52665          * @param {Roo.GridDD} dd The drag drop object
52666          * @param {String} targetId The target drag drop object
52667          * @param {event} e The raw browser event
52668          */
52669         "dragover" : true,
52670         /**
52671          * @event dragenter
52672          *  Fires when the dragged row(s) first cross another DD target while being dragged
52673          * @param {Grid} this
52674          * @param {Roo.GridDD} dd The drag drop object
52675          * @param {String} targetId The target drag drop object
52676          * @param {event} e The raw browser event
52677          */
52678         "dragenter" : true,
52679         /**
52680          * @event dragout
52681          * Fires when the dragged row(s) leave another DD target while being dragged
52682          * @param {Grid} this
52683          * @param {Roo.GridDD} dd The drag drop object
52684          * @param {String} targetId The target drag drop object
52685          * @param {event} e The raw browser event
52686          */
52687         "dragout" : true,
52688         /**
52689          * @event rowclass
52690          * Fires when a row is rendered, so you can change add a style to it.
52691          * @param {GridView} gridview   The grid view
52692          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52693          */
52694         'rowclass' : true,
52695
52696         /**
52697          * @event render
52698          * Fires when the grid is rendered
52699          * @param {Grid} grid
52700          */
52701         'render' : true
52702     });
52703
52704     Roo.grid.Grid.superclass.constructor.call(this);
52705 };
52706 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52707     
52708     /**
52709      * @cfg {String} ddGroup - drag drop group.
52710      */
52711
52712     /**
52713      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52714      */
52715     minColumnWidth : 25,
52716
52717     /**
52718      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52719      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52720      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52721      */
52722     autoSizeColumns : false,
52723
52724     /**
52725      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52726      */
52727     autoSizeHeaders : true,
52728
52729     /**
52730      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52731      */
52732     monitorWindowResize : true,
52733
52734     /**
52735      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52736      * rows measured to get a columns size. Default is 0 (all rows).
52737      */
52738     maxRowsToMeasure : 0,
52739
52740     /**
52741      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52742      */
52743     trackMouseOver : true,
52744
52745     /**
52746     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52747     */
52748     
52749     /**
52750     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52751     */
52752     enableDragDrop : false,
52753     
52754     /**
52755     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52756     */
52757     enableColumnMove : true,
52758     
52759     /**
52760     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52761     */
52762     enableColumnHide : true,
52763     
52764     /**
52765     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52766     */
52767     enableRowHeightSync : false,
52768     
52769     /**
52770     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52771     */
52772     stripeRows : true,
52773     
52774     /**
52775     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52776     */
52777     autoHeight : false,
52778
52779     /**
52780      * @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.
52781      */
52782     autoExpandColumn : false,
52783
52784     /**
52785     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52786     * Default is 50.
52787     */
52788     autoExpandMin : 50,
52789
52790     /**
52791     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52792     */
52793     autoExpandMax : 1000,
52794
52795     /**
52796     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52797     */
52798     view : null,
52799
52800     /**
52801     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52802     */
52803     loadMask : false,
52804     /**
52805     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52806     */
52807     dropTarget: false,
52808     
52809    
52810     
52811     // private
52812     rendered : false,
52813
52814     /**
52815     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52816     * of a fixed width. Default is false.
52817     */
52818     /**
52819     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52820     */
52821     /**
52822      * Called once after all setup has been completed and the grid is ready to be rendered.
52823      * @return {Roo.grid.Grid} this
52824      */
52825     render : function()
52826     {
52827         var c = this.container;
52828         // try to detect autoHeight/width mode
52829         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52830             this.autoHeight = true;
52831         }
52832         var view = this.getView();
52833         view.init(this);
52834
52835         c.on("click", this.onClick, this);
52836         c.on("dblclick", this.onDblClick, this);
52837         c.on("contextmenu", this.onContextMenu, this);
52838         c.on("keydown", this.onKeyDown, this);
52839         if (Roo.isTouch) {
52840             c.on("touchstart", this.onTouchStart, this);
52841         }
52842
52843         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52844
52845         this.getSelectionModel().init(this);
52846
52847         view.render();
52848
52849         if(this.loadMask){
52850             this.loadMask = new Roo.LoadMask(this.container,
52851                     Roo.apply({store:this.dataSource}, this.loadMask));
52852         }
52853         
52854         
52855         if (this.toolbar && this.toolbar.xtype) {
52856             this.toolbar.container = this.getView().getHeaderPanel(true);
52857             this.toolbar = new Roo.Toolbar(this.toolbar);
52858         }
52859         if (this.footer && this.footer.xtype) {
52860             this.footer.dataSource = this.getDataSource();
52861             this.footer.container = this.getView().getFooterPanel(true);
52862             this.footer = Roo.factory(this.footer, Roo);
52863         }
52864         if (this.dropTarget && this.dropTarget.xtype) {
52865             delete this.dropTarget.xtype;
52866             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52867         }
52868         
52869         
52870         this.rendered = true;
52871         this.fireEvent('render', this);
52872         return this;
52873     },
52874
52875         /**
52876          * Reconfigures the grid to use a different Store and Column Model.
52877          * The View will be bound to the new objects and refreshed.
52878          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52879          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52880          */
52881     reconfigure : function(dataSource, colModel){
52882         if(this.loadMask){
52883             this.loadMask.destroy();
52884             this.loadMask = new Roo.LoadMask(this.container,
52885                     Roo.apply({store:dataSource}, this.loadMask));
52886         }
52887         this.view.bind(dataSource, colModel);
52888         this.dataSource = dataSource;
52889         this.colModel = colModel;
52890         this.view.refresh(true);
52891     },
52892
52893     // private
52894     onKeyDown : function(e){
52895         this.fireEvent("keydown", e);
52896     },
52897
52898     /**
52899      * Destroy this grid.
52900      * @param {Boolean} removeEl True to remove the element
52901      */
52902     destroy : function(removeEl, keepListeners){
52903         if(this.loadMask){
52904             this.loadMask.destroy();
52905         }
52906         var c = this.container;
52907         c.removeAllListeners();
52908         this.view.destroy();
52909         this.colModel.purgeListeners();
52910         if(!keepListeners){
52911             this.purgeListeners();
52912         }
52913         c.update("");
52914         if(removeEl === true){
52915             c.remove();
52916         }
52917     },
52918
52919     // private
52920     processEvent : function(name, e){
52921         // does this fire select???
52922         //Roo.log('grid:processEvent '  + name);
52923         
52924         if (name != 'touchstart' ) {
52925             this.fireEvent(name, e);    
52926         }
52927         
52928         var t = e.getTarget();
52929         var v = this.view;
52930         var header = v.findHeaderIndex(t);
52931         if(header !== false){
52932             var ename = name == 'touchstart' ? 'click' : name;
52933              
52934             this.fireEvent("header" + ename, this, header, e);
52935         }else{
52936             var row = v.findRowIndex(t);
52937             var cell = v.findCellIndex(t);
52938             if (name == 'touchstart') {
52939                 // first touch is always a click.
52940                 // hopefull this happens after selection is updated.?
52941                 name = false;
52942                 
52943                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52944                     var cs = this.selModel.getSelectedCell();
52945                     if (row == cs[0] && cell == cs[1]){
52946                         name = 'dblclick';
52947                     }
52948                 }
52949                 if (typeof(this.selModel.getSelections) != 'undefined') {
52950                     var cs = this.selModel.getSelections();
52951                     var ds = this.dataSource;
52952                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52953                         name = 'dblclick';
52954                     }
52955                 }
52956                 if (!name) {
52957                     return;
52958                 }
52959             }
52960             
52961             
52962             if(row !== false){
52963                 this.fireEvent("row" + name, this, row, e);
52964                 if(cell !== false){
52965                     this.fireEvent("cell" + name, this, row, cell, e);
52966                 }
52967             }
52968         }
52969     },
52970
52971     // private
52972     onClick : function(e){
52973         this.processEvent("click", e);
52974     },
52975    // private
52976     onTouchStart : function(e){
52977         this.processEvent("touchstart", e);
52978     },
52979
52980     // private
52981     onContextMenu : function(e, t){
52982         this.processEvent("contextmenu", e);
52983     },
52984
52985     // private
52986     onDblClick : function(e){
52987         this.processEvent("dblclick", e);
52988     },
52989
52990     // private
52991     walkCells : function(row, col, step, fn, scope){
52992         var cm = this.colModel, clen = cm.getColumnCount();
52993         var ds = this.dataSource, rlen = ds.getCount(), first = true;
52994         if(step < 0){
52995             if(col < 0){
52996                 row--;
52997                 first = false;
52998             }
52999             while(row >= 0){
53000                 if(!first){
53001                     col = clen-1;
53002                 }
53003                 first = false;
53004                 while(col >= 0){
53005                     if(fn.call(scope || this, row, col, cm) === true){
53006                         return [row, col];
53007                     }
53008                     col--;
53009                 }
53010                 row--;
53011             }
53012         } else {
53013             if(col >= clen){
53014                 row++;
53015                 first = false;
53016             }
53017             while(row < rlen){
53018                 if(!first){
53019                     col = 0;
53020                 }
53021                 first = false;
53022                 while(col < clen){
53023                     if(fn.call(scope || this, row, col, cm) === true){
53024                         return [row, col];
53025                     }
53026                     col++;
53027                 }
53028                 row++;
53029             }
53030         }
53031         return null;
53032     },
53033
53034     // private
53035     getSelections : function(){
53036         return this.selModel.getSelections();
53037     },
53038
53039     /**
53040      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
53041      * but if manual update is required this method will initiate it.
53042      */
53043     autoSize : function(){
53044         if(this.rendered){
53045             this.view.layout();
53046             if(this.view.adjustForScroll){
53047                 this.view.adjustForScroll();
53048             }
53049         }
53050     },
53051
53052     /**
53053      * Returns the grid's underlying element.
53054      * @return {Element} The element
53055      */
53056     getGridEl : function(){
53057         return this.container;
53058     },
53059
53060     // private for compatibility, overridden by editor grid
53061     stopEditing : function(){},
53062
53063     /**
53064      * Returns the grid's SelectionModel.
53065      * @return {SelectionModel}
53066      */
53067     getSelectionModel : function(){
53068         if(!this.selModel){
53069             this.selModel = new Roo.grid.RowSelectionModel();
53070         }
53071         return this.selModel;
53072     },
53073
53074     /**
53075      * Returns the grid's DataSource.
53076      * @return {DataSource}
53077      */
53078     getDataSource : function(){
53079         return this.dataSource;
53080     },
53081
53082     /**
53083      * Returns the grid's ColumnModel.
53084      * @return {ColumnModel}
53085      */
53086     getColumnModel : function(){
53087         return this.colModel;
53088     },
53089
53090     /**
53091      * Returns the grid's GridView object.
53092      * @return {GridView}
53093      */
53094     getView : function(){
53095         if(!this.view){
53096             this.view = new Roo.grid.GridView(this.viewConfig);
53097         }
53098         return this.view;
53099     },
53100     /**
53101      * Called to get grid's drag proxy text, by default returns this.ddText.
53102      * @return {String}
53103      */
53104     getDragDropText : function(){
53105         var count = this.selModel.getCount();
53106         return String.format(this.ddText, count, count == 1 ? '' : 's');
53107     }
53108 });
53109 /**
53110  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
53111  * %0 is replaced with the number of selected rows.
53112  * @type String
53113  */
53114 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
53115  * Based on:
53116  * Ext JS Library 1.1.1
53117  * Copyright(c) 2006-2007, Ext JS, LLC.
53118  *
53119  * Originally Released Under LGPL - original licence link has changed is not relivant.
53120  *
53121  * Fork - LGPL
53122  * <script type="text/javascript">
53123  */
53124  
53125 Roo.grid.AbstractGridView = function(){
53126         this.grid = null;
53127         
53128         this.events = {
53129             "beforerowremoved" : true,
53130             "beforerowsinserted" : true,
53131             "beforerefresh" : true,
53132             "rowremoved" : true,
53133             "rowsinserted" : true,
53134             "rowupdated" : true,
53135             "refresh" : true
53136         };
53137     Roo.grid.AbstractGridView.superclass.constructor.call(this);
53138 };
53139
53140 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
53141     rowClass : "x-grid-row",
53142     cellClass : "x-grid-cell",
53143     tdClass : "x-grid-td",
53144     hdClass : "x-grid-hd",
53145     splitClass : "x-grid-hd-split",
53146     
53147     init: function(grid){
53148         this.grid = grid;
53149                 var cid = this.grid.getGridEl().id;
53150         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
53151         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
53152         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
53153         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
53154         },
53155         
53156     getColumnRenderers : function(){
53157         var renderers = [];
53158         var cm = this.grid.colModel;
53159         var colCount = cm.getColumnCount();
53160         for(var i = 0; i < colCount; i++){
53161             renderers[i] = cm.getRenderer(i);
53162         }
53163         return renderers;
53164     },
53165     
53166     getColumnIds : function(){
53167         var ids = [];
53168         var cm = this.grid.colModel;
53169         var colCount = cm.getColumnCount();
53170         for(var i = 0; i < colCount; i++){
53171             ids[i] = cm.getColumnId(i);
53172         }
53173         return ids;
53174     },
53175     
53176     getDataIndexes : function(){
53177         if(!this.indexMap){
53178             this.indexMap = this.buildIndexMap();
53179         }
53180         return this.indexMap.colToData;
53181     },
53182     
53183     getColumnIndexByDataIndex : function(dataIndex){
53184         if(!this.indexMap){
53185             this.indexMap = this.buildIndexMap();
53186         }
53187         return this.indexMap.dataToCol[dataIndex];
53188     },
53189     
53190     /**
53191      * Set a css style for a column dynamically. 
53192      * @param {Number} colIndex The index of the column
53193      * @param {String} name The css property name
53194      * @param {String} value The css value
53195      */
53196     setCSSStyle : function(colIndex, name, value){
53197         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
53198         Roo.util.CSS.updateRule(selector, name, value);
53199     },
53200     
53201     generateRules : function(cm){
53202         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
53203         Roo.util.CSS.removeStyleSheet(rulesId);
53204         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53205             var cid = cm.getColumnId(i);
53206             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
53207                          this.tdSelector, cid, " {\n}\n",
53208                          this.hdSelector, cid, " {\n}\n",
53209                          this.splitSelector, cid, " {\n}\n");
53210         }
53211         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53212     }
53213 });/*
53214  * Based on:
53215  * Ext JS Library 1.1.1
53216  * Copyright(c) 2006-2007, Ext JS, LLC.
53217  *
53218  * Originally Released Under LGPL - original licence link has changed is not relivant.
53219  *
53220  * Fork - LGPL
53221  * <script type="text/javascript">
53222  */
53223
53224 // private
53225 // This is a support class used internally by the Grid components
53226 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
53227     this.grid = grid;
53228     this.view = grid.getView();
53229     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53230     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
53231     if(hd2){
53232         this.setHandleElId(Roo.id(hd));
53233         this.setOuterHandleElId(Roo.id(hd2));
53234     }
53235     this.scroll = false;
53236 };
53237 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
53238     maxDragWidth: 120,
53239     getDragData : function(e){
53240         var t = Roo.lib.Event.getTarget(e);
53241         var h = this.view.findHeaderCell(t);
53242         if(h){
53243             return {ddel: h.firstChild, header:h};
53244         }
53245         return false;
53246     },
53247
53248     onInitDrag : function(e){
53249         this.view.headersDisabled = true;
53250         var clone = this.dragData.ddel.cloneNode(true);
53251         clone.id = Roo.id();
53252         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
53253         this.proxy.update(clone);
53254         return true;
53255     },
53256
53257     afterValidDrop : function(){
53258         var v = this.view;
53259         setTimeout(function(){
53260             v.headersDisabled = false;
53261         }, 50);
53262     },
53263
53264     afterInvalidDrop : function(){
53265         var v = this.view;
53266         setTimeout(function(){
53267             v.headersDisabled = false;
53268         }, 50);
53269     }
53270 });
53271 /*
53272  * Based on:
53273  * Ext JS Library 1.1.1
53274  * Copyright(c) 2006-2007, Ext JS, LLC.
53275  *
53276  * Originally Released Under LGPL - original licence link has changed is not relivant.
53277  *
53278  * Fork - LGPL
53279  * <script type="text/javascript">
53280  */
53281 // private
53282 // This is a support class used internally by the Grid components
53283 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
53284     this.grid = grid;
53285     this.view = grid.getView();
53286     // split the proxies so they don't interfere with mouse events
53287     this.proxyTop = Roo.DomHelper.append(document.body, {
53288         cls:"col-move-top", html:"&#160;"
53289     }, true);
53290     this.proxyBottom = Roo.DomHelper.append(document.body, {
53291         cls:"col-move-bottom", html:"&#160;"
53292     }, true);
53293     this.proxyTop.hide = this.proxyBottom.hide = function(){
53294         this.setLeftTop(-100,-100);
53295         this.setStyle("visibility", "hidden");
53296     };
53297     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53298     // temporarily disabled
53299     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
53300     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
53301 };
53302 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
53303     proxyOffsets : [-4, -9],
53304     fly: Roo.Element.fly,
53305
53306     getTargetFromEvent : function(e){
53307         var t = Roo.lib.Event.getTarget(e);
53308         var cindex = this.view.findCellIndex(t);
53309         if(cindex !== false){
53310             return this.view.getHeaderCell(cindex);
53311         }
53312         return null;
53313     },
53314
53315     nextVisible : function(h){
53316         var v = this.view, cm = this.grid.colModel;
53317         h = h.nextSibling;
53318         while(h){
53319             if(!cm.isHidden(v.getCellIndex(h))){
53320                 return h;
53321             }
53322             h = h.nextSibling;
53323         }
53324         return null;
53325     },
53326
53327     prevVisible : function(h){
53328         var v = this.view, cm = this.grid.colModel;
53329         h = h.prevSibling;
53330         while(h){
53331             if(!cm.isHidden(v.getCellIndex(h))){
53332                 return h;
53333             }
53334             h = h.prevSibling;
53335         }
53336         return null;
53337     },
53338
53339     positionIndicator : function(h, n, e){
53340         var x = Roo.lib.Event.getPageX(e);
53341         var r = Roo.lib.Dom.getRegion(n.firstChild);
53342         var px, pt, py = r.top + this.proxyOffsets[1];
53343         if((r.right - x) <= (r.right-r.left)/2){
53344             px = r.right+this.view.borderWidth;
53345             pt = "after";
53346         }else{
53347             px = r.left;
53348             pt = "before";
53349         }
53350         var oldIndex = this.view.getCellIndex(h);
53351         var newIndex = this.view.getCellIndex(n);
53352
53353         if(this.grid.colModel.isFixed(newIndex)){
53354             return false;
53355         }
53356
53357         var locked = this.grid.colModel.isLocked(newIndex);
53358
53359         if(pt == "after"){
53360             newIndex++;
53361         }
53362         if(oldIndex < newIndex){
53363             newIndex--;
53364         }
53365         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
53366             return false;
53367         }
53368         px +=  this.proxyOffsets[0];
53369         this.proxyTop.setLeftTop(px, py);
53370         this.proxyTop.show();
53371         if(!this.bottomOffset){
53372             this.bottomOffset = this.view.mainHd.getHeight();
53373         }
53374         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
53375         this.proxyBottom.show();
53376         return pt;
53377     },
53378
53379     onNodeEnter : function(n, dd, e, data){
53380         if(data.header != n){
53381             this.positionIndicator(data.header, n, e);
53382         }
53383     },
53384
53385     onNodeOver : function(n, dd, e, data){
53386         var result = false;
53387         if(data.header != n){
53388             result = this.positionIndicator(data.header, n, e);
53389         }
53390         if(!result){
53391             this.proxyTop.hide();
53392             this.proxyBottom.hide();
53393         }
53394         return result ? this.dropAllowed : this.dropNotAllowed;
53395     },
53396
53397     onNodeOut : function(n, dd, e, data){
53398         this.proxyTop.hide();
53399         this.proxyBottom.hide();
53400     },
53401
53402     onNodeDrop : function(n, dd, e, data){
53403         var h = data.header;
53404         if(h != n){
53405             var cm = this.grid.colModel;
53406             var x = Roo.lib.Event.getPageX(e);
53407             var r = Roo.lib.Dom.getRegion(n.firstChild);
53408             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
53409             var oldIndex = this.view.getCellIndex(h);
53410             var newIndex = this.view.getCellIndex(n);
53411             var locked = cm.isLocked(newIndex);
53412             if(pt == "after"){
53413                 newIndex++;
53414             }
53415             if(oldIndex < newIndex){
53416                 newIndex--;
53417             }
53418             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
53419                 return false;
53420             }
53421             cm.setLocked(oldIndex, locked, true);
53422             cm.moveColumn(oldIndex, newIndex);
53423             this.grid.fireEvent("columnmove", oldIndex, newIndex);
53424             return true;
53425         }
53426         return false;
53427     }
53428 });
53429 /*
53430  * Based on:
53431  * Ext JS Library 1.1.1
53432  * Copyright(c) 2006-2007, Ext JS, LLC.
53433  *
53434  * Originally Released Under LGPL - original licence link has changed is not relivant.
53435  *
53436  * Fork - LGPL
53437  * <script type="text/javascript">
53438  */
53439   
53440 /**
53441  * @class Roo.grid.GridView
53442  * @extends Roo.util.Observable
53443  *
53444  * @constructor
53445  * @param {Object} config
53446  */
53447 Roo.grid.GridView = function(config){
53448     Roo.grid.GridView.superclass.constructor.call(this);
53449     this.el = null;
53450
53451     Roo.apply(this, config);
53452 };
53453
53454 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
53455
53456     unselectable :  'unselectable="on"',
53457     unselectableCls :  'x-unselectable',
53458     
53459     
53460     rowClass : "x-grid-row",
53461
53462     cellClass : "x-grid-col",
53463
53464     tdClass : "x-grid-td",
53465
53466     hdClass : "x-grid-hd",
53467
53468     splitClass : "x-grid-split",
53469
53470     sortClasses : ["sort-asc", "sort-desc"],
53471
53472     enableMoveAnim : false,
53473
53474     hlColor: "C3DAF9",
53475
53476     dh : Roo.DomHelper,
53477
53478     fly : Roo.Element.fly,
53479
53480     css : Roo.util.CSS,
53481
53482     borderWidth: 1,
53483
53484     splitOffset: 3,
53485
53486     scrollIncrement : 22,
53487
53488     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
53489
53490     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
53491
53492     bind : function(ds, cm){
53493         if(this.ds){
53494             this.ds.un("load", this.onLoad, this);
53495             this.ds.un("datachanged", this.onDataChange, this);
53496             this.ds.un("add", this.onAdd, this);
53497             this.ds.un("remove", this.onRemove, this);
53498             this.ds.un("update", this.onUpdate, this);
53499             this.ds.un("clear", this.onClear, this);
53500         }
53501         if(ds){
53502             ds.on("load", this.onLoad, this);
53503             ds.on("datachanged", this.onDataChange, this);
53504             ds.on("add", this.onAdd, this);
53505             ds.on("remove", this.onRemove, this);
53506             ds.on("update", this.onUpdate, this);
53507             ds.on("clear", this.onClear, this);
53508         }
53509         this.ds = ds;
53510
53511         if(this.cm){
53512             this.cm.un("widthchange", this.onColWidthChange, this);
53513             this.cm.un("headerchange", this.onHeaderChange, this);
53514             this.cm.un("hiddenchange", this.onHiddenChange, this);
53515             this.cm.un("columnmoved", this.onColumnMove, this);
53516             this.cm.un("columnlockchange", this.onColumnLock, this);
53517         }
53518         if(cm){
53519             this.generateRules(cm);
53520             cm.on("widthchange", this.onColWidthChange, this);
53521             cm.on("headerchange", this.onHeaderChange, this);
53522             cm.on("hiddenchange", this.onHiddenChange, this);
53523             cm.on("columnmoved", this.onColumnMove, this);
53524             cm.on("columnlockchange", this.onColumnLock, this);
53525         }
53526         this.cm = cm;
53527     },
53528
53529     init: function(grid){
53530         Roo.grid.GridView.superclass.init.call(this, grid);
53531
53532         this.bind(grid.dataSource, grid.colModel);
53533
53534         grid.on("headerclick", this.handleHeaderClick, this);
53535
53536         if(grid.trackMouseOver){
53537             grid.on("mouseover", this.onRowOver, this);
53538             grid.on("mouseout", this.onRowOut, this);
53539         }
53540         grid.cancelTextSelection = function(){};
53541         this.gridId = grid.id;
53542
53543         var tpls = this.templates || {};
53544
53545         if(!tpls.master){
53546             tpls.master = new Roo.Template(
53547                '<div class="x-grid" hidefocus="true">',
53548                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
53549                   '<div class="x-grid-topbar"></div>',
53550                   '<div class="x-grid-scroller"><div></div></div>',
53551                   '<div class="x-grid-locked">',
53552                       '<div class="x-grid-header">{lockedHeader}</div>',
53553                       '<div class="x-grid-body">{lockedBody}</div>',
53554                   "</div>",
53555                   '<div class="x-grid-viewport">',
53556                       '<div class="x-grid-header">{header}</div>',
53557                       '<div class="x-grid-body">{body}</div>',
53558                   "</div>",
53559                   '<div class="x-grid-bottombar"></div>',
53560                  
53561                   '<div class="x-grid-resize-proxy">&#160;</div>',
53562                "</div>"
53563             );
53564             tpls.master.disableformats = true;
53565         }
53566
53567         if(!tpls.header){
53568             tpls.header = new Roo.Template(
53569                '<table border="0" cellspacing="0" cellpadding="0">',
53570                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
53571                "</table>{splits}"
53572             );
53573             tpls.header.disableformats = true;
53574         }
53575         tpls.header.compile();
53576
53577         if(!tpls.hcell){
53578             tpls.hcell = new Roo.Template(
53579                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
53580                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
53581                 "</div></td>"
53582              );
53583              tpls.hcell.disableFormats = true;
53584         }
53585         tpls.hcell.compile();
53586
53587         if(!tpls.hsplit){
53588             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
53589                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
53590             tpls.hsplit.disableFormats = true;
53591         }
53592         tpls.hsplit.compile();
53593
53594         if(!tpls.body){
53595             tpls.body = new Roo.Template(
53596                '<table border="0" cellspacing="0" cellpadding="0">',
53597                "<tbody>{rows}</tbody>",
53598                "</table>"
53599             );
53600             tpls.body.disableFormats = true;
53601         }
53602         tpls.body.compile();
53603
53604         if(!tpls.row){
53605             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
53606             tpls.row.disableFormats = true;
53607         }
53608         tpls.row.compile();
53609
53610         if(!tpls.cell){
53611             tpls.cell = new Roo.Template(
53612                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
53613                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
53614                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
53615                 "</td>"
53616             );
53617             tpls.cell.disableFormats = true;
53618         }
53619         tpls.cell.compile();
53620
53621         this.templates = tpls;
53622     },
53623
53624     // remap these for backwards compat
53625     onColWidthChange : function(){
53626         this.updateColumns.apply(this, arguments);
53627     },
53628     onHeaderChange : function(){
53629         this.updateHeaders.apply(this, arguments);
53630     }, 
53631     onHiddenChange : function(){
53632         this.handleHiddenChange.apply(this, arguments);
53633     },
53634     onColumnMove : function(){
53635         this.handleColumnMove.apply(this, arguments);
53636     },
53637     onColumnLock : function(){
53638         this.handleLockChange.apply(this, arguments);
53639     },
53640
53641     onDataChange : function(){
53642         this.refresh();
53643         this.updateHeaderSortState();
53644     },
53645
53646     onClear : function(){
53647         this.refresh();
53648     },
53649
53650     onUpdate : function(ds, record){
53651         this.refreshRow(record);
53652     },
53653
53654     refreshRow : function(record){
53655         var ds = this.ds, index;
53656         if(typeof record == 'number'){
53657             index = record;
53658             record = ds.getAt(index);
53659         }else{
53660             index = ds.indexOf(record);
53661         }
53662         this.insertRows(ds, index, index, true);
53663         this.onRemove(ds, record, index+1, true);
53664         this.syncRowHeights(index, index);
53665         this.layout();
53666         this.fireEvent("rowupdated", this, index, record);
53667     },
53668
53669     onAdd : function(ds, records, index){
53670         this.insertRows(ds, index, index + (records.length-1));
53671     },
53672
53673     onRemove : function(ds, record, index, isUpdate){
53674         if(isUpdate !== true){
53675             this.fireEvent("beforerowremoved", this, index, record);
53676         }
53677         var bt = this.getBodyTable(), lt = this.getLockedTable();
53678         if(bt.rows[index]){
53679             bt.firstChild.removeChild(bt.rows[index]);
53680         }
53681         if(lt.rows[index]){
53682             lt.firstChild.removeChild(lt.rows[index]);
53683         }
53684         if(isUpdate !== true){
53685             this.stripeRows(index);
53686             this.syncRowHeights(index, index);
53687             this.layout();
53688             this.fireEvent("rowremoved", this, index, record);
53689         }
53690     },
53691
53692     onLoad : function(){
53693         this.scrollToTop();
53694     },
53695
53696     /**
53697      * Scrolls the grid to the top
53698      */
53699     scrollToTop : function(){
53700         if(this.scroller){
53701             this.scroller.dom.scrollTop = 0;
53702             this.syncScroll();
53703         }
53704     },
53705
53706     /**
53707      * Gets a panel in the header of the grid that can be used for toolbars etc.
53708      * After modifying the contents of this panel a call to grid.autoSize() may be
53709      * required to register any changes in size.
53710      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53711      * @return Roo.Element
53712      */
53713     getHeaderPanel : function(doShow){
53714         if(doShow){
53715             this.headerPanel.show();
53716         }
53717         return this.headerPanel;
53718     },
53719
53720     /**
53721      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53722      * After modifying the contents of this panel a call to grid.autoSize() may be
53723      * required to register any changes in size.
53724      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53725      * @return Roo.Element
53726      */
53727     getFooterPanel : function(doShow){
53728         if(doShow){
53729             this.footerPanel.show();
53730         }
53731         return this.footerPanel;
53732     },
53733
53734     initElements : function(){
53735         var E = Roo.Element;
53736         var el = this.grid.getGridEl().dom.firstChild;
53737         var cs = el.childNodes;
53738
53739         this.el = new E(el);
53740         
53741          this.focusEl = new E(el.firstChild);
53742         this.focusEl.swallowEvent("click", true);
53743         
53744         this.headerPanel = new E(cs[1]);
53745         this.headerPanel.enableDisplayMode("block");
53746
53747         this.scroller = new E(cs[2]);
53748         this.scrollSizer = new E(this.scroller.dom.firstChild);
53749
53750         this.lockedWrap = new E(cs[3]);
53751         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53752         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53753
53754         this.mainWrap = new E(cs[4]);
53755         this.mainHd = new E(this.mainWrap.dom.firstChild);
53756         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53757
53758         this.footerPanel = new E(cs[5]);
53759         this.footerPanel.enableDisplayMode("block");
53760
53761         this.resizeProxy = new E(cs[6]);
53762
53763         this.headerSelector = String.format(
53764            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53765            this.lockedHd.id, this.mainHd.id
53766         );
53767
53768         this.splitterSelector = String.format(
53769            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53770            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53771         );
53772     },
53773     idToCssName : function(s)
53774     {
53775         return s.replace(/[^a-z0-9]+/ig, '-');
53776     },
53777
53778     getHeaderCell : function(index){
53779         return Roo.DomQuery.select(this.headerSelector)[index];
53780     },
53781
53782     getHeaderCellMeasure : function(index){
53783         return this.getHeaderCell(index).firstChild;
53784     },
53785
53786     getHeaderCellText : function(index){
53787         return this.getHeaderCell(index).firstChild.firstChild;
53788     },
53789
53790     getLockedTable : function(){
53791         return this.lockedBody.dom.firstChild;
53792     },
53793
53794     getBodyTable : function(){
53795         return this.mainBody.dom.firstChild;
53796     },
53797
53798     getLockedRow : function(index){
53799         return this.getLockedTable().rows[index];
53800     },
53801
53802     getRow : function(index){
53803         return this.getBodyTable().rows[index];
53804     },
53805
53806     getRowComposite : function(index){
53807         if(!this.rowEl){
53808             this.rowEl = new Roo.CompositeElementLite();
53809         }
53810         var els = [], lrow, mrow;
53811         if(lrow = this.getLockedRow(index)){
53812             els.push(lrow);
53813         }
53814         if(mrow = this.getRow(index)){
53815             els.push(mrow);
53816         }
53817         this.rowEl.elements = els;
53818         return this.rowEl;
53819     },
53820     /**
53821      * Gets the 'td' of the cell
53822      * 
53823      * @param {Integer} rowIndex row to select
53824      * @param {Integer} colIndex column to select
53825      * 
53826      * @return {Object} 
53827      */
53828     getCell : function(rowIndex, colIndex){
53829         var locked = this.cm.getLockedCount();
53830         var source;
53831         if(colIndex < locked){
53832             source = this.lockedBody.dom.firstChild;
53833         }else{
53834             source = this.mainBody.dom.firstChild;
53835             colIndex -= locked;
53836         }
53837         return source.rows[rowIndex].childNodes[colIndex];
53838     },
53839
53840     getCellText : function(rowIndex, colIndex){
53841         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53842     },
53843
53844     getCellBox : function(cell){
53845         var b = this.fly(cell).getBox();
53846         if(Roo.isOpera){ // opera fails to report the Y
53847             b.y = cell.offsetTop + this.mainBody.getY();
53848         }
53849         return b;
53850     },
53851
53852     getCellIndex : function(cell){
53853         var id = String(cell.className).match(this.cellRE);
53854         if(id){
53855             return parseInt(id[1], 10);
53856         }
53857         return 0;
53858     },
53859
53860     findHeaderIndex : function(n){
53861         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53862         return r ? this.getCellIndex(r) : false;
53863     },
53864
53865     findHeaderCell : function(n){
53866         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53867         return r ? r : false;
53868     },
53869
53870     findRowIndex : function(n){
53871         if(!n){
53872             return false;
53873         }
53874         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53875         return r ? r.rowIndex : false;
53876     },
53877
53878     findCellIndex : function(node){
53879         var stop = this.el.dom;
53880         while(node && node != stop){
53881             if(this.findRE.test(node.className)){
53882                 return this.getCellIndex(node);
53883             }
53884             node = node.parentNode;
53885         }
53886         return false;
53887     },
53888
53889     getColumnId : function(index){
53890         return this.cm.getColumnId(index);
53891     },
53892
53893     getSplitters : function()
53894     {
53895         if(this.splitterSelector){
53896            return Roo.DomQuery.select(this.splitterSelector);
53897         }else{
53898             return null;
53899       }
53900     },
53901
53902     getSplitter : function(index){
53903         return this.getSplitters()[index];
53904     },
53905
53906     onRowOver : function(e, t){
53907         var row;
53908         if((row = this.findRowIndex(t)) !== false){
53909             this.getRowComposite(row).addClass("x-grid-row-over");
53910         }
53911     },
53912
53913     onRowOut : function(e, t){
53914         var row;
53915         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53916             this.getRowComposite(row).removeClass("x-grid-row-over");
53917         }
53918     },
53919
53920     renderHeaders : function(){
53921         var cm = this.cm;
53922         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53923         var cb = [], lb = [], sb = [], lsb = [], p = {};
53924         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53925             p.cellId = "x-grid-hd-0-" + i;
53926             p.splitId = "x-grid-csplit-0-" + i;
53927             p.id = cm.getColumnId(i);
53928             p.title = cm.getColumnTooltip(i) || "";
53929             p.value = cm.getColumnHeader(i) || "";
53930             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53931             if(!cm.isLocked(i)){
53932                 cb[cb.length] = ct.apply(p);
53933                 sb[sb.length] = st.apply(p);
53934             }else{
53935                 lb[lb.length] = ct.apply(p);
53936                 lsb[lsb.length] = st.apply(p);
53937             }
53938         }
53939         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53940                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53941     },
53942
53943     updateHeaders : function(){
53944         var html = this.renderHeaders();
53945         this.lockedHd.update(html[0]);
53946         this.mainHd.update(html[1]);
53947     },
53948
53949     /**
53950      * Focuses the specified row.
53951      * @param {Number} row The row index
53952      */
53953     focusRow : function(row)
53954     {
53955         //Roo.log('GridView.focusRow');
53956         var x = this.scroller.dom.scrollLeft;
53957         this.focusCell(row, 0, false);
53958         this.scroller.dom.scrollLeft = x;
53959     },
53960
53961     /**
53962      * Focuses the specified cell.
53963      * @param {Number} row The row index
53964      * @param {Number} col The column index
53965      * @param {Boolean} hscroll false to disable horizontal scrolling
53966      */
53967     focusCell : function(row, col, hscroll)
53968     {
53969         //Roo.log('GridView.focusCell');
53970         var el = this.ensureVisible(row, col, hscroll);
53971         this.focusEl.alignTo(el, "tl-tl");
53972         if(Roo.isGecko){
53973             this.focusEl.focus();
53974         }else{
53975             this.focusEl.focus.defer(1, this.focusEl);
53976         }
53977     },
53978
53979     /**
53980      * Scrolls the specified cell into view
53981      * @param {Number} row The row index
53982      * @param {Number} col The column index
53983      * @param {Boolean} hscroll false to disable horizontal scrolling
53984      */
53985     ensureVisible : function(row, col, hscroll)
53986     {
53987         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
53988         //return null; //disable for testing.
53989         if(typeof row != "number"){
53990             row = row.rowIndex;
53991         }
53992         if(row < 0 && row >= this.ds.getCount()){
53993             return  null;
53994         }
53995         col = (col !== undefined ? col : 0);
53996         var cm = this.grid.colModel;
53997         while(cm.isHidden(col)){
53998             col++;
53999         }
54000
54001         var el = this.getCell(row, col);
54002         if(!el){
54003             return null;
54004         }
54005         var c = this.scroller.dom;
54006
54007         var ctop = parseInt(el.offsetTop, 10);
54008         var cleft = parseInt(el.offsetLeft, 10);
54009         var cbot = ctop + el.offsetHeight;
54010         var cright = cleft + el.offsetWidth;
54011         
54012         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
54013         var stop = parseInt(c.scrollTop, 10);
54014         var sleft = parseInt(c.scrollLeft, 10);
54015         var sbot = stop + ch;
54016         var sright = sleft + c.clientWidth;
54017         /*
54018         Roo.log('GridView.ensureVisible:' +
54019                 ' ctop:' + ctop +
54020                 ' c.clientHeight:' + c.clientHeight +
54021                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
54022                 ' stop:' + stop +
54023                 ' cbot:' + cbot +
54024                 ' sbot:' + sbot +
54025                 ' ch:' + ch  
54026                 );
54027         */
54028         if(ctop < stop){
54029              c.scrollTop = ctop;
54030             //Roo.log("set scrolltop to ctop DISABLE?");
54031         }else if(cbot > sbot){
54032             //Roo.log("set scrolltop to cbot-ch");
54033             c.scrollTop = cbot-ch;
54034         }
54035         
54036         if(hscroll !== false){
54037             if(cleft < sleft){
54038                 c.scrollLeft = cleft;
54039             }else if(cright > sright){
54040                 c.scrollLeft = cright-c.clientWidth;
54041             }
54042         }
54043          
54044         return el;
54045     },
54046
54047     updateColumns : function(){
54048         this.grid.stopEditing();
54049         var cm = this.grid.colModel, colIds = this.getColumnIds();
54050         //var totalWidth = cm.getTotalWidth();
54051         var pos = 0;
54052         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54053             //if(cm.isHidden(i)) continue;
54054             var w = cm.getColumnWidth(i);
54055             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
54056             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
54057         }
54058         this.updateSplitters();
54059     },
54060
54061     generateRules : function(cm){
54062         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
54063         Roo.util.CSS.removeStyleSheet(rulesId);
54064         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54065             var cid = cm.getColumnId(i);
54066             var align = '';
54067             if(cm.config[i].align){
54068                 align = 'text-align:'+cm.config[i].align+';';
54069             }
54070             var hidden = '';
54071             if(cm.isHidden(i)){
54072                 hidden = 'display:none;';
54073             }
54074             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
54075             ruleBuf.push(
54076                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
54077                     this.hdSelector, cid, " {\n", align, width, "}\n",
54078                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
54079                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
54080         }
54081         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
54082     },
54083
54084     updateSplitters : function(){
54085         var cm = this.cm, s = this.getSplitters();
54086         if(s){ // splitters not created yet
54087             var pos = 0, locked = true;
54088             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54089                 if(cm.isHidden(i)) {
54090                     continue;
54091                 }
54092                 var w = cm.getColumnWidth(i); // make sure it's a number
54093                 if(!cm.isLocked(i) && locked){
54094                     pos = 0;
54095                     locked = false;
54096                 }
54097                 pos += w;
54098                 s[i].style.left = (pos-this.splitOffset) + "px";
54099             }
54100         }
54101     },
54102
54103     handleHiddenChange : function(colModel, colIndex, hidden){
54104         if(hidden){
54105             this.hideColumn(colIndex);
54106         }else{
54107             this.unhideColumn(colIndex);
54108         }
54109     },
54110
54111     hideColumn : function(colIndex){
54112         var cid = this.getColumnId(colIndex);
54113         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
54114         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
54115         if(Roo.isSafari){
54116             this.updateHeaders();
54117         }
54118         this.updateSplitters();
54119         this.layout();
54120     },
54121
54122     unhideColumn : function(colIndex){
54123         var cid = this.getColumnId(colIndex);
54124         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
54125         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
54126
54127         if(Roo.isSafari){
54128             this.updateHeaders();
54129         }
54130         this.updateSplitters();
54131         this.layout();
54132     },
54133
54134     insertRows : function(dm, firstRow, lastRow, isUpdate){
54135         if(firstRow == 0 && lastRow == dm.getCount()-1){
54136             this.refresh();
54137         }else{
54138             if(!isUpdate){
54139                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
54140             }
54141             var s = this.getScrollState();
54142             var markup = this.renderRows(firstRow, lastRow);
54143             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
54144             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
54145             this.restoreScroll(s);
54146             if(!isUpdate){
54147                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
54148                 this.syncRowHeights(firstRow, lastRow);
54149                 this.stripeRows(firstRow);
54150                 this.layout();
54151             }
54152         }
54153     },
54154
54155     bufferRows : function(markup, target, index){
54156         var before = null, trows = target.rows, tbody = target.tBodies[0];
54157         if(index < trows.length){
54158             before = trows[index];
54159         }
54160         var b = document.createElement("div");
54161         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
54162         var rows = b.firstChild.rows;
54163         for(var i = 0, len = rows.length; i < len; i++){
54164             if(before){
54165                 tbody.insertBefore(rows[0], before);
54166             }else{
54167                 tbody.appendChild(rows[0]);
54168             }
54169         }
54170         b.innerHTML = "";
54171         b = null;
54172     },
54173
54174     deleteRows : function(dm, firstRow, lastRow){
54175         if(dm.getRowCount()<1){
54176             this.fireEvent("beforerefresh", this);
54177             this.mainBody.update("");
54178             this.lockedBody.update("");
54179             this.fireEvent("refresh", this);
54180         }else{
54181             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
54182             var bt = this.getBodyTable();
54183             var tbody = bt.firstChild;
54184             var rows = bt.rows;
54185             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
54186                 tbody.removeChild(rows[firstRow]);
54187             }
54188             this.stripeRows(firstRow);
54189             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
54190         }
54191     },
54192
54193     updateRows : function(dataSource, firstRow, lastRow){
54194         var s = this.getScrollState();
54195         this.refresh();
54196         this.restoreScroll(s);
54197     },
54198
54199     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
54200         if(!noRefresh){
54201            this.refresh();
54202         }
54203         this.updateHeaderSortState();
54204     },
54205
54206     getScrollState : function(){
54207         
54208         var sb = this.scroller.dom;
54209         return {left: sb.scrollLeft, top: sb.scrollTop};
54210     },
54211
54212     stripeRows : function(startRow){
54213         if(!this.grid.stripeRows || this.ds.getCount() < 1){
54214             return;
54215         }
54216         startRow = startRow || 0;
54217         var rows = this.getBodyTable().rows;
54218         var lrows = this.getLockedTable().rows;
54219         var cls = ' x-grid-row-alt ';
54220         for(var i = startRow, len = rows.length; i < len; i++){
54221             var row = rows[i], lrow = lrows[i];
54222             var isAlt = ((i+1) % 2 == 0);
54223             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
54224             if(isAlt == hasAlt){
54225                 continue;
54226             }
54227             if(isAlt){
54228                 row.className += " x-grid-row-alt";
54229             }else{
54230                 row.className = row.className.replace("x-grid-row-alt", "");
54231             }
54232             if(lrow){
54233                 lrow.className = row.className;
54234             }
54235         }
54236     },
54237
54238     restoreScroll : function(state){
54239         //Roo.log('GridView.restoreScroll');
54240         var sb = this.scroller.dom;
54241         sb.scrollLeft = state.left;
54242         sb.scrollTop = state.top;
54243         this.syncScroll();
54244     },
54245
54246     syncScroll : function(){
54247         //Roo.log('GridView.syncScroll');
54248         var sb = this.scroller.dom;
54249         var sh = this.mainHd.dom;
54250         var bs = this.mainBody.dom;
54251         var lv = this.lockedBody.dom;
54252         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
54253         lv.scrollTop = bs.scrollTop = sb.scrollTop;
54254     },
54255
54256     handleScroll : function(e){
54257         this.syncScroll();
54258         var sb = this.scroller.dom;
54259         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
54260         e.stopEvent();
54261     },
54262
54263     handleWheel : function(e){
54264         var d = e.getWheelDelta();
54265         this.scroller.dom.scrollTop -= d*22;
54266         // set this here to prevent jumpy scrolling on large tables
54267         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
54268         e.stopEvent();
54269     },
54270
54271     renderRows : function(startRow, endRow){
54272         // pull in all the crap needed to render rows
54273         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
54274         var colCount = cm.getColumnCount();
54275
54276         if(ds.getCount() < 1){
54277             return ["", ""];
54278         }
54279
54280         // build a map for all the columns
54281         var cs = [];
54282         for(var i = 0; i < colCount; i++){
54283             var name = cm.getDataIndex(i);
54284             cs[i] = {
54285                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
54286                 renderer : cm.getRenderer(i),
54287                 id : cm.getColumnId(i),
54288                 locked : cm.isLocked(i)
54289             };
54290         }
54291
54292         startRow = startRow || 0;
54293         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
54294
54295         // records to render
54296         var rs = ds.getRange(startRow, endRow);
54297
54298         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
54299     },
54300
54301     // As much as I hate to duplicate code, this was branched because FireFox really hates
54302     // [].join("") on strings. The performance difference was substantial enough to
54303     // branch this function
54304     doRender : Roo.isGecko ?
54305             function(cs, rs, ds, startRow, colCount, stripe){
54306                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54307                 // buffers
54308                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54309                 
54310                 var hasListener = this.grid.hasListener('rowclass');
54311                 var rowcfg = {};
54312                 for(var j = 0, len = rs.length; j < len; j++){
54313                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
54314                     for(var i = 0; i < colCount; i++){
54315                         c = cs[i];
54316                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54317                         p.id = c.id;
54318                         p.css = p.attr = "";
54319                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54320                         if(p.value == undefined || p.value === "") {
54321                             p.value = "&#160;";
54322                         }
54323                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54324                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54325                         }
54326                         var markup = ct.apply(p);
54327                         if(!c.locked){
54328                             cb+= markup;
54329                         }else{
54330                             lcb+= markup;
54331                         }
54332                     }
54333                     var alt = [];
54334                     if(stripe && ((rowIndex+1) % 2 == 0)){
54335                         alt.push("x-grid-row-alt")
54336                     }
54337                     if(r.dirty){
54338                         alt.push(  " x-grid-dirty-row");
54339                     }
54340                     rp.cells = lcb;
54341                     if(this.getRowClass){
54342                         alt.push(this.getRowClass(r, rowIndex));
54343                     }
54344                     if (hasListener) {
54345                         rowcfg = {
54346                              
54347                             record: r,
54348                             rowIndex : rowIndex,
54349                             rowClass : ''
54350                         };
54351                         this.grid.fireEvent('rowclass', this, rowcfg);
54352                         alt.push(rowcfg.rowClass);
54353                     }
54354                     rp.alt = alt.join(" ");
54355                     lbuf+= rt.apply(rp);
54356                     rp.cells = cb;
54357                     buf+=  rt.apply(rp);
54358                 }
54359                 return [lbuf, buf];
54360             } :
54361             function(cs, rs, ds, startRow, colCount, stripe){
54362                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54363                 // buffers
54364                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54365                 var hasListener = this.grid.hasListener('rowclass');
54366  
54367                 var rowcfg = {};
54368                 for(var j = 0, len = rs.length; j < len; j++){
54369                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
54370                     for(var i = 0; i < colCount; i++){
54371                         c = cs[i];
54372                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54373                         p.id = c.id;
54374                         p.css = p.attr = "";
54375                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54376                         if(p.value == undefined || p.value === "") {
54377                             p.value = "&#160;";
54378                         }
54379                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54380                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54381                         }
54382                         
54383                         var markup = ct.apply(p);
54384                         if(!c.locked){
54385                             cb[cb.length] = markup;
54386                         }else{
54387                             lcb[lcb.length] = markup;
54388                         }
54389                     }
54390                     var alt = [];
54391                     if(stripe && ((rowIndex+1) % 2 == 0)){
54392                         alt.push( "x-grid-row-alt");
54393                     }
54394                     if(r.dirty){
54395                         alt.push(" x-grid-dirty-row");
54396                     }
54397                     rp.cells = lcb;
54398                     if(this.getRowClass){
54399                         alt.push( this.getRowClass(r, rowIndex));
54400                     }
54401                     if (hasListener) {
54402                         rowcfg = {
54403                              
54404                             record: r,
54405                             rowIndex : rowIndex,
54406                             rowClass : ''
54407                         };
54408                         this.grid.fireEvent('rowclass', this, rowcfg);
54409                         alt.push(rowcfg.rowClass);
54410                     }
54411                     rp.alt = alt.join(" ");
54412                     rp.cells = lcb.join("");
54413                     lbuf[lbuf.length] = rt.apply(rp);
54414                     rp.cells = cb.join("");
54415                     buf[buf.length] =  rt.apply(rp);
54416                 }
54417                 return [lbuf.join(""), buf.join("")];
54418             },
54419
54420     renderBody : function(){
54421         var markup = this.renderRows();
54422         var bt = this.templates.body;
54423         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
54424     },
54425
54426     /**
54427      * Refreshes the grid
54428      * @param {Boolean} headersToo
54429      */
54430     refresh : function(headersToo){
54431         this.fireEvent("beforerefresh", this);
54432         this.grid.stopEditing();
54433         var result = this.renderBody();
54434         this.lockedBody.update(result[0]);
54435         this.mainBody.update(result[1]);
54436         if(headersToo === true){
54437             this.updateHeaders();
54438             this.updateColumns();
54439             this.updateSplitters();
54440             this.updateHeaderSortState();
54441         }
54442         this.syncRowHeights();
54443         this.layout();
54444         this.fireEvent("refresh", this);
54445     },
54446
54447     handleColumnMove : function(cm, oldIndex, newIndex){
54448         this.indexMap = null;
54449         var s = this.getScrollState();
54450         this.refresh(true);
54451         this.restoreScroll(s);
54452         this.afterMove(newIndex);
54453     },
54454
54455     afterMove : function(colIndex){
54456         if(this.enableMoveAnim && Roo.enableFx){
54457             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
54458         }
54459         // if multisort - fix sortOrder, and reload..
54460         if (this.grid.dataSource.multiSort) {
54461             // the we can call sort again..
54462             var dm = this.grid.dataSource;
54463             var cm = this.grid.colModel;
54464             var so = [];
54465             for(var i = 0; i < cm.config.length; i++ ) {
54466                 
54467                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
54468                     continue; // dont' bother, it's not in sort list or being set.
54469                 }
54470                 
54471                 so.push(cm.config[i].dataIndex);
54472             };
54473             dm.sortOrder = so;
54474             dm.load(dm.lastOptions);
54475             
54476             
54477         }
54478         
54479     },
54480
54481     updateCell : function(dm, rowIndex, dataIndex){
54482         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
54483         if(typeof colIndex == "undefined"){ // not present in grid
54484             return;
54485         }
54486         var cm = this.grid.colModel;
54487         var cell = this.getCell(rowIndex, colIndex);
54488         var cellText = this.getCellText(rowIndex, colIndex);
54489
54490         var p = {
54491             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
54492             id : cm.getColumnId(colIndex),
54493             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
54494         };
54495         var renderer = cm.getRenderer(colIndex);
54496         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
54497         if(typeof val == "undefined" || val === "") {
54498             val = "&#160;";
54499         }
54500         cellText.innerHTML = val;
54501         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
54502         this.syncRowHeights(rowIndex, rowIndex);
54503     },
54504
54505     calcColumnWidth : function(colIndex, maxRowsToMeasure){
54506         var maxWidth = 0;
54507         if(this.grid.autoSizeHeaders){
54508             var h = this.getHeaderCellMeasure(colIndex);
54509             maxWidth = Math.max(maxWidth, h.scrollWidth);
54510         }
54511         var tb, index;
54512         if(this.cm.isLocked(colIndex)){
54513             tb = this.getLockedTable();
54514             index = colIndex;
54515         }else{
54516             tb = this.getBodyTable();
54517             index = colIndex - this.cm.getLockedCount();
54518         }
54519         if(tb && tb.rows){
54520             var rows = tb.rows;
54521             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
54522             for(var i = 0; i < stopIndex; i++){
54523                 var cell = rows[i].childNodes[index].firstChild;
54524                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
54525             }
54526         }
54527         return maxWidth + /*margin for error in IE*/ 5;
54528     },
54529     /**
54530      * Autofit a column to its content.
54531      * @param {Number} colIndex
54532      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
54533      */
54534      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
54535          if(this.cm.isHidden(colIndex)){
54536              return; // can't calc a hidden column
54537          }
54538         if(forceMinSize){
54539             var cid = this.cm.getColumnId(colIndex);
54540             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
54541            if(this.grid.autoSizeHeaders){
54542                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
54543            }
54544         }
54545         var newWidth = this.calcColumnWidth(colIndex);
54546         this.cm.setColumnWidth(colIndex,
54547             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
54548         if(!suppressEvent){
54549             this.grid.fireEvent("columnresize", colIndex, newWidth);
54550         }
54551     },
54552
54553     /**
54554      * Autofits all columns to their content and then expands to fit any extra space in the grid
54555      */
54556      autoSizeColumns : function(){
54557         var cm = this.grid.colModel;
54558         var colCount = cm.getColumnCount();
54559         for(var i = 0; i < colCount; i++){
54560             this.autoSizeColumn(i, true, true);
54561         }
54562         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
54563             this.fitColumns();
54564         }else{
54565             this.updateColumns();
54566             this.layout();
54567         }
54568     },
54569
54570     /**
54571      * Autofits all columns to the grid's width proportionate with their current size
54572      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
54573      */
54574     fitColumns : function(reserveScrollSpace){
54575         var cm = this.grid.colModel;
54576         var colCount = cm.getColumnCount();
54577         var cols = [];
54578         var width = 0;
54579         var i, w;
54580         for (i = 0; i < colCount; i++){
54581             if(!cm.isHidden(i) && !cm.isFixed(i)){
54582                 w = cm.getColumnWidth(i);
54583                 cols.push(i);
54584                 cols.push(w);
54585                 width += w;
54586             }
54587         }
54588         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
54589         if(reserveScrollSpace){
54590             avail -= 17;
54591         }
54592         var frac = (avail - cm.getTotalWidth())/width;
54593         while (cols.length){
54594             w = cols.pop();
54595             i = cols.pop();
54596             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
54597         }
54598         this.updateColumns();
54599         this.layout();
54600     },
54601
54602     onRowSelect : function(rowIndex){
54603         var row = this.getRowComposite(rowIndex);
54604         row.addClass("x-grid-row-selected");
54605     },
54606
54607     onRowDeselect : function(rowIndex){
54608         var row = this.getRowComposite(rowIndex);
54609         row.removeClass("x-grid-row-selected");
54610     },
54611
54612     onCellSelect : function(row, col){
54613         var cell = this.getCell(row, col);
54614         if(cell){
54615             Roo.fly(cell).addClass("x-grid-cell-selected");
54616         }
54617     },
54618
54619     onCellDeselect : function(row, col){
54620         var cell = this.getCell(row, col);
54621         if(cell){
54622             Roo.fly(cell).removeClass("x-grid-cell-selected");
54623         }
54624     },
54625
54626     updateHeaderSortState : function(){
54627         
54628         // sort state can be single { field: xxx, direction : yyy}
54629         // or   { xxx=>ASC , yyy : DESC ..... }
54630         
54631         var mstate = {};
54632         if (!this.ds.multiSort) { 
54633             var state = this.ds.getSortState();
54634             if(!state){
54635                 return;
54636             }
54637             mstate[state.field] = state.direction;
54638             // FIXME... - this is not used here.. but might be elsewhere..
54639             this.sortState = state;
54640             
54641         } else {
54642             mstate = this.ds.sortToggle;
54643         }
54644         //remove existing sort classes..
54645         
54646         var sc = this.sortClasses;
54647         var hds = this.el.select(this.headerSelector).removeClass(sc);
54648         
54649         for(var f in mstate) {
54650         
54651             var sortColumn = this.cm.findColumnIndex(f);
54652             
54653             if(sortColumn != -1){
54654                 var sortDir = mstate[f];        
54655                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
54656             }
54657         }
54658         
54659          
54660         
54661     },
54662
54663
54664     handleHeaderClick : function(g, index,e){
54665         
54666         Roo.log("header click");
54667         
54668         if (Roo.isTouch) {
54669             // touch events on header are handled by context
54670             this.handleHdCtx(g,index,e);
54671             return;
54672         }
54673         
54674         
54675         if(this.headersDisabled){
54676             return;
54677         }
54678         var dm = g.dataSource, cm = g.colModel;
54679         if(!cm.isSortable(index)){
54680             return;
54681         }
54682         g.stopEditing();
54683         
54684         if (dm.multiSort) {
54685             // update the sortOrder
54686             var so = [];
54687             for(var i = 0; i < cm.config.length; i++ ) {
54688                 
54689                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
54690                     continue; // dont' bother, it's not in sort list or being set.
54691                 }
54692                 
54693                 so.push(cm.config[i].dataIndex);
54694             };
54695             dm.sortOrder = so;
54696         }
54697         
54698         
54699         dm.sort(cm.getDataIndex(index));
54700     },
54701
54702
54703     destroy : function(){
54704         if(this.colMenu){
54705             this.colMenu.removeAll();
54706             Roo.menu.MenuMgr.unregister(this.colMenu);
54707             this.colMenu.getEl().remove();
54708             delete this.colMenu;
54709         }
54710         if(this.hmenu){
54711             this.hmenu.removeAll();
54712             Roo.menu.MenuMgr.unregister(this.hmenu);
54713             this.hmenu.getEl().remove();
54714             delete this.hmenu;
54715         }
54716         if(this.grid.enableColumnMove){
54717             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54718             if(dds){
54719                 for(var dd in dds){
54720                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54721                         var elid = dds[dd].dragElId;
54722                         dds[dd].unreg();
54723                         Roo.get(elid).remove();
54724                     } else if(dds[dd].config.isTarget){
54725                         dds[dd].proxyTop.remove();
54726                         dds[dd].proxyBottom.remove();
54727                         dds[dd].unreg();
54728                     }
54729                     if(Roo.dd.DDM.locationCache[dd]){
54730                         delete Roo.dd.DDM.locationCache[dd];
54731                     }
54732                 }
54733                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54734             }
54735         }
54736         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54737         this.bind(null, null);
54738         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54739     },
54740
54741     handleLockChange : function(){
54742         this.refresh(true);
54743     },
54744
54745     onDenyColumnLock : function(){
54746
54747     },
54748
54749     onDenyColumnHide : function(){
54750
54751     },
54752
54753     handleHdMenuClick : function(item){
54754         var index = this.hdCtxIndex;
54755         var cm = this.cm, ds = this.ds;
54756         switch(item.id){
54757             case "asc":
54758                 ds.sort(cm.getDataIndex(index), "ASC");
54759                 break;
54760             case "desc":
54761                 ds.sort(cm.getDataIndex(index), "DESC");
54762                 break;
54763             case "lock":
54764                 var lc = cm.getLockedCount();
54765                 if(cm.getColumnCount(true) <= lc+1){
54766                     this.onDenyColumnLock();
54767                     return;
54768                 }
54769                 if(lc != index){
54770                     cm.setLocked(index, true, true);
54771                     cm.moveColumn(index, lc);
54772                     this.grid.fireEvent("columnmove", index, lc);
54773                 }else{
54774                     cm.setLocked(index, true);
54775                 }
54776             break;
54777             case "unlock":
54778                 var lc = cm.getLockedCount();
54779                 if((lc-1) != index){
54780                     cm.setLocked(index, false, true);
54781                     cm.moveColumn(index, lc-1);
54782                     this.grid.fireEvent("columnmove", index, lc-1);
54783                 }else{
54784                     cm.setLocked(index, false);
54785                 }
54786             break;
54787             case 'wider': // used to expand cols on touch..
54788             case 'narrow':
54789                 var cw = cm.getColumnWidth(index);
54790                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54791                 cw = Math.max(0, cw);
54792                 cw = Math.min(cw,4000);
54793                 cm.setColumnWidth(index, cw);
54794                 break;
54795                 
54796             default:
54797                 index = cm.getIndexById(item.id.substr(4));
54798                 if(index != -1){
54799                     if(item.checked && cm.getColumnCount(true) <= 1){
54800                         this.onDenyColumnHide();
54801                         return false;
54802                     }
54803                     cm.setHidden(index, item.checked);
54804                 }
54805         }
54806         return true;
54807     },
54808
54809     beforeColMenuShow : function(){
54810         var cm = this.cm,  colCount = cm.getColumnCount();
54811         this.colMenu.removeAll();
54812         for(var i = 0; i < colCount; i++){
54813             this.colMenu.add(new Roo.menu.CheckItem({
54814                 id: "col-"+cm.getColumnId(i),
54815                 text: cm.getColumnHeader(i),
54816                 checked: !cm.isHidden(i),
54817                 hideOnClick:false
54818             }));
54819         }
54820     },
54821
54822     handleHdCtx : function(g, index, e){
54823         e.stopEvent();
54824         var hd = this.getHeaderCell(index);
54825         this.hdCtxIndex = index;
54826         var ms = this.hmenu.items, cm = this.cm;
54827         ms.get("asc").setDisabled(!cm.isSortable(index));
54828         ms.get("desc").setDisabled(!cm.isSortable(index));
54829         if(this.grid.enableColLock !== false){
54830             ms.get("lock").setDisabled(cm.isLocked(index));
54831             ms.get("unlock").setDisabled(!cm.isLocked(index));
54832         }
54833         this.hmenu.show(hd, "tl-bl");
54834     },
54835
54836     handleHdOver : function(e){
54837         var hd = this.findHeaderCell(e.getTarget());
54838         if(hd && !this.headersDisabled){
54839             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54840                this.fly(hd).addClass("x-grid-hd-over");
54841             }
54842         }
54843     },
54844
54845     handleHdOut : function(e){
54846         var hd = this.findHeaderCell(e.getTarget());
54847         if(hd){
54848             this.fly(hd).removeClass("x-grid-hd-over");
54849         }
54850     },
54851
54852     handleSplitDblClick : function(e, t){
54853         var i = this.getCellIndex(t);
54854         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54855             this.autoSizeColumn(i, true);
54856             this.layout();
54857         }
54858     },
54859
54860     render : function(){
54861
54862         var cm = this.cm;
54863         var colCount = cm.getColumnCount();
54864
54865         if(this.grid.monitorWindowResize === true){
54866             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54867         }
54868         var header = this.renderHeaders();
54869         var body = this.templates.body.apply({rows:""});
54870         var html = this.templates.master.apply({
54871             lockedBody: body,
54872             body: body,
54873             lockedHeader: header[0],
54874             header: header[1]
54875         });
54876
54877         //this.updateColumns();
54878
54879         this.grid.getGridEl().dom.innerHTML = html;
54880
54881         this.initElements();
54882         
54883         // a kludge to fix the random scolling effect in webkit
54884         this.el.on("scroll", function() {
54885             this.el.dom.scrollTop=0; // hopefully not recursive..
54886         },this);
54887
54888         this.scroller.on("scroll", this.handleScroll, this);
54889         this.lockedBody.on("mousewheel", this.handleWheel, this);
54890         this.mainBody.on("mousewheel", this.handleWheel, this);
54891
54892         this.mainHd.on("mouseover", this.handleHdOver, this);
54893         this.mainHd.on("mouseout", this.handleHdOut, this);
54894         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54895                 {delegate: "."+this.splitClass});
54896
54897         this.lockedHd.on("mouseover", this.handleHdOver, this);
54898         this.lockedHd.on("mouseout", this.handleHdOut, this);
54899         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54900                 {delegate: "."+this.splitClass});
54901
54902         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54903             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54904         }
54905
54906         this.updateSplitters();
54907
54908         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54909             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54910             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54911         }
54912
54913         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54914             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54915             this.hmenu.add(
54916                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54917                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54918             );
54919             if(this.grid.enableColLock !== false){
54920                 this.hmenu.add('-',
54921                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54922                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54923                 );
54924             }
54925             if (Roo.isTouch) {
54926                  this.hmenu.add('-',
54927                     {id:"wider", text: this.columnsWiderText},
54928                     {id:"narrow", text: this.columnsNarrowText }
54929                 );
54930                 
54931                  
54932             }
54933             
54934             if(this.grid.enableColumnHide !== false){
54935
54936                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54937                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54938                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54939
54940                 this.hmenu.add('-',
54941                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54942                 );
54943             }
54944             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54945
54946             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54947         }
54948
54949         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54950             this.dd = new Roo.grid.GridDragZone(this.grid, {
54951                 ddGroup : this.grid.ddGroup || 'GridDD'
54952             });
54953             
54954         }
54955
54956         /*
54957         for(var i = 0; i < colCount; i++){
54958             if(cm.isHidden(i)){
54959                 this.hideColumn(i);
54960             }
54961             if(cm.config[i].align){
54962                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54963                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54964             }
54965         }*/
54966         
54967         this.updateHeaderSortState();
54968
54969         this.beforeInitialResize();
54970         this.layout(true);
54971
54972         // two part rendering gives faster view to the user
54973         this.renderPhase2.defer(1, this);
54974     },
54975
54976     renderPhase2 : function(){
54977         // render the rows now
54978         this.refresh();
54979         if(this.grid.autoSizeColumns){
54980             this.autoSizeColumns();
54981         }
54982     },
54983
54984     beforeInitialResize : function(){
54985
54986     },
54987
54988     onColumnSplitterMoved : function(i, w){
54989         this.userResized = true;
54990         var cm = this.grid.colModel;
54991         cm.setColumnWidth(i, w, true);
54992         var cid = cm.getColumnId(i);
54993         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54994         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54995         this.updateSplitters();
54996         this.layout();
54997         this.grid.fireEvent("columnresize", i, w);
54998     },
54999
55000     syncRowHeights : function(startIndex, endIndex){
55001         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
55002             startIndex = startIndex || 0;
55003             var mrows = this.getBodyTable().rows;
55004             var lrows = this.getLockedTable().rows;
55005             var len = mrows.length-1;
55006             endIndex = Math.min(endIndex || len, len);
55007             for(var i = startIndex; i <= endIndex; i++){
55008                 var m = mrows[i], l = lrows[i];
55009                 var h = Math.max(m.offsetHeight, l.offsetHeight);
55010                 m.style.height = l.style.height = h + "px";
55011             }
55012         }
55013     },
55014
55015     layout : function(initialRender, is2ndPass){
55016         var g = this.grid;
55017         var auto = g.autoHeight;
55018         var scrollOffset = 16;
55019         var c = g.getGridEl(), cm = this.cm,
55020                 expandCol = g.autoExpandColumn,
55021                 gv = this;
55022         //c.beginMeasure();
55023
55024         if(!c.dom.offsetWidth){ // display:none?
55025             if(initialRender){
55026                 this.lockedWrap.show();
55027                 this.mainWrap.show();
55028             }
55029             return;
55030         }
55031
55032         var hasLock = this.cm.isLocked(0);
55033
55034         var tbh = this.headerPanel.getHeight();
55035         var bbh = this.footerPanel.getHeight();
55036
55037         if(auto){
55038             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
55039             var newHeight = ch + c.getBorderWidth("tb");
55040             if(g.maxHeight){
55041                 newHeight = Math.min(g.maxHeight, newHeight);
55042             }
55043             c.setHeight(newHeight);
55044         }
55045
55046         if(g.autoWidth){
55047             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
55048         }
55049
55050         var s = this.scroller;
55051
55052         var csize = c.getSize(true);
55053
55054         this.el.setSize(csize.width, csize.height);
55055
55056         this.headerPanel.setWidth(csize.width);
55057         this.footerPanel.setWidth(csize.width);
55058
55059         var hdHeight = this.mainHd.getHeight();
55060         var vw = csize.width;
55061         var vh = csize.height - (tbh + bbh);
55062
55063         s.setSize(vw, vh);
55064
55065         var bt = this.getBodyTable();
55066         var ltWidth = hasLock ?
55067                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
55068
55069         var scrollHeight = bt.offsetHeight;
55070         var scrollWidth = ltWidth + bt.offsetWidth;
55071         var vscroll = false, hscroll = false;
55072
55073         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
55074
55075         var lw = this.lockedWrap, mw = this.mainWrap;
55076         var lb = this.lockedBody, mb = this.mainBody;
55077
55078         setTimeout(function(){
55079             var t = s.dom.offsetTop;
55080             var w = s.dom.clientWidth,
55081                 h = s.dom.clientHeight;
55082
55083             lw.setTop(t);
55084             lw.setSize(ltWidth, h);
55085
55086             mw.setLeftTop(ltWidth, t);
55087             mw.setSize(w-ltWidth, h);
55088
55089             lb.setHeight(h-hdHeight);
55090             mb.setHeight(h-hdHeight);
55091
55092             if(is2ndPass !== true && !gv.userResized && expandCol){
55093                 // high speed resize without full column calculation
55094                 
55095                 var ci = cm.getIndexById(expandCol);
55096                 if (ci < 0) {
55097                     ci = cm.findColumnIndex(expandCol);
55098                 }
55099                 ci = Math.max(0, ci); // make sure it's got at least the first col.
55100                 var expandId = cm.getColumnId(ci);
55101                 var  tw = cm.getTotalWidth(false);
55102                 var currentWidth = cm.getColumnWidth(ci);
55103                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
55104                 if(currentWidth != cw){
55105                     cm.setColumnWidth(ci, cw, true);
55106                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
55107                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
55108                     gv.updateSplitters();
55109                     gv.layout(false, true);
55110                 }
55111             }
55112
55113             if(initialRender){
55114                 lw.show();
55115                 mw.show();
55116             }
55117             //c.endMeasure();
55118         }, 10);
55119     },
55120
55121     onWindowResize : function(){
55122         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
55123             return;
55124         }
55125         this.layout();
55126     },
55127
55128     appendFooter : function(parentEl){
55129         return null;
55130     },
55131
55132     sortAscText : "Sort Ascending",
55133     sortDescText : "Sort Descending",
55134     lockText : "Lock Column",
55135     unlockText : "Unlock Column",
55136     columnsText : "Columns",
55137  
55138     columnsWiderText : "Wider",
55139     columnsNarrowText : "Thinner"
55140 });
55141
55142
55143 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
55144     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
55145     this.proxy.el.addClass('x-grid3-col-dd');
55146 };
55147
55148 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
55149     handleMouseDown : function(e){
55150
55151     },
55152
55153     callHandleMouseDown : function(e){
55154         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
55155     }
55156 });
55157 /*
55158  * Based on:
55159  * Ext JS Library 1.1.1
55160  * Copyright(c) 2006-2007, Ext JS, LLC.
55161  *
55162  * Originally Released Under LGPL - original licence link has changed is not relivant.
55163  *
55164  * Fork - LGPL
55165  * <script type="text/javascript">
55166  */
55167  
55168 // private
55169 // This is a support class used internally by the Grid components
55170 Roo.grid.SplitDragZone = function(grid, hd, hd2){
55171     this.grid = grid;
55172     this.view = grid.getView();
55173     this.proxy = this.view.resizeProxy;
55174     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
55175         "gridSplitters" + this.grid.getGridEl().id, {
55176         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
55177     });
55178     this.setHandleElId(Roo.id(hd));
55179     this.setOuterHandleElId(Roo.id(hd2));
55180     this.scroll = false;
55181 };
55182 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
55183     fly: Roo.Element.fly,
55184
55185     b4StartDrag : function(x, y){
55186         this.view.headersDisabled = true;
55187         this.proxy.setHeight(this.view.mainWrap.getHeight());
55188         var w = this.cm.getColumnWidth(this.cellIndex);
55189         var minw = Math.max(w-this.grid.minColumnWidth, 0);
55190         this.resetConstraints();
55191         this.setXConstraint(minw, 1000);
55192         this.setYConstraint(0, 0);
55193         this.minX = x - minw;
55194         this.maxX = x + 1000;
55195         this.startPos = x;
55196         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
55197     },
55198
55199
55200     handleMouseDown : function(e){
55201         ev = Roo.EventObject.setEvent(e);
55202         var t = this.fly(ev.getTarget());
55203         if(t.hasClass("x-grid-split")){
55204             this.cellIndex = this.view.getCellIndex(t.dom);
55205             this.split = t.dom;
55206             this.cm = this.grid.colModel;
55207             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
55208                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
55209             }
55210         }
55211     },
55212
55213     endDrag : function(e){
55214         this.view.headersDisabled = false;
55215         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
55216         var diff = endX - this.startPos;
55217         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
55218     },
55219
55220     autoOffset : function(){
55221         this.setDelta(0,0);
55222     }
55223 });/*
55224  * Based on:
55225  * Ext JS Library 1.1.1
55226  * Copyright(c) 2006-2007, Ext JS, LLC.
55227  *
55228  * Originally Released Under LGPL - original licence link has changed is not relivant.
55229  *
55230  * Fork - LGPL
55231  * <script type="text/javascript">
55232  */
55233  
55234 // private
55235 // This is a support class used internally by the Grid components
55236 Roo.grid.GridDragZone = function(grid, config){
55237     this.view = grid.getView();
55238     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
55239     if(this.view.lockedBody){
55240         this.setHandleElId(Roo.id(this.view.mainBody.dom));
55241         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
55242     }
55243     this.scroll = false;
55244     this.grid = grid;
55245     this.ddel = document.createElement('div');
55246     this.ddel.className = 'x-grid-dd-wrap';
55247 };
55248
55249 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
55250     ddGroup : "GridDD",
55251
55252     getDragData : function(e){
55253         var t = Roo.lib.Event.getTarget(e);
55254         var rowIndex = this.view.findRowIndex(t);
55255         var sm = this.grid.selModel;
55256             
55257         //Roo.log(rowIndex);
55258         
55259         if (sm.getSelectedCell) {
55260             // cell selection..
55261             if (!sm.getSelectedCell()) {
55262                 return false;
55263             }
55264             if (rowIndex != sm.getSelectedCell()[0]) {
55265                 return false;
55266             }
55267         
55268         }
55269         
55270         if(rowIndex !== false){
55271             
55272             // if editorgrid.. 
55273             
55274             
55275             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
55276                
55277             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
55278               //  
55279             //}
55280             if (e.hasModifier()){
55281                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
55282             }
55283             
55284             Roo.log("getDragData");
55285             
55286             return {
55287                 grid: this.grid,
55288                 ddel: this.ddel,
55289                 rowIndex: rowIndex,
55290                 selections:sm.getSelections ? sm.getSelections() : (
55291                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
55292                 )
55293             };
55294         }
55295         return false;
55296     },
55297
55298     onInitDrag : function(e){
55299         var data = this.dragData;
55300         this.ddel.innerHTML = this.grid.getDragDropText();
55301         this.proxy.update(this.ddel);
55302         // fire start drag?
55303     },
55304
55305     afterRepair : function(){
55306         this.dragging = false;
55307     },
55308
55309     getRepairXY : function(e, data){
55310         return false;
55311     },
55312
55313     onEndDrag : function(data, e){
55314         // fire end drag?
55315     },
55316
55317     onValidDrop : function(dd, e, id){
55318         // fire drag drop?
55319         this.hideProxy();
55320     },
55321
55322     beforeInvalidDrop : function(e, id){
55323
55324     }
55325 });/*
55326  * Based on:
55327  * Ext JS Library 1.1.1
55328  * Copyright(c) 2006-2007, Ext JS, LLC.
55329  *
55330  * Originally Released Under LGPL - original licence link has changed is not relivant.
55331  *
55332  * Fork - LGPL
55333  * <script type="text/javascript">
55334  */
55335  
55336
55337 /**
55338  * @class Roo.grid.ColumnModel
55339  * @extends Roo.util.Observable
55340  * This is the default implementation of a ColumnModel used by the Grid. It defines
55341  * the columns in the grid.
55342  * <br>Usage:<br>
55343  <pre><code>
55344  var colModel = new Roo.grid.ColumnModel([
55345         {header: "Ticker", width: 60, sortable: true, locked: true},
55346         {header: "Company Name", width: 150, sortable: true},
55347         {header: "Market Cap.", width: 100, sortable: true},
55348         {header: "$ Sales", width: 100, sortable: true, renderer: money},
55349         {header: "Employees", width: 100, sortable: true, resizable: false}
55350  ]);
55351  </code></pre>
55352  * <p>
55353  
55354  * The config options listed for this class are options which may appear in each
55355  * individual column definition.
55356  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
55357  * @constructor
55358  * @param {Object} config An Array of column config objects. See this class's
55359  * config objects for details.
55360 */
55361 Roo.grid.ColumnModel = function(config){
55362         /**
55363      * The config passed into the constructor
55364      */
55365     this.config = config;
55366     this.lookup = {};
55367
55368     // if no id, create one
55369     // if the column does not have a dataIndex mapping,
55370     // map it to the order it is in the config
55371     for(var i = 0, len = config.length; i < len; i++){
55372         var c = config[i];
55373         if(typeof c.dataIndex == "undefined"){
55374             c.dataIndex = i;
55375         }
55376         if(typeof c.renderer == "string"){
55377             c.renderer = Roo.util.Format[c.renderer];
55378         }
55379         if(typeof c.id == "undefined"){
55380             c.id = Roo.id();
55381         }
55382         if(c.editor && c.editor.xtype){
55383             c.editor  = Roo.factory(c.editor, Roo.grid);
55384         }
55385         if(c.editor && c.editor.isFormField){
55386             c.editor = new Roo.grid.GridEditor(c.editor);
55387         }
55388         this.lookup[c.id] = c;
55389     }
55390
55391     /**
55392      * The width of columns which have no width specified (defaults to 100)
55393      * @type Number
55394      */
55395     this.defaultWidth = 100;
55396
55397     /**
55398      * Default sortable of columns which have no sortable specified (defaults to false)
55399      * @type Boolean
55400      */
55401     this.defaultSortable = false;
55402
55403     this.addEvents({
55404         /**
55405              * @event widthchange
55406              * Fires when the width of a column changes.
55407              * @param {ColumnModel} this
55408              * @param {Number} columnIndex The column index
55409              * @param {Number} newWidth The new width
55410              */
55411             "widthchange": true,
55412         /**
55413              * @event headerchange
55414              * Fires when the text of a header changes.
55415              * @param {ColumnModel} this
55416              * @param {Number} columnIndex The column index
55417              * @param {Number} newText The new header text
55418              */
55419             "headerchange": true,
55420         /**
55421              * @event hiddenchange
55422              * Fires when a column is hidden or "unhidden".
55423              * @param {ColumnModel} this
55424              * @param {Number} columnIndex The column index
55425              * @param {Boolean} hidden true if hidden, false otherwise
55426              */
55427             "hiddenchange": true,
55428             /**
55429          * @event columnmoved
55430          * Fires when a column is moved.
55431          * @param {ColumnModel} this
55432          * @param {Number} oldIndex
55433          * @param {Number} newIndex
55434          */
55435         "columnmoved" : true,
55436         /**
55437          * @event columlockchange
55438          * Fires when a column's locked state is changed
55439          * @param {ColumnModel} this
55440          * @param {Number} colIndex
55441          * @param {Boolean} locked true if locked
55442          */
55443         "columnlockchange" : true
55444     });
55445     Roo.grid.ColumnModel.superclass.constructor.call(this);
55446 };
55447 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
55448     /**
55449      * @cfg {String} header The header text to display in the Grid view.
55450      */
55451     /**
55452      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
55453      * {@link Roo.data.Record} definition from which to draw the column's value. If not
55454      * specified, the column's index is used as an index into the Record's data Array.
55455      */
55456     /**
55457      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
55458      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
55459      */
55460     /**
55461      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
55462      * Defaults to the value of the {@link #defaultSortable} property.
55463      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
55464      */
55465     /**
55466      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
55467      */
55468     /**
55469      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
55470      */
55471     /**
55472      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
55473      */
55474     /**
55475      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
55476      */
55477     /**
55478      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
55479      * given the cell's data value. See {@link #setRenderer}. If not specified, the
55480      * default renderer uses the raw data value. If an object is returned (bootstrap only)
55481      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
55482      */
55483        /**
55484      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
55485      */
55486     /**
55487      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
55488      */
55489     /**
55490      * @cfg {String} cursor (Optional)
55491      */
55492     /**
55493      * @cfg {String} tooltip (Optional)
55494      */
55495     /**
55496      * @cfg {Number} xs (Optional)
55497      */
55498     /**
55499      * @cfg {Number} sm (Optional)
55500      */
55501     /**
55502      * @cfg {Number} md (Optional)
55503      */
55504     /**
55505      * @cfg {Number} lg (Optional)
55506      */
55507     /**
55508      * Returns the id of the column at the specified index.
55509      * @param {Number} index The column index
55510      * @return {String} the id
55511      */
55512     getColumnId : function(index){
55513         return this.config[index].id;
55514     },
55515
55516     /**
55517      * Returns the column for a specified id.
55518      * @param {String} id The column id
55519      * @return {Object} the column
55520      */
55521     getColumnById : function(id){
55522         return this.lookup[id];
55523     },
55524
55525     
55526     /**
55527      * Returns the column for a specified dataIndex.
55528      * @param {String} dataIndex The column dataIndex
55529      * @return {Object|Boolean} the column or false if not found
55530      */
55531     getColumnByDataIndex: function(dataIndex){
55532         var index = this.findColumnIndex(dataIndex);
55533         return index > -1 ? this.config[index] : false;
55534     },
55535     
55536     /**
55537      * Returns the index for a specified column id.
55538      * @param {String} id The column id
55539      * @return {Number} the index, or -1 if not found
55540      */
55541     getIndexById : function(id){
55542         for(var i = 0, len = this.config.length; i < len; i++){
55543             if(this.config[i].id == id){
55544                 return i;
55545             }
55546         }
55547         return -1;
55548     },
55549     
55550     /**
55551      * Returns the index for a specified column dataIndex.
55552      * @param {String} dataIndex The column dataIndex
55553      * @return {Number} the index, or -1 if not found
55554      */
55555     
55556     findColumnIndex : function(dataIndex){
55557         for(var i = 0, len = this.config.length; i < len; i++){
55558             if(this.config[i].dataIndex == dataIndex){
55559                 return i;
55560             }
55561         }
55562         return -1;
55563     },
55564     
55565     
55566     moveColumn : function(oldIndex, newIndex){
55567         var c = this.config[oldIndex];
55568         this.config.splice(oldIndex, 1);
55569         this.config.splice(newIndex, 0, c);
55570         this.dataMap = null;
55571         this.fireEvent("columnmoved", this, oldIndex, newIndex);
55572     },
55573
55574     isLocked : function(colIndex){
55575         return this.config[colIndex].locked === true;
55576     },
55577
55578     setLocked : function(colIndex, value, suppressEvent){
55579         if(this.isLocked(colIndex) == value){
55580             return;
55581         }
55582         this.config[colIndex].locked = value;
55583         if(!suppressEvent){
55584             this.fireEvent("columnlockchange", this, colIndex, value);
55585         }
55586     },
55587
55588     getTotalLockedWidth : function(){
55589         var totalWidth = 0;
55590         for(var i = 0; i < this.config.length; i++){
55591             if(this.isLocked(i) && !this.isHidden(i)){
55592                 this.totalWidth += this.getColumnWidth(i);
55593             }
55594         }
55595         return totalWidth;
55596     },
55597
55598     getLockedCount : function(){
55599         for(var i = 0, len = this.config.length; i < len; i++){
55600             if(!this.isLocked(i)){
55601                 return i;
55602             }
55603         }
55604     },
55605
55606     /**
55607      * Returns the number of columns.
55608      * @return {Number}
55609      */
55610     getColumnCount : function(visibleOnly){
55611         if(visibleOnly === true){
55612             var c = 0;
55613             for(var i = 0, len = this.config.length; i < len; i++){
55614                 if(!this.isHidden(i)){
55615                     c++;
55616                 }
55617             }
55618             return c;
55619         }
55620         return this.config.length;
55621     },
55622
55623     /**
55624      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
55625      * @param {Function} fn
55626      * @param {Object} scope (optional)
55627      * @return {Array} result
55628      */
55629     getColumnsBy : function(fn, scope){
55630         var r = [];
55631         for(var i = 0, len = this.config.length; i < len; i++){
55632             var c = this.config[i];
55633             if(fn.call(scope||this, c, i) === true){
55634                 r[r.length] = c;
55635             }
55636         }
55637         return r;
55638     },
55639
55640     /**
55641      * Returns true if the specified column is sortable.
55642      * @param {Number} col The column index
55643      * @return {Boolean}
55644      */
55645     isSortable : function(col){
55646         if(typeof this.config[col].sortable == "undefined"){
55647             return this.defaultSortable;
55648         }
55649         return this.config[col].sortable;
55650     },
55651
55652     /**
55653      * Returns the rendering (formatting) function defined for the column.
55654      * @param {Number} col The column index.
55655      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
55656      */
55657     getRenderer : function(col){
55658         if(!this.config[col].renderer){
55659             return Roo.grid.ColumnModel.defaultRenderer;
55660         }
55661         return this.config[col].renderer;
55662     },
55663
55664     /**
55665      * Sets the rendering (formatting) function for a column.
55666      * @param {Number} col The column index
55667      * @param {Function} fn The function to use to process the cell's raw data
55668      * to return HTML markup for the grid view. The render function is called with
55669      * the following parameters:<ul>
55670      * <li>Data value.</li>
55671      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
55672      * <li>css A CSS style string to apply to the table cell.</li>
55673      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
55674      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
55675      * <li>Row index</li>
55676      * <li>Column index</li>
55677      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
55678      */
55679     setRenderer : function(col, fn){
55680         this.config[col].renderer = fn;
55681     },
55682
55683     /**
55684      * Returns the width for the specified column.
55685      * @param {Number} col The column index
55686      * @return {Number}
55687      */
55688     getColumnWidth : function(col){
55689         return this.config[col].width * 1 || this.defaultWidth;
55690     },
55691
55692     /**
55693      * Sets the width for a column.
55694      * @param {Number} col The column index
55695      * @param {Number} width The new width
55696      */
55697     setColumnWidth : function(col, width, suppressEvent){
55698         this.config[col].width = width;
55699         this.totalWidth = null;
55700         if(!suppressEvent){
55701              this.fireEvent("widthchange", this, col, width);
55702         }
55703     },
55704
55705     /**
55706      * Returns the total width of all columns.
55707      * @param {Boolean} includeHidden True to include hidden column widths
55708      * @return {Number}
55709      */
55710     getTotalWidth : function(includeHidden){
55711         if(!this.totalWidth){
55712             this.totalWidth = 0;
55713             for(var i = 0, len = this.config.length; i < len; i++){
55714                 if(includeHidden || !this.isHidden(i)){
55715                     this.totalWidth += this.getColumnWidth(i);
55716                 }
55717             }
55718         }
55719         return this.totalWidth;
55720     },
55721
55722     /**
55723      * Returns the header for the specified column.
55724      * @param {Number} col The column index
55725      * @return {String}
55726      */
55727     getColumnHeader : function(col){
55728         return this.config[col].header;
55729     },
55730
55731     /**
55732      * Sets the header for a column.
55733      * @param {Number} col The column index
55734      * @param {String} header The new header
55735      */
55736     setColumnHeader : function(col, header){
55737         this.config[col].header = header;
55738         this.fireEvent("headerchange", this, col, header);
55739     },
55740
55741     /**
55742      * Returns the tooltip for the specified column.
55743      * @param {Number} col The column index
55744      * @return {String}
55745      */
55746     getColumnTooltip : function(col){
55747             return this.config[col].tooltip;
55748     },
55749     /**
55750      * Sets the tooltip for a column.
55751      * @param {Number} col The column index
55752      * @param {String} tooltip The new tooltip
55753      */
55754     setColumnTooltip : function(col, tooltip){
55755             this.config[col].tooltip = tooltip;
55756     },
55757
55758     /**
55759      * Returns the dataIndex for the specified column.
55760      * @param {Number} col The column index
55761      * @return {Number}
55762      */
55763     getDataIndex : function(col){
55764         return this.config[col].dataIndex;
55765     },
55766
55767     /**
55768      * Sets the dataIndex for a column.
55769      * @param {Number} col The column index
55770      * @param {Number} dataIndex The new dataIndex
55771      */
55772     setDataIndex : function(col, dataIndex){
55773         this.config[col].dataIndex = dataIndex;
55774     },
55775
55776     
55777     
55778     /**
55779      * Returns true if the cell is editable.
55780      * @param {Number} colIndex The column index
55781      * @param {Number} rowIndex The row index
55782      * @return {Boolean}
55783      */
55784     isCellEditable : function(colIndex, rowIndex){
55785         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55786     },
55787
55788     /**
55789      * Returns the editor defined for the cell/column.
55790      * return false or null to disable editing.
55791      * @param {Number} colIndex The column index
55792      * @param {Number} rowIndex The row index
55793      * @return {Object}
55794      */
55795     getCellEditor : function(colIndex, rowIndex){
55796         return this.config[colIndex].editor;
55797     },
55798
55799     /**
55800      * Sets if a column is editable.
55801      * @param {Number} col The column index
55802      * @param {Boolean} editable True if the column is editable
55803      */
55804     setEditable : function(col, editable){
55805         this.config[col].editable = editable;
55806     },
55807
55808
55809     /**
55810      * Returns true if the column is hidden.
55811      * @param {Number} colIndex The column index
55812      * @return {Boolean}
55813      */
55814     isHidden : function(colIndex){
55815         return this.config[colIndex].hidden;
55816     },
55817
55818
55819     /**
55820      * Returns true if the column width cannot be changed
55821      */
55822     isFixed : function(colIndex){
55823         return this.config[colIndex].fixed;
55824     },
55825
55826     /**
55827      * Returns true if the column can be resized
55828      * @return {Boolean}
55829      */
55830     isResizable : function(colIndex){
55831         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55832     },
55833     /**
55834      * Sets if a column is hidden.
55835      * @param {Number} colIndex The column index
55836      * @param {Boolean} hidden True if the column is hidden
55837      */
55838     setHidden : function(colIndex, hidden){
55839         this.config[colIndex].hidden = hidden;
55840         this.totalWidth = null;
55841         this.fireEvent("hiddenchange", this, colIndex, hidden);
55842     },
55843
55844     /**
55845      * Sets the editor for a column.
55846      * @param {Number} col The column index
55847      * @param {Object} editor The editor object
55848      */
55849     setEditor : function(col, editor){
55850         this.config[col].editor = editor;
55851     }
55852 });
55853
55854 Roo.grid.ColumnModel.defaultRenderer = function(value){
55855         if(typeof value == "string" && value.length < 1){
55856             return "&#160;";
55857         }
55858         return value;
55859 };
55860
55861 // Alias for backwards compatibility
55862 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55863 /*
55864  * Based on:
55865  * Ext JS Library 1.1.1
55866  * Copyright(c) 2006-2007, Ext JS, LLC.
55867  *
55868  * Originally Released Under LGPL - original licence link has changed is not relivant.
55869  *
55870  * Fork - LGPL
55871  * <script type="text/javascript">
55872  */
55873
55874 /**
55875  * @class Roo.grid.AbstractSelectionModel
55876  * @extends Roo.util.Observable
55877  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55878  * implemented by descendant classes.  This class should not be directly instantiated.
55879  * @constructor
55880  */
55881 Roo.grid.AbstractSelectionModel = function(){
55882     this.locked = false;
55883     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55884 };
55885
55886 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55887     /** @ignore Called by the grid automatically. Do not call directly. */
55888     init : function(grid){
55889         this.grid = grid;
55890         this.initEvents();
55891     },
55892
55893     /**
55894      * Locks the selections.
55895      */
55896     lock : function(){
55897         this.locked = true;
55898     },
55899
55900     /**
55901      * Unlocks the selections.
55902      */
55903     unlock : function(){
55904         this.locked = false;
55905     },
55906
55907     /**
55908      * Returns true if the selections are locked.
55909      * @return {Boolean}
55910      */
55911     isLocked : function(){
55912         return this.locked;
55913     }
55914 });/*
55915  * Based on:
55916  * Ext JS Library 1.1.1
55917  * Copyright(c) 2006-2007, Ext JS, LLC.
55918  *
55919  * Originally Released Under LGPL - original licence link has changed is not relivant.
55920  *
55921  * Fork - LGPL
55922  * <script type="text/javascript">
55923  */
55924 /**
55925  * @extends Roo.grid.AbstractSelectionModel
55926  * @class Roo.grid.RowSelectionModel
55927  * The default SelectionModel used by {@link Roo.grid.Grid}.
55928  * It supports multiple selections and keyboard selection/navigation. 
55929  * @constructor
55930  * @param {Object} config
55931  */
55932 Roo.grid.RowSelectionModel = function(config){
55933     Roo.apply(this, config);
55934     this.selections = new Roo.util.MixedCollection(false, function(o){
55935         return o.id;
55936     });
55937
55938     this.last = false;
55939     this.lastActive = false;
55940
55941     this.addEvents({
55942         /**
55943              * @event selectionchange
55944              * Fires when the selection changes
55945              * @param {SelectionModel} this
55946              */
55947             "selectionchange" : true,
55948         /**
55949              * @event afterselectionchange
55950              * Fires after the selection changes (eg. by key press or clicking)
55951              * @param {SelectionModel} this
55952              */
55953             "afterselectionchange" : true,
55954         /**
55955              * @event beforerowselect
55956              * Fires when a row is selected being selected, return false to cancel.
55957              * @param {SelectionModel} this
55958              * @param {Number} rowIndex The selected index
55959              * @param {Boolean} keepExisting False if other selections will be cleared
55960              */
55961             "beforerowselect" : true,
55962         /**
55963              * @event rowselect
55964              * Fires when a row is selected.
55965              * @param {SelectionModel} this
55966              * @param {Number} rowIndex The selected index
55967              * @param {Roo.data.Record} r The record
55968              */
55969             "rowselect" : true,
55970         /**
55971              * @event rowdeselect
55972              * Fires when a row is deselected.
55973              * @param {SelectionModel} this
55974              * @param {Number} rowIndex The selected index
55975              */
55976         "rowdeselect" : true
55977     });
55978     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55979     this.locked = false;
55980 };
55981
55982 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
55983     /**
55984      * @cfg {Boolean} singleSelect
55985      * True to allow selection of only one row at a time (defaults to false)
55986      */
55987     singleSelect : false,
55988
55989     // private
55990     initEvents : function(){
55991
55992         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
55993             this.grid.on("mousedown", this.handleMouseDown, this);
55994         }else{ // allow click to work like normal
55995             this.grid.on("rowclick", this.handleDragableRowClick, this);
55996         }
55997
55998         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
55999             "up" : function(e){
56000                 if(!e.shiftKey){
56001                     this.selectPrevious(e.shiftKey);
56002                 }else if(this.last !== false && this.lastActive !== false){
56003                     var last = this.last;
56004                     this.selectRange(this.last,  this.lastActive-1);
56005                     this.grid.getView().focusRow(this.lastActive);
56006                     if(last !== false){
56007                         this.last = last;
56008                     }
56009                 }else{
56010                     this.selectFirstRow();
56011                 }
56012                 this.fireEvent("afterselectionchange", this);
56013             },
56014             "down" : function(e){
56015                 if(!e.shiftKey){
56016                     this.selectNext(e.shiftKey);
56017                 }else if(this.last !== false && this.lastActive !== false){
56018                     var last = this.last;
56019                     this.selectRange(this.last,  this.lastActive+1);
56020                     this.grid.getView().focusRow(this.lastActive);
56021                     if(last !== false){
56022                         this.last = last;
56023                     }
56024                 }else{
56025                     this.selectFirstRow();
56026                 }
56027                 this.fireEvent("afterselectionchange", this);
56028             },
56029             scope: this
56030         });
56031
56032         var view = this.grid.view;
56033         view.on("refresh", this.onRefresh, this);
56034         view.on("rowupdated", this.onRowUpdated, this);
56035         view.on("rowremoved", this.onRemove, this);
56036     },
56037
56038     // private
56039     onRefresh : function(){
56040         var ds = this.grid.dataSource, i, v = this.grid.view;
56041         var s = this.selections;
56042         s.each(function(r){
56043             if((i = ds.indexOfId(r.id)) != -1){
56044                 v.onRowSelect(i);
56045                 s.add(ds.getAt(i)); // updating the selection relate data
56046             }else{
56047                 s.remove(r);
56048             }
56049         });
56050     },
56051
56052     // private
56053     onRemove : function(v, index, r){
56054         this.selections.remove(r);
56055     },
56056
56057     // private
56058     onRowUpdated : function(v, index, r){
56059         if(this.isSelected(r)){
56060             v.onRowSelect(index);
56061         }
56062     },
56063
56064     /**
56065      * Select records.
56066      * @param {Array} records The records to select
56067      * @param {Boolean} keepExisting (optional) True to keep existing selections
56068      */
56069     selectRecords : function(records, keepExisting){
56070         if(!keepExisting){
56071             this.clearSelections();
56072         }
56073         var ds = this.grid.dataSource;
56074         for(var i = 0, len = records.length; i < len; i++){
56075             this.selectRow(ds.indexOf(records[i]), true);
56076         }
56077     },
56078
56079     /**
56080      * Gets the number of selected rows.
56081      * @return {Number}
56082      */
56083     getCount : function(){
56084         return this.selections.length;
56085     },
56086
56087     /**
56088      * Selects the first row in the grid.
56089      */
56090     selectFirstRow : function(){
56091         this.selectRow(0);
56092     },
56093
56094     /**
56095      * Select the last row.
56096      * @param {Boolean} keepExisting (optional) True to keep existing selections
56097      */
56098     selectLastRow : function(keepExisting){
56099         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
56100     },
56101
56102     /**
56103      * Selects the row immediately following the last selected row.
56104      * @param {Boolean} keepExisting (optional) True to keep existing selections
56105      */
56106     selectNext : function(keepExisting){
56107         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
56108             this.selectRow(this.last+1, keepExisting);
56109             this.grid.getView().focusRow(this.last);
56110         }
56111     },
56112
56113     /**
56114      * Selects the row that precedes the last selected row.
56115      * @param {Boolean} keepExisting (optional) True to keep existing selections
56116      */
56117     selectPrevious : function(keepExisting){
56118         if(this.last){
56119             this.selectRow(this.last-1, keepExisting);
56120             this.grid.getView().focusRow(this.last);
56121         }
56122     },
56123
56124     /**
56125      * Returns the selected records
56126      * @return {Array} Array of selected records
56127      */
56128     getSelections : function(){
56129         return [].concat(this.selections.items);
56130     },
56131
56132     /**
56133      * Returns the first selected record.
56134      * @return {Record}
56135      */
56136     getSelected : function(){
56137         return this.selections.itemAt(0);
56138     },
56139
56140
56141     /**
56142      * Clears all selections.
56143      */
56144     clearSelections : function(fast){
56145         if(this.locked) {
56146             return;
56147         }
56148         if(fast !== true){
56149             var ds = this.grid.dataSource;
56150             var s = this.selections;
56151             s.each(function(r){
56152                 this.deselectRow(ds.indexOfId(r.id));
56153             }, this);
56154             s.clear();
56155         }else{
56156             this.selections.clear();
56157         }
56158         this.last = false;
56159     },
56160
56161
56162     /**
56163      * Selects all rows.
56164      */
56165     selectAll : function(){
56166         if(this.locked) {
56167             return;
56168         }
56169         this.selections.clear();
56170         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
56171             this.selectRow(i, true);
56172         }
56173     },
56174
56175     /**
56176      * Returns True if there is a selection.
56177      * @return {Boolean}
56178      */
56179     hasSelection : function(){
56180         return this.selections.length > 0;
56181     },
56182
56183     /**
56184      * Returns True if the specified row is selected.
56185      * @param {Number/Record} record The record or index of the record to check
56186      * @return {Boolean}
56187      */
56188     isSelected : function(index){
56189         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
56190         return (r && this.selections.key(r.id) ? true : false);
56191     },
56192
56193     /**
56194      * Returns True if the specified record id is selected.
56195      * @param {String} id The id of record to check
56196      * @return {Boolean}
56197      */
56198     isIdSelected : function(id){
56199         return (this.selections.key(id) ? true : false);
56200     },
56201
56202     // private
56203     handleMouseDown : function(e, t){
56204         var view = this.grid.getView(), rowIndex;
56205         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
56206             return;
56207         };
56208         if(e.shiftKey && this.last !== false){
56209             var last = this.last;
56210             this.selectRange(last, rowIndex, e.ctrlKey);
56211             this.last = last; // reset the last
56212             view.focusRow(rowIndex);
56213         }else{
56214             var isSelected = this.isSelected(rowIndex);
56215             if(e.button !== 0 && isSelected){
56216                 view.focusRow(rowIndex);
56217             }else if(e.ctrlKey && isSelected){
56218                 this.deselectRow(rowIndex);
56219             }else if(!isSelected){
56220                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
56221                 view.focusRow(rowIndex);
56222             }
56223         }
56224         this.fireEvent("afterselectionchange", this);
56225     },
56226     // private
56227     handleDragableRowClick :  function(grid, rowIndex, e) 
56228     {
56229         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
56230             this.selectRow(rowIndex, false);
56231             grid.view.focusRow(rowIndex);
56232              this.fireEvent("afterselectionchange", this);
56233         }
56234     },
56235     
56236     /**
56237      * Selects multiple rows.
56238      * @param {Array} rows Array of the indexes of the row to select
56239      * @param {Boolean} keepExisting (optional) True to keep existing selections
56240      */
56241     selectRows : function(rows, keepExisting){
56242         if(!keepExisting){
56243             this.clearSelections();
56244         }
56245         for(var i = 0, len = rows.length; i < len; i++){
56246             this.selectRow(rows[i], true);
56247         }
56248     },
56249
56250     /**
56251      * Selects a range of rows. All rows in between startRow and endRow are also selected.
56252      * @param {Number} startRow The index of the first row in the range
56253      * @param {Number} endRow The index of the last row in the range
56254      * @param {Boolean} keepExisting (optional) True to retain existing selections
56255      */
56256     selectRange : function(startRow, endRow, keepExisting){
56257         if(this.locked) {
56258             return;
56259         }
56260         if(!keepExisting){
56261             this.clearSelections();
56262         }
56263         if(startRow <= endRow){
56264             for(var i = startRow; i <= endRow; i++){
56265                 this.selectRow(i, true);
56266             }
56267         }else{
56268             for(var i = startRow; i >= endRow; i--){
56269                 this.selectRow(i, true);
56270             }
56271         }
56272     },
56273
56274     /**
56275      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
56276      * @param {Number} startRow The index of the first row in the range
56277      * @param {Number} endRow The index of the last row in the range
56278      */
56279     deselectRange : function(startRow, endRow, preventViewNotify){
56280         if(this.locked) {
56281             return;
56282         }
56283         for(var i = startRow; i <= endRow; i++){
56284             this.deselectRow(i, preventViewNotify);
56285         }
56286     },
56287
56288     /**
56289      * Selects a row.
56290      * @param {Number} row The index of the row to select
56291      * @param {Boolean} keepExisting (optional) True to keep existing selections
56292      */
56293     selectRow : function(index, keepExisting, preventViewNotify){
56294         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
56295             return;
56296         }
56297         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
56298             if(!keepExisting || this.singleSelect){
56299                 this.clearSelections();
56300             }
56301             var r = this.grid.dataSource.getAt(index);
56302             this.selections.add(r);
56303             this.last = this.lastActive = index;
56304             if(!preventViewNotify){
56305                 this.grid.getView().onRowSelect(index);
56306             }
56307             this.fireEvent("rowselect", this, index, r);
56308             this.fireEvent("selectionchange", this);
56309         }
56310     },
56311
56312     /**
56313      * Deselects a row.
56314      * @param {Number} row The index of the row to deselect
56315      */
56316     deselectRow : function(index, preventViewNotify){
56317         if(this.locked) {
56318             return;
56319         }
56320         if(this.last == index){
56321             this.last = false;
56322         }
56323         if(this.lastActive == index){
56324             this.lastActive = false;
56325         }
56326         var r = this.grid.dataSource.getAt(index);
56327         this.selections.remove(r);
56328         if(!preventViewNotify){
56329             this.grid.getView().onRowDeselect(index);
56330         }
56331         this.fireEvent("rowdeselect", this, index);
56332         this.fireEvent("selectionchange", this);
56333     },
56334
56335     // private
56336     restoreLast : function(){
56337         if(this._last){
56338             this.last = this._last;
56339         }
56340     },
56341
56342     // private
56343     acceptsNav : function(row, col, cm){
56344         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56345     },
56346
56347     // private
56348     onEditorKey : function(field, e){
56349         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
56350         if(k == e.TAB){
56351             e.stopEvent();
56352             ed.completeEdit();
56353             if(e.shiftKey){
56354                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56355             }else{
56356                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56357             }
56358         }else if(k == e.ENTER && !e.ctrlKey){
56359             e.stopEvent();
56360             ed.completeEdit();
56361             if(e.shiftKey){
56362                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
56363             }else{
56364                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
56365             }
56366         }else if(k == e.ESC){
56367             ed.cancelEdit();
56368         }
56369         if(newCell){
56370             g.startEditing(newCell[0], newCell[1]);
56371         }
56372     }
56373 });/*
56374  * Based on:
56375  * Ext JS Library 1.1.1
56376  * Copyright(c) 2006-2007, Ext JS, LLC.
56377  *
56378  * Originally Released Under LGPL - original licence link has changed is not relivant.
56379  *
56380  * Fork - LGPL
56381  * <script type="text/javascript">
56382  */
56383 /**
56384  * @class Roo.grid.CellSelectionModel
56385  * @extends Roo.grid.AbstractSelectionModel
56386  * This class provides the basic implementation for cell selection in a grid.
56387  * @constructor
56388  * @param {Object} config The object containing the configuration of this model.
56389  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
56390  */
56391 Roo.grid.CellSelectionModel = function(config){
56392     Roo.apply(this, config);
56393
56394     this.selection = null;
56395
56396     this.addEvents({
56397         /**
56398              * @event beforerowselect
56399              * Fires before a cell is selected.
56400              * @param {SelectionModel} this
56401              * @param {Number} rowIndex The selected row index
56402              * @param {Number} colIndex The selected cell index
56403              */
56404             "beforecellselect" : true,
56405         /**
56406              * @event cellselect
56407              * Fires when a cell is selected.
56408              * @param {SelectionModel} this
56409              * @param {Number} rowIndex The selected row index
56410              * @param {Number} colIndex The selected cell index
56411              */
56412             "cellselect" : true,
56413         /**
56414              * @event selectionchange
56415              * Fires when the active selection changes.
56416              * @param {SelectionModel} this
56417              * @param {Object} selection null for no selection or an object (o) with two properties
56418                 <ul>
56419                 <li>o.record: the record object for the row the selection is in</li>
56420                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
56421                 </ul>
56422              */
56423             "selectionchange" : true,
56424         /**
56425              * @event tabend
56426              * Fires when the tab (or enter) was pressed on the last editable cell
56427              * You can use this to trigger add new row.
56428              * @param {SelectionModel} this
56429              */
56430             "tabend" : true,
56431          /**
56432              * @event beforeeditnext
56433              * Fires before the next editable sell is made active
56434              * You can use this to skip to another cell or fire the tabend
56435              *    if you set cell to false
56436              * @param {Object} eventdata object : { cell : [ row, col ] } 
56437              */
56438             "beforeeditnext" : true
56439     });
56440     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
56441 };
56442
56443 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
56444     
56445     enter_is_tab: false,
56446
56447     /** @ignore */
56448     initEvents : function(){
56449         this.grid.on("mousedown", this.handleMouseDown, this);
56450         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
56451         var view = this.grid.view;
56452         view.on("refresh", this.onViewChange, this);
56453         view.on("rowupdated", this.onRowUpdated, this);
56454         view.on("beforerowremoved", this.clearSelections, this);
56455         view.on("beforerowsinserted", this.clearSelections, this);
56456         if(this.grid.isEditor){
56457             this.grid.on("beforeedit", this.beforeEdit,  this);
56458         }
56459     },
56460
56461         //private
56462     beforeEdit : function(e){
56463         this.select(e.row, e.column, false, true, e.record);
56464     },
56465
56466         //private
56467     onRowUpdated : function(v, index, r){
56468         if(this.selection && this.selection.record == r){
56469             v.onCellSelect(index, this.selection.cell[1]);
56470         }
56471     },
56472
56473         //private
56474     onViewChange : function(){
56475         this.clearSelections(true);
56476     },
56477
56478         /**
56479          * Returns the currently selected cell,.
56480          * @return {Array} The selected cell (row, column) or null if none selected.
56481          */
56482     getSelectedCell : function(){
56483         return this.selection ? this.selection.cell : null;
56484     },
56485
56486     /**
56487      * Clears all selections.
56488      * @param {Boolean} true to prevent the gridview from being notified about the change.
56489      */
56490     clearSelections : function(preventNotify){
56491         var s = this.selection;
56492         if(s){
56493             if(preventNotify !== true){
56494                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
56495             }
56496             this.selection = null;
56497             this.fireEvent("selectionchange", this, null);
56498         }
56499     },
56500
56501     /**
56502      * Returns true if there is a selection.
56503      * @return {Boolean}
56504      */
56505     hasSelection : function(){
56506         return this.selection ? true : false;
56507     },
56508
56509     /** @ignore */
56510     handleMouseDown : function(e, t){
56511         var v = this.grid.getView();
56512         if(this.isLocked()){
56513             return;
56514         };
56515         var row = v.findRowIndex(t);
56516         var cell = v.findCellIndex(t);
56517         if(row !== false && cell !== false){
56518             this.select(row, cell);
56519         }
56520     },
56521
56522     /**
56523      * Selects a cell.
56524      * @param {Number} rowIndex
56525      * @param {Number} collIndex
56526      */
56527     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
56528         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
56529             this.clearSelections();
56530             r = r || this.grid.dataSource.getAt(rowIndex);
56531             this.selection = {
56532                 record : r,
56533                 cell : [rowIndex, colIndex]
56534             };
56535             if(!preventViewNotify){
56536                 var v = this.grid.getView();
56537                 v.onCellSelect(rowIndex, colIndex);
56538                 if(preventFocus !== true){
56539                     v.focusCell(rowIndex, colIndex);
56540                 }
56541             }
56542             this.fireEvent("cellselect", this, rowIndex, colIndex);
56543             this.fireEvent("selectionchange", this, this.selection);
56544         }
56545     },
56546
56547         //private
56548     isSelectable : function(rowIndex, colIndex, cm){
56549         return !cm.isHidden(colIndex);
56550     },
56551
56552     /** @ignore */
56553     handleKeyDown : function(e){
56554         //Roo.log('Cell Sel Model handleKeyDown');
56555         if(!e.isNavKeyPress()){
56556             return;
56557         }
56558         var g = this.grid, s = this.selection;
56559         if(!s){
56560             e.stopEvent();
56561             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
56562             if(cell){
56563                 this.select(cell[0], cell[1]);
56564             }
56565             return;
56566         }
56567         var sm = this;
56568         var walk = function(row, col, step){
56569             return g.walkCells(row, col, step, sm.isSelectable,  sm);
56570         };
56571         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
56572         var newCell;
56573
56574       
56575
56576         switch(k){
56577             case e.TAB:
56578                 // handled by onEditorKey
56579                 if (g.isEditor && g.editing) {
56580                     return;
56581                 }
56582                 if(e.shiftKey) {
56583                     newCell = walk(r, c-1, -1);
56584                 } else {
56585                     newCell = walk(r, c+1, 1);
56586                 }
56587                 break;
56588             
56589             case e.DOWN:
56590                newCell = walk(r+1, c, 1);
56591                 break;
56592             
56593             case e.UP:
56594                 newCell = walk(r-1, c, -1);
56595                 break;
56596             
56597             case e.RIGHT:
56598                 newCell = walk(r, c+1, 1);
56599                 break;
56600             
56601             case e.LEFT:
56602                 newCell = walk(r, c-1, -1);
56603                 break;
56604             
56605             case e.ENTER:
56606                 
56607                 if(g.isEditor && !g.editing){
56608                    g.startEditing(r, c);
56609                    e.stopEvent();
56610                    return;
56611                 }
56612                 
56613                 
56614              break;
56615         };
56616         if(newCell){
56617             this.select(newCell[0], newCell[1]);
56618             e.stopEvent();
56619             
56620         }
56621     },
56622
56623     acceptsNav : function(row, col, cm){
56624         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56625     },
56626     /**
56627      * Selects a cell.
56628      * @param {Number} field (not used) - as it's normally used as a listener
56629      * @param {Number} e - event - fake it by using
56630      *
56631      * var e = Roo.EventObjectImpl.prototype;
56632      * e.keyCode = e.TAB
56633      *
56634      * 
56635      */
56636     onEditorKey : function(field, e){
56637         
56638         var k = e.getKey(),
56639             newCell,
56640             g = this.grid,
56641             ed = g.activeEditor,
56642             forward = false;
56643         ///Roo.log('onEditorKey' + k);
56644         
56645         
56646         if (this.enter_is_tab && k == e.ENTER) {
56647             k = e.TAB;
56648         }
56649         
56650         if(k == e.TAB){
56651             if(e.shiftKey){
56652                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56653             }else{
56654                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56655                 forward = true;
56656             }
56657             
56658             e.stopEvent();
56659             
56660         } else if(k == e.ENTER &&  !e.ctrlKey){
56661             ed.completeEdit();
56662             e.stopEvent();
56663             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56664         
56665                 } else if(k == e.ESC){
56666             ed.cancelEdit();
56667         }
56668                 
56669         if (newCell) {
56670             var ecall = { cell : newCell, forward : forward };
56671             this.fireEvent('beforeeditnext', ecall );
56672             newCell = ecall.cell;
56673                         forward = ecall.forward;
56674         }
56675                 
56676         if(newCell){
56677             //Roo.log('next cell after edit');
56678             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
56679         } else if (forward) {
56680             // tabbed past last
56681             this.fireEvent.defer(100, this, ['tabend',this]);
56682         }
56683     }
56684 });/*
56685  * Based on:
56686  * Ext JS Library 1.1.1
56687  * Copyright(c) 2006-2007, Ext JS, LLC.
56688  *
56689  * Originally Released Under LGPL - original licence link has changed is not relivant.
56690  *
56691  * Fork - LGPL
56692  * <script type="text/javascript">
56693  */
56694  
56695 /**
56696  * @class Roo.grid.EditorGrid
56697  * @extends Roo.grid.Grid
56698  * Class for creating and editable grid.
56699  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
56700  * The container MUST have some type of size defined for the grid to fill. The container will be 
56701  * automatically set to position relative if it isn't already.
56702  * @param {Object} dataSource The data model to bind to
56703  * @param {Object} colModel The column model with info about this grid's columns
56704  */
56705 Roo.grid.EditorGrid = function(container, config){
56706     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
56707     this.getGridEl().addClass("xedit-grid");
56708
56709     if(!this.selModel){
56710         this.selModel = new Roo.grid.CellSelectionModel();
56711     }
56712
56713     this.activeEditor = null;
56714
56715         this.addEvents({
56716             /**
56717              * @event beforeedit
56718              * Fires before cell editing is triggered. The edit event object has the following properties <br />
56719              * <ul style="padding:5px;padding-left:16px;">
56720              * <li>grid - This grid</li>
56721              * <li>record - The record being edited</li>
56722              * <li>field - The field name being edited</li>
56723              * <li>value - The value for the field being edited.</li>
56724              * <li>row - The grid row index</li>
56725              * <li>column - The grid column index</li>
56726              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56727              * </ul>
56728              * @param {Object} e An edit event (see above for description)
56729              */
56730             "beforeedit" : true,
56731             /**
56732              * @event afteredit
56733              * Fires after a cell is edited. <br />
56734              * <ul style="padding:5px;padding-left:16px;">
56735              * <li>grid - This grid</li>
56736              * <li>record - The record being edited</li>
56737              * <li>field - The field name being edited</li>
56738              * <li>value - The value being set</li>
56739              * <li>originalValue - The original value for the field, before the edit.</li>
56740              * <li>row - The grid row index</li>
56741              * <li>column - The grid column index</li>
56742              * </ul>
56743              * @param {Object} e An edit event (see above for description)
56744              */
56745             "afteredit" : true,
56746             /**
56747              * @event validateedit
56748              * Fires after a cell is edited, but before the value is set in the record. 
56749          * You can use this to modify the value being set in the field, Return false
56750              * to cancel the change. The edit event object has the following properties <br />
56751              * <ul style="padding:5px;padding-left:16px;">
56752          * <li>editor - This editor</li>
56753              * <li>grid - This grid</li>
56754              * <li>record - The record being edited</li>
56755              * <li>field - The field name being edited</li>
56756              * <li>value - The value being set</li>
56757              * <li>originalValue - The original value for the field, before the edit.</li>
56758              * <li>row - The grid row index</li>
56759              * <li>column - The grid column index</li>
56760              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56761              * </ul>
56762              * @param {Object} e An edit event (see above for description)
56763              */
56764             "validateedit" : true
56765         });
56766     this.on("bodyscroll", this.stopEditing,  this);
56767     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56768 };
56769
56770 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56771     /**
56772      * @cfg {Number} clicksToEdit
56773      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56774      */
56775     clicksToEdit: 2,
56776
56777     // private
56778     isEditor : true,
56779     // private
56780     trackMouseOver: false, // causes very odd FF errors
56781
56782     onCellDblClick : function(g, row, col){
56783         this.startEditing(row, col);
56784     },
56785
56786     onEditComplete : function(ed, value, startValue){
56787         this.editing = false;
56788         this.activeEditor = null;
56789         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56790         var r = ed.record;
56791         var field = this.colModel.getDataIndex(ed.col);
56792         var e = {
56793             grid: this,
56794             record: r,
56795             field: field,
56796             originalValue: startValue,
56797             value: value,
56798             row: ed.row,
56799             column: ed.col,
56800             cancel:false,
56801             editor: ed
56802         };
56803         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
56804         cell.show();
56805           
56806         if(String(value) !== String(startValue)){
56807             
56808             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56809                 r.set(field, e.value);
56810                 // if we are dealing with a combo box..
56811                 // then we also set the 'name' colum to be the displayField
56812                 if (ed.field.displayField && ed.field.name) {
56813                     r.set(ed.field.name, ed.field.el.dom.value);
56814                 }
56815                 
56816                 delete e.cancel; //?? why!!!
56817                 this.fireEvent("afteredit", e);
56818             }
56819         } else {
56820             this.fireEvent("afteredit", e); // always fire it!
56821         }
56822         this.view.focusCell(ed.row, ed.col);
56823     },
56824
56825     /**
56826      * Starts editing the specified for the specified row/column
56827      * @param {Number} rowIndex
56828      * @param {Number} colIndex
56829      */
56830     startEditing : function(row, col){
56831         this.stopEditing();
56832         if(this.colModel.isCellEditable(col, row)){
56833             this.view.ensureVisible(row, col, true);
56834           
56835             var r = this.dataSource.getAt(row);
56836             var field = this.colModel.getDataIndex(col);
56837             var cell = Roo.get(this.view.getCell(row,col));
56838             var e = {
56839                 grid: this,
56840                 record: r,
56841                 field: field,
56842                 value: r.data[field],
56843                 row: row,
56844                 column: col,
56845                 cancel:false 
56846             };
56847             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56848                 this.editing = true;
56849                 var ed = this.colModel.getCellEditor(col, row);
56850                 
56851                 if (!ed) {
56852                     return;
56853                 }
56854                 if(!ed.rendered){
56855                     ed.render(ed.parentEl || document.body);
56856                 }
56857                 ed.field.reset();
56858                
56859                 cell.hide();
56860                 
56861                 (function(){ // complex but required for focus issues in safari, ie and opera
56862                     ed.row = row;
56863                     ed.col = col;
56864                     ed.record = r;
56865                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56866                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56867                     this.activeEditor = ed;
56868                     var v = r.data[field];
56869                     ed.startEdit(this.view.getCell(row, col), v);
56870                     // combo's with 'displayField and name set
56871                     if (ed.field.displayField && ed.field.name) {
56872                         ed.field.el.dom.value = r.data[ed.field.name];
56873                     }
56874                     
56875                     
56876                 }).defer(50, this);
56877             }
56878         }
56879     },
56880         
56881     /**
56882      * Stops any active editing
56883      */
56884     stopEditing : function(){
56885         if(this.activeEditor){
56886             this.activeEditor.completeEdit();
56887         }
56888         this.activeEditor = null;
56889     },
56890         
56891          /**
56892      * Called to get grid's drag proxy text, by default returns this.ddText.
56893      * @return {String}
56894      */
56895     getDragDropText : function(){
56896         var count = this.selModel.getSelectedCell() ? 1 : 0;
56897         return String.format(this.ddText, count, count == 1 ? '' : 's');
56898     }
56899         
56900 });/*
56901  * Based on:
56902  * Ext JS Library 1.1.1
56903  * Copyright(c) 2006-2007, Ext JS, LLC.
56904  *
56905  * Originally Released Under LGPL - original licence link has changed is not relivant.
56906  *
56907  * Fork - LGPL
56908  * <script type="text/javascript">
56909  */
56910
56911 // private - not really -- you end up using it !
56912 // This is a support class used internally by the Grid components
56913
56914 /**
56915  * @class Roo.grid.GridEditor
56916  * @extends Roo.Editor
56917  * Class for creating and editable grid elements.
56918  * @param {Object} config any settings (must include field)
56919  */
56920 Roo.grid.GridEditor = function(field, config){
56921     if (!config && field.field) {
56922         config = field;
56923         field = Roo.factory(config.field, Roo.form);
56924     }
56925     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56926     field.monitorTab = false;
56927 };
56928
56929 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56930     
56931     /**
56932      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56933      */
56934     
56935     alignment: "tl-tl",
56936     autoSize: "width",
56937     hideEl : false,
56938     cls: "x-small-editor x-grid-editor",
56939     shim:false,
56940     shadow:"frame"
56941 });/*
56942  * Based on:
56943  * Ext JS Library 1.1.1
56944  * Copyright(c) 2006-2007, Ext JS, LLC.
56945  *
56946  * Originally Released Under LGPL - original licence link has changed is not relivant.
56947  *
56948  * Fork - LGPL
56949  * <script type="text/javascript">
56950  */
56951   
56952
56953   
56954 Roo.grid.PropertyRecord = Roo.data.Record.create([
56955     {name:'name',type:'string'},  'value'
56956 ]);
56957
56958
56959 Roo.grid.PropertyStore = function(grid, source){
56960     this.grid = grid;
56961     this.store = new Roo.data.Store({
56962         recordType : Roo.grid.PropertyRecord
56963     });
56964     this.store.on('update', this.onUpdate,  this);
56965     if(source){
56966         this.setSource(source);
56967     }
56968     Roo.grid.PropertyStore.superclass.constructor.call(this);
56969 };
56970
56971
56972
56973 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56974     setSource : function(o){
56975         this.source = o;
56976         this.store.removeAll();
56977         var data = [];
56978         for(var k in o){
56979             if(this.isEditableValue(o[k])){
56980                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
56981             }
56982         }
56983         this.store.loadRecords({records: data}, {}, true);
56984     },
56985
56986     onUpdate : function(ds, record, type){
56987         if(type == Roo.data.Record.EDIT){
56988             var v = record.data['value'];
56989             var oldValue = record.modified['value'];
56990             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
56991                 this.source[record.id] = v;
56992                 record.commit();
56993                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
56994             }else{
56995                 record.reject();
56996             }
56997         }
56998     },
56999
57000     getProperty : function(row){
57001        return this.store.getAt(row);
57002     },
57003
57004     isEditableValue: function(val){
57005         if(val && val instanceof Date){
57006             return true;
57007         }else if(typeof val == 'object' || typeof val == 'function'){
57008             return false;
57009         }
57010         return true;
57011     },
57012
57013     setValue : function(prop, value){
57014         this.source[prop] = value;
57015         this.store.getById(prop).set('value', value);
57016     },
57017
57018     getSource : function(){
57019         return this.source;
57020     }
57021 });
57022
57023 Roo.grid.PropertyColumnModel = function(grid, store){
57024     this.grid = grid;
57025     var g = Roo.grid;
57026     g.PropertyColumnModel.superclass.constructor.call(this, [
57027         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
57028         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
57029     ]);
57030     this.store = store;
57031     this.bselect = Roo.DomHelper.append(document.body, {
57032         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
57033             {tag: 'option', value: 'true', html: 'true'},
57034             {tag: 'option', value: 'false', html: 'false'}
57035         ]
57036     });
57037     Roo.id(this.bselect);
57038     var f = Roo.form;
57039     this.editors = {
57040         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
57041         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
57042         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
57043         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
57044         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
57045     };
57046     this.renderCellDelegate = this.renderCell.createDelegate(this);
57047     this.renderPropDelegate = this.renderProp.createDelegate(this);
57048 };
57049
57050 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
57051     
57052     
57053     nameText : 'Name',
57054     valueText : 'Value',
57055     
57056     dateFormat : 'm/j/Y',
57057     
57058     
57059     renderDate : function(dateVal){
57060         return dateVal.dateFormat(this.dateFormat);
57061     },
57062
57063     renderBool : function(bVal){
57064         return bVal ? 'true' : 'false';
57065     },
57066
57067     isCellEditable : function(colIndex, rowIndex){
57068         return colIndex == 1;
57069     },
57070
57071     getRenderer : function(col){
57072         return col == 1 ?
57073             this.renderCellDelegate : this.renderPropDelegate;
57074     },
57075
57076     renderProp : function(v){
57077         return this.getPropertyName(v);
57078     },
57079
57080     renderCell : function(val){
57081         var rv = val;
57082         if(val instanceof Date){
57083             rv = this.renderDate(val);
57084         }else if(typeof val == 'boolean'){
57085             rv = this.renderBool(val);
57086         }
57087         return Roo.util.Format.htmlEncode(rv);
57088     },
57089
57090     getPropertyName : function(name){
57091         var pn = this.grid.propertyNames;
57092         return pn && pn[name] ? pn[name] : name;
57093     },
57094
57095     getCellEditor : function(colIndex, rowIndex){
57096         var p = this.store.getProperty(rowIndex);
57097         var n = p.data['name'], val = p.data['value'];
57098         
57099         if(typeof(this.grid.customEditors[n]) == 'string'){
57100             return this.editors[this.grid.customEditors[n]];
57101         }
57102         if(typeof(this.grid.customEditors[n]) != 'undefined'){
57103             return this.grid.customEditors[n];
57104         }
57105         if(val instanceof Date){
57106             return this.editors['date'];
57107         }else if(typeof val == 'number'){
57108             return this.editors['number'];
57109         }else if(typeof val == 'boolean'){
57110             return this.editors['boolean'];
57111         }else{
57112             return this.editors['string'];
57113         }
57114     }
57115 });
57116
57117 /**
57118  * @class Roo.grid.PropertyGrid
57119  * @extends Roo.grid.EditorGrid
57120  * This class represents the  interface of a component based property grid control.
57121  * <br><br>Usage:<pre><code>
57122  var grid = new Roo.grid.PropertyGrid("my-container-id", {
57123       
57124  });
57125  // set any options
57126  grid.render();
57127  * </code></pre>
57128   
57129  * @constructor
57130  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57131  * The container MUST have some type of size defined for the grid to fill. The container will be
57132  * automatically set to position relative if it isn't already.
57133  * @param {Object} config A config object that sets properties on this grid.
57134  */
57135 Roo.grid.PropertyGrid = function(container, config){
57136     config = config || {};
57137     var store = new Roo.grid.PropertyStore(this);
57138     this.store = store;
57139     var cm = new Roo.grid.PropertyColumnModel(this, store);
57140     store.store.sort('name', 'ASC');
57141     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
57142         ds: store.store,
57143         cm: cm,
57144         enableColLock:false,
57145         enableColumnMove:false,
57146         stripeRows:false,
57147         trackMouseOver: false,
57148         clicksToEdit:1
57149     }, config));
57150     this.getGridEl().addClass('x-props-grid');
57151     this.lastEditRow = null;
57152     this.on('columnresize', this.onColumnResize, this);
57153     this.addEvents({
57154          /**
57155              * @event beforepropertychange
57156              * Fires before a property changes (return false to stop?)
57157              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57158              * @param {String} id Record Id
57159              * @param {String} newval New Value
57160          * @param {String} oldval Old Value
57161              */
57162         "beforepropertychange": true,
57163         /**
57164              * @event propertychange
57165              * Fires after a property changes
57166              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57167              * @param {String} id Record Id
57168              * @param {String} newval New Value
57169          * @param {String} oldval Old Value
57170              */
57171         "propertychange": true
57172     });
57173     this.customEditors = this.customEditors || {};
57174 };
57175 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
57176     
57177      /**
57178      * @cfg {Object} customEditors map of colnames=> custom editors.
57179      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
57180      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
57181      * false disables editing of the field.
57182          */
57183     
57184       /**
57185      * @cfg {Object} propertyNames map of property Names to their displayed value
57186          */
57187     
57188     render : function(){
57189         Roo.grid.PropertyGrid.superclass.render.call(this);
57190         this.autoSize.defer(100, this);
57191     },
57192
57193     autoSize : function(){
57194         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
57195         if(this.view){
57196             this.view.fitColumns();
57197         }
57198     },
57199
57200     onColumnResize : function(){
57201         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
57202         this.autoSize();
57203     },
57204     /**
57205      * Sets the data for the Grid
57206      * accepts a Key => Value object of all the elements avaiable.
57207      * @param {Object} data  to appear in grid.
57208      */
57209     setSource : function(source){
57210         this.store.setSource(source);
57211         //this.autoSize();
57212     },
57213     /**
57214      * Gets all the data from the grid.
57215      * @return {Object} data  data stored in grid
57216      */
57217     getSource : function(){
57218         return this.store.getSource();
57219     }
57220 });/*
57221   
57222  * Licence LGPL
57223  
57224  */
57225  
57226 /**
57227  * @class Roo.grid.Calendar
57228  * @extends Roo.util.Grid
57229  * This class extends the Grid to provide a calendar widget
57230  * <br><br>Usage:<pre><code>
57231  var grid = new Roo.grid.Calendar("my-container-id", {
57232      ds: myDataStore,
57233      cm: myColModel,
57234      selModel: mySelectionModel,
57235      autoSizeColumns: true,
57236      monitorWindowResize: false,
57237      trackMouseOver: true
57238      eventstore : real data store..
57239  });
57240  // set any options
57241  grid.render();
57242   
57243   * @constructor
57244  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57245  * The container MUST have some type of size defined for the grid to fill. The container will be
57246  * automatically set to position relative if it isn't already.
57247  * @param {Object} config A config object that sets properties on this grid.
57248  */
57249 Roo.grid.Calendar = function(container, config){
57250         // initialize the container
57251         this.container = Roo.get(container);
57252         this.container.update("");
57253         this.container.setStyle("overflow", "hidden");
57254     this.container.addClass('x-grid-container');
57255
57256     this.id = this.container.id;
57257
57258     Roo.apply(this, config);
57259     // check and correct shorthanded configs
57260     
57261     var rows = [];
57262     var d =1;
57263     for (var r = 0;r < 6;r++) {
57264         
57265         rows[r]=[];
57266         for (var c =0;c < 7;c++) {
57267             rows[r][c]= '';
57268         }
57269     }
57270     if (this.eventStore) {
57271         this.eventStore= Roo.factory(this.eventStore, Roo.data);
57272         this.eventStore.on('load',this.onLoad, this);
57273         this.eventStore.on('beforeload',this.clearEvents, this);
57274          
57275     }
57276     
57277     this.dataSource = new Roo.data.Store({
57278             proxy: new Roo.data.MemoryProxy(rows),
57279             reader: new Roo.data.ArrayReader({}, [
57280                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
57281     });
57282
57283     this.dataSource.load();
57284     this.ds = this.dataSource;
57285     this.ds.xmodule = this.xmodule || false;
57286     
57287     
57288     var cellRender = function(v,x,r)
57289     {
57290         return String.format(
57291             '<div class="fc-day  fc-widget-content"><div>' +
57292                 '<div class="fc-event-container"></div>' +
57293                 '<div class="fc-day-number">{0}</div>'+
57294                 
57295                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
57296             '</div></div>', v);
57297     
57298     }
57299     
57300     
57301     this.colModel = new Roo.grid.ColumnModel( [
57302         {
57303             xtype: 'ColumnModel',
57304             xns: Roo.grid,
57305             dataIndex : 'weekday0',
57306             header : 'Sunday',
57307             renderer : cellRender
57308         },
57309         {
57310             xtype: 'ColumnModel',
57311             xns: Roo.grid,
57312             dataIndex : 'weekday1',
57313             header : 'Monday',
57314             renderer : cellRender
57315         },
57316         {
57317             xtype: 'ColumnModel',
57318             xns: Roo.grid,
57319             dataIndex : 'weekday2',
57320             header : 'Tuesday',
57321             renderer : cellRender
57322         },
57323         {
57324             xtype: 'ColumnModel',
57325             xns: Roo.grid,
57326             dataIndex : 'weekday3',
57327             header : 'Wednesday',
57328             renderer : cellRender
57329         },
57330         {
57331             xtype: 'ColumnModel',
57332             xns: Roo.grid,
57333             dataIndex : 'weekday4',
57334             header : 'Thursday',
57335             renderer : cellRender
57336         },
57337         {
57338             xtype: 'ColumnModel',
57339             xns: Roo.grid,
57340             dataIndex : 'weekday5',
57341             header : 'Friday',
57342             renderer : cellRender
57343         },
57344         {
57345             xtype: 'ColumnModel',
57346             xns: Roo.grid,
57347             dataIndex : 'weekday6',
57348             header : 'Saturday',
57349             renderer : cellRender
57350         }
57351     ]);
57352     this.cm = this.colModel;
57353     this.cm.xmodule = this.xmodule || false;
57354  
57355         
57356           
57357     //this.selModel = new Roo.grid.CellSelectionModel();
57358     //this.sm = this.selModel;
57359     //this.selModel.init(this);
57360     
57361     
57362     if(this.width){
57363         this.container.setWidth(this.width);
57364     }
57365
57366     if(this.height){
57367         this.container.setHeight(this.height);
57368     }
57369     /** @private */
57370         this.addEvents({
57371         // raw events
57372         /**
57373          * @event click
57374          * The raw click event for the entire grid.
57375          * @param {Roo.EventObject} e
57376          */
57377         "click" : true,
57378         /**
57379          * @event dblclick
57380          * The raw dblclick event for the entire grid.
57381          * @param {Roo.EventObject} e
57382          */
57383         "dblclick" : true,
57384         /**
57385          * @event contextmenu
57386          * The raw contextmenu event for the entire grid.
57387          * @param {Roo.EventObject} e
57388          */
57389         "contextmenu" : true,
57390         /**
57391          * @event mousedown
57392          * The raw mousedown event for the entire grid.
57393          * @param {Roo.EventObject} e
57394          */
57395         "mousedown" : true,
57396         /**
57397          * @event mouseup
57398          * The raw mouseup event for the entire grid.
57399          * @param {Roo.EventObject} e
57400          */
57401         "mouseup" : true,
57402         /**
57403          * @event mouseover
57404          * The raw mouseover event for the entire grid.
57405          * @param {Roo.EventObject} e
57406          */
57407         "mouseover" : true,
57408         /**
57409          * @event mouseout
57410          * The raw mouseout event for the entire grid.
57411          * @param {Roo.EventObject} e
57412          */
57413         "mouseout" : true,
57414         /**
57415          * @event keypress
57416          * The raw keypress event for the entire grid.
57417          * @param {Roo.EventObject} e
57418          */
57419         "keypress" : true,
57420         /**
57421          * @event keydown
57422          * The raw keydown event for the entire grid.
57423          * @param {Roo.EventObject} e
57424          */
57425         "keydown" : true,
57426
57427         // custom events
57428
57429         /**
57430          * @event cellclick
57431          * Fires when a cell is clicked
57432          * @param {Grid} this
57433          * @param {Number} rowIndex
57434          * @param {Number} columnIndex
57435          * @param {Roo.EventObject} e
57436          */
57437         "cellclick" : true,
57438         /**
57439          * @event celldblclick
57440          * Fires when a cell is double clicked
57441          * @param {Grid} this
57442          * @param {Number} rowIndex
57443          * @param {Number} columnIndex
57444          * @param {Roo.EventObject} e
57445          */
57446         "celldblclick" : true,
57447         /**
57448          * @event rowclick
57449          * Fires when a row is clicked
57450          * @param {Grid} this
57451          * @param {Number} rowIndex
57452          * @param {Roo.EventObject} e
57453          */
57454         "rowclick" : true,
57455         /**
57456          * @event rowdblclick
57457          * Fires when a row is double clicked
57458          * @param {Grid} this
57459          * @param {Number} rowIndex
57460          * @param {Roo.EventObject} e
57461          */
57462         "rowdblclick" : true,
57463         /**
57464          * @event headerclick
57465          * Fires when a header is clicked
57466          * @param {Grid} this
57467          * @param {Number} columnIndex
57468          * @param {Roo.EventObject} e
57469          */
57470         "headerclick" : true,
57471         /**
57472          * @event headerdblclick
57473          * Fires when a header cell is double clicked
57474          * @param {Grid} this
57475          * @param {Number} columnIndex
57476          * @param {Roo.EventObject} e
57477          */
57478         "headerdblclick" : true,
57479         /**
57480          * @event rowcontextmenu
57481          * Fires when a row is right clicked
57482          * @param {Grid} this
57483          * @param {Number} rowIndex
57484          * @param {Roo.EventObject} e
57485          */
57486         "rowcontextmenu" : true,
57487         /**
57488          * @event cellcontextmenu
57489          * Fires when a cell is right clicked
57490          * @param {Grid} this
57491          * @param {Number} rowIndex
57492          * @param {Number} cellIndex
57493          * @param {Roo.EventObject} e
57494          */
57495          "cellcontextmenu" : true,
57496         /**
57497          * @event headercontextmenu
57498          * Fires when a header is right clicked
57499          * @param {Grid} this
57500          * @param {Number} columnIndex
57501          * @param {Roo.EventObject} e
57502          */
57503         "headercontextmenu" : true,
57504         /**
57505          * @event bodyscroll
57506          * Fires when the body element is scrolled
57507          * @param {Number} scrollLeft
57508          * @param {Number} scrollTop
57509          */
57510         "bodyscroll" : true,
57511         /**
57512          * @event columnresize
57513          * Fires when the user resizes a column
57514          * @param {Number} columnIndex
57515          * @param {Number} newSize
57516          */
57517         "columnresize" : true,
57518         /**
57519          * @event columnmove
57520          * Fires when the user moves a column
57521          * @param {Number} oldIndex
57522          * @param {Number} newIndex
57523          */
57524         "columnmove" : true,
57525         /**
57526          * @event startdrag
57527          * Fires when row(s) start being dragged
57528          * @param {Grid} this
57529          * @param {Roo.GridDD} dd The drag drop object
57530          * @param {event} e The raw browser event
57531          */
57532         "startdrag" : true,
57533         /**
57534          * @event enddrag
57535          * Fires when a drag operation is complete
57536          * @param {Grid} this
57537          * @param {Roo.GridDD} dd The drag drop object
57538          * @param {event} e The raw browser event
57539          */
57540         "enddrag" : true,
57541         /**
57542          * @event dragdrop
57543          * Fires when dragged row(s) are dropped on a valid DD target
57544          * @param {Grid} this
57545          * @param {Roo.GridDD} dd The drag drop object
57546          * @param {String} targetId The target drag drop object
57547          * @param {event} e The raw browser event
57548          */
57549         "dragdrop" : true,
57550         /**
57551          * @event dragover
57552          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57553          * @param {Grid} this
57554          * @param {Roo.GridDD} dd The drag drop object
57555          * @param {String} targetId The target drag drop object
57556          * @param {event} e The raw browser event
57557          */
57558         "dragover" : true,
57559         /**
57560          * @event dragenter
57561          *  Fires when the dragged row(s) first cross another DD target while being dragged
57562          * @param {Grid} this
57563          * @param {Roo.GridDD} dd The drag drop object
57564          * @param {String} targetId The target drag drop object
57565          * @param {event} e The raw browser event
57566          */
57567         "dragenter" : true,
57568         /**
57569          * @event dragout
57570          * Fires when the dragged row(s) leave another DD target while being dragged
57571          * @param {Grid} this
57572          * @param {Roo.GridDD} dd The drag drop object
57573          * @param {String} targetId The target drag drop object
57574          * @param {event} e The raw browser event
57575          */
57576         "dragout" : true,
57577         /**
57578          * @event rowclass
57579          * Fires when a row is rendered, so you can change add a style to it.
57580          * @param {GridView} gridview   The grid view
57581          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57582          */
57583         'rowclass' : true,
57584
57585         /**
57586          * @event render
57587          * Fires when the grid is rendered
57588          * @param {Grid} grid
57589          */
57590         'render' : true,
57591             /**
57592              * @event select
57593              * Fires when a date is selected
57594              * @param {DatePicker} this
57595              * @param {Date} date The selected date
57596              */
57597         'select': true,
57598         /**
57599              * @event monthchange
57600              * Fires when the displayed month changes 
57601              * @param {DatePicker} this
57602              * @param {Date} date The selected month
57603              */
57604         'monthchange': true,
57605         /**
57606              * @event evententer
57607              * Fires when mouse over an event
57608              * @param {Calendar} this
57609              * @param {event} Event
57610              */
57611         'evententer': true,
57612         /**
57613              * @event eventleave
57614              * Fires when the mouse leaves an
57615              * @param {Calendar} this
57616              * @param {event}
57617              */
57618         'eventleave': true,
57619         /**
57620              * @event eventclick
57621              * Fires when the mouse click an
57622              * @param {Calendar} this
57623              * @param {event}
57624              */
57625         'eventclick': true,
57626         /**
57627              * @event eventrender
57628              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
57629              * @param {Calendar} this
57630              * @param {data} data to be modified
57631              */
57632         'eventrender': true
57633         
57634     });
57635
57636     Roo.grid.Grid.superclass.constructor.call(this);
57637     this.on('render', function() {
57638         this.view.el.addClass('x-grid-cal'); 
57639         
57640         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
57641
57642     },this);
57643     
57644     if (!Roo.grid.Calendar.style) {
57645         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
57646             
57647             
57648             '.x-grid-cal .x-grid-col' :  {
57649                 height: 'auto !important',
57650                 'vertical-align': 'top'
57651             },
57652             '.x-grid-cal  .fc-event-hori' : {
57653                 height: '14px'
57654             }
57655              
57656             
57657         }, Roo.id());
57658     }
57659
57660     
57661     
57662 };
57663 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
57664     /**
57665      * @cfg {Store} eventStore The store that loads events.
57666      */
57667     eventStore : 25,
57668
57669      
57670     activeDate : false,
57671     startDay : 0,
57672     autoWidth : true,
57673     monitorWindowResize : false,
57674
57675     
57676     resizeColumns : function() {
57677         var col = (this.view.el.getWidth() / 7) - 3;
57678         // loop through cols, and setWidth
57679         for(var i =0 ; i < 7 ; i++){
57680             this.cm.setColumnWidth(i, col);
57681         }
57682     },
57683      setDate :function(date) {
57684         
57685         Roo.log('setDate?');
57686         
57687         this.resizeColumns();
57688         var vd = this.activeDate;
57689         this.activeDate = date;
57690 //        if(vd && this.el){
57691 //            var t = date.getTime();
57692 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
57693 //                Roo.log('using add remove');
57694 //                
57695 //                this.fireEvent('monthchange', this, date);
57696 //                
57697 //                this.cells.removeClass("fc-state-highlight");
57698 //                this.cells.each(function(c){
57699 //                   if(c.dateValue == t){
57700 //                       c.addClass("fc-state-highlight");
57701 //                       setTimeout(function(){
57702 //                            try{c.dom.firstChild.focus();}catch(e){}
57703 //                       }, 50);
57704 //                       return false;
57705 //                   }
57706 //                   return true;
57707 //                });
57708 //                return;
57709 //            }
57710 //        }
57711         
57712         var days = date.getDaysInMonth();
57713         
57714         var firstOfMonth = date.getFirstDateOfMonth();
57715         var startingPos = firstOfMonth.getDay()-this.startDay;
57716         
57717         if(startingPos < this.startDay){
57718             startingPos += 7;
57719         }
57720         
57721         var pm = date.add(Date.MONTH, -1);
57722         var prevStart = pm.getDaysInMonth()-startingPos;
57723 //        
57724         
57725         
57726         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57727         
57728         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57729         //this.cells.addClassOnOver('fc-state-hover');
57730         
57731         var cells = this.cells.elements;
57732         var textEls = this.textNodes;
57733         
57734         //Roo.each(cells, function(cell){
57735         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57736         //});
57737         
57738         days += startingPos;
57739
57740         // convert everything to numbers so it's fast
57741         var day = 86400000;
57742         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57743         //Roo.log(d);
57744         //Roo.log(pm);
57745         //Roo.log(prevStart);
57746         
57747         var today = new Date().clearTime().getTime();
57748         var sel = date.clearTime().getTime();
57749         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57750         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57751         var ddMatch = this.disabledDatesRE;
57752         var ddText = this.disabledDatesText;
57753         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57754         var ddaysText = this.disabledDaysText;
57755         var format = this.format;
57756         
57757         var setCellClass = function(cal, cell){
57758             
57759             //Roo.log('set Cell Class');
57760             cell.title = "";
57761             var t = d.getTime();
57762             
57763             //Roo.log(d);
57764             
57765             
57766             cell.dateValue = t;
57767             if(t == today){
57768                 cell.className += " fc-today";
57769                 cell.className += " fc-state-highlight";
57770                 cell.title = cal.todayText;
57771             }
57772             if(t == sel){
57773                 // disable highlight in other month..
57774                 cell.className += " fc-state-highlight";
57775                 
57776             }
57777             // disabling
57778             if(t < min) {
57779                 //cell.className = " fc-state-disabled";
57780                 cell.title = cal.minText;
57781                 return;
57782             }
57783             if(t > max) {
57784                 //cell.className = " fc-state-disabled";
57785                 cell.title = cal.maxText;
57786                 return;
57787             }
57788             if(ddays){
57789                 if(ddays.indexOf(d.getDay()) != -1){
57790                     // cell.title = ddaysText;
57791                    // cell.className = " fc-state-disabled";
57792                 }
57793             }
57794             if(ddMatch && format){
57795                 var fvalue = d.dateFormat(format);
57796                 if(ddMatch.test(fvalue)){
57797                     cell.title = ddText.replace("%0", fvalue);
57798                    cell.className = " fc-state-disabled";
57799                 }
57800             }
57801             
57802             if (!cell.initialClassName) {
57803                 cell.initialClassName = cell.dom.className;
57804             }
57805             
57806             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57807         };
57808
57809         var i = 0;
57810         
57811         for(; i < startingPos; i++) {
57812             cells[i].dayName =  (++prevStart);
57813             Roo.log(textEls[i]);
57814             d.setDate(d.getDate()+1);
57815             
57816             //cells[i].className = "fc-past fc-other-month";
57817             setCellClass(this, cells[i]);
57818         }
57819         
57820         var intDay = 0;
57821         
57822         for(; i < days; i++){
57823             intDay = i - startingPos + 1;
57824             cells[i].dayName =  (intDay);
57825             d.setDate(d.getDate()+1);
57826             
57827             cells[i].className = ''; // "x-date-active";
57828             setCellClass(this, cells[i]);
57829         }
57830         var extraDays = 0;
57831         
57832         for(; i < 42; i++) {
57833             //textEls[i].innerHTML = (++extraDays);
57834             
57835             d.setDate(d.getDate()+1);
57836             cells[i].dayName = (++extraDays);
57837             cells[i].className = "fc-future fc-other-month";
57838             setCellClass(this, cells[i]);
57839         }
57840         
57841         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57842         
57843         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57844         
57845         // this will cause all the cells to mis
57846         var rows= [];
57847         var i =0;
57848         for (var r = 0;r < 6;r++) {
57849             for (var c =0;c < 7;c++) {
57850                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57851             }    
57852         }
57853         
57854         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57855         for(i=0;i<cells.length;i++) {
57856             
57857             this.cells.elements[i].dayName = cells[i].dayName ;
57858             this.cells.elements[i].className = cells[i].className;
57859             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57860             this.cells.elements[i].title = cells[i].title ;
57861             this.cells.elements[i].dateValue = cells[i].dateValue ;
57862         }
57863         
57864         
57865         
57866         
57867         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57868         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57869         
57870         ////if(totalRows != 6){
57871             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57872            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57873        // }
57874         
57875         this.fireEvent('monthchange', this, date);
57876         
57877         
57878     },
57879  /**
57880      * Returns the grid's SelectionModel.
57881      * @return {SelectionModel}
57882      */
57883     getSelectionModel : function(){
57884         if(!this.selModel){
57885             this.selModel = new Roo.grid.CellSelectionModel();
57886         }
57887         return this.selModel;
57888     },
57889
57890     load: function() {
57891         this.eventStore.load()
57892         
57893         
57894         
57895     },
57896     
57897     findCell : function(dt) {
57898         dt = dt.clearTime().getTime();
57899         var ret = false;
57900         this.cells.each(function(c){
57901             //Roo.log("check " +c.dateValue + '?=' + dt);
57902             if(c.dateValue == dt){
57903                 ret = c;
57904                 return false;
57905             }
57906             return true;
57907         });
57908         
57909         return ret;
57910     },
57911     
57912     findCells : function(rec) {
57913         var s = rec.data.start_dt.clone().clearTime().getTime();
57914        // Roo.log(s);
57915         var e= rec.data.end_dt.clone().clearTime().getTime();
57916        // Roo.log(e);
57917         var ret = [];
57918         this.cells.each(function(c){
57919              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57920             
57921             if(c.dateValue > e){
57922                 return ;
57923             }
57924             if(c.dateValue < s){
57925                 return ;
57926             }
57927             ret.push(c);
57928         });
57929         
57930         return ret;    
57931     },
57932     
57933     findBestRow: function(cells)
57934     {
57935         var ret = 0;
57936         
57937         for (var i =0 ; i < cells.length;i++) {
57938             ret  = Math.max(cells[i].rows || 0,ret);
57939         }
57940         return ret;
57941         
57942     },
57943     
57944     
57945     addItem : function(rec)
57946     {
57947         // look for vertical location slot in
57948         var cells = this.findCells(rec);
57949         
57950         rec.row = this.findBestRow(cells);
57951         
57952         // work out the location.
57953         
57954         var crow = false;
57955         var rows = [];
57956         for(var i =0; i < cells.length; i++) {
57957             if (!crow) {
57958                 crow = {
57959                     start : cells[i],
57960                     end :  cells[i]
57961                 };
57962                 continue;
57963             }
57964             if (crow.start.getY() == cells[i].getY()) {
57965                 // on same row.
57966                 crow.end = cells[i];
57967                 continue;
57968             }
57969             // different row.
57970             rows.push(crow);
57971             crow = {
57972                 start: cells[i],
57973                 end : cells[i]
57974             };
57975             
57976         }
57977         
57978         rows.push(crow);
57979         rec.els = [];
57980         rec.rows = rows;
57981         rec.cells = cells;
57982         for (var i = 0; i < cells.length;i++) {
57983             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
57984             
57985         }
57986         
57987         
57988     },
57989     
57990     clearEvents: function() {
57991         
57992         if (!this.eventStore.getCount()) {
57993             return;
57994         }
57995         // reset number of rows in cells.
57996         Roo.each(this.cells.elements, function(c){
57997             c.rows = 0;
57998         });
57999         
58000         this.eventStore.each(function(e) {
58001             this.clearEvent(e);
58002         },this);
58003         
58004     },
58005     
58006     clearEvent : function(ev)
58007     {
58008         if (ev.els) {
58009             Roo.each(ev.els, function(el) {
58010                 el.un('mouseenter' ,this.onEventEnter, this);
58011                 el.un('mouseleave' ,this.onEventLeave, this);
58012                 el.remove();
58013             },this);
58014             ev.els = [];
58015         }
58016     },
58017     
58018     
58019     renderEvent : function(ev,ctr) {
58020         if (!ctr) {
58021              ctr = this.view.el.select('.fc-event-container',true).first();
58022         }
58023         
58024          
58025         this.clearEvent(ev);
58026             //code
58027        
58028         
58029         
58030         ev.els = [];
58031         var cells = ev.cells;
58032         var rows = ev.rows;
58033         this.fireEvent('eventrender', this, ev);
58034         
58035         for(var i =0; i < rows.length; i++) {
58036             
58037             cls = '';
58038             if (i == 0) {
58039                 cls += ' fc-event-start';
58040             }
58041             if ((i+1) == rows.length) {
58042                 cls += ' fc-event-end';
58043             }
58044             
58045             //Roo.log(ev.data);
58046             // how many rows should it span..
58047             var cg = this.eventTmpl.append(ctr,Roo.apply({
58048                 fccls : cls
58049                 
58050             }, ev.data) , true);
58051             
58052             
58053             cg.on('mouseenter' ,this.onEventEnter, this, ev);
58054             cg.on('mouseleave' ,this.onEventLeave, this, ev);
58055             cg.on('click', this.onEventClick, this, ev);
58056             
58057             ev.els.push(cg);
58058             
58059             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
58060             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
58061             //Roo.log(cg);
58062              
58063             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
58064             cg.setWidth(ebox.right - sbox.x -2);
58065         }
58066     },
58067     
58068     renderEvents: function()
58069     {   
58070         // first make sure there is enough space..
58071         
58072         if (!this.eventTmpl) {
58073             this.eventTmpl = new Roo.Template(
58074                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
58075                     '<div class="fc-event-inner">' +
58076                         '<span class="fc-event-time">{time}</span>' +
58077                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
58078                     '</div>' +
58079                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
58080                 '</div>'
58081             );
58082                 
58083         }
58084                
58085         
58086         
58087         this.cells.each(function(c) {
58088             //Roo.log(c.select('.fc-day-content div',true).first());
58089             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
58090         });
58091         
58092         var ctr = this.view.el.select('.fc-event-container',true).first();
58093         
58094         var cls;
58095         this.eventStore.each(function(ev){
58096             
58097             this.renderEvent(ev);
58098              
58099              
58100         }, this);
58101         this.view.layout();
58102         
58103     },
58104     
58105     onEventEnter: function (e, el,event,d) {
58106         this.fireEvent('evententer', this, el, event);
58107     },
58108     
58109     onEventLeave: function (e, el,event,d) {
58110         this.fireEvent('eventleave', this, el, event);
58111     },
58112     
58113     onEventClick: function (e, el,event,d) {
58114         this.fireEvent('eventclick', this, el, event);
58115     },
58116     
58117     onMonthChange: function () {
58118         this.store.load();
58119     },
58120     
58121     onLoad: function () {
58122         
58123         //Roo.log('calendar onload');
58124 //         
58125         if(this.eventStore.getCount() > 0){
58126             
58127            
58128             
58129             this.eventStore.each(function(d){
58130                 
58131                 
58132                 // FIXME..
58133                 var add =   d.data;
58134                 if (typeof(add.end_dt) == 'undefined')  {
58135                     Roo.log("Missing End time in calendar data: ");
58136                     Roo.log(d);
58137                     return;
58138                 }
58139                 if (typeof(add.start_dt) == 'undefined')  {
58140                     Roo.log("Missing Start time in calendar data: ");
58141                     Roo.log(d);
58142                     return;
58143                 }
58144                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
58145                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
58146                 add.id = add.id || d.id;
58147                 add.title = add.title || '??';
58148                 
58149                 this.addItem(d);
58150                 
58151              
58152             },this);
58153         }
58154         
58155         this.renderEvents();
58156     }
58157     
58158
58159 });
58160 /*
58161  grid : {
58162                 xtype: 'Grid',
58163                 xns: Roo.grid,
58164                 listeners : {
58165                     render : function ()
58166                     {
58167                         _this.grid = this;
58168                         
58169                         if (!this.view.el.hasClass('course-timesheet')) {
58170                             this.view.el.addClass('course-timesheet');
58171                         }
58172                         if (this.tsStyle) {
58173                             this.ds.load({});
58174                             return; 
58175                         }
58176                         Roo.log('width');
58177                         Roo.log(_this.grid.view.el.getWidth());
58178                         
58179                         
58180                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
58181                             '.course-timesheet .x-grid-row' : {
58182                                 height: '80px'
58183                             },
58184                             '.x-grid-row td' : {
58185                                 'vertical-align' : 0
58186                             },
58187                             '.course-edit-link' : {
58188                                 'color' : 'blue',
58189                                 'text-overflow' : 'ellipsis',
58190                                 'overflow' : 'hidden',
58191                                 'white-space' : 'nowrap',
58192                                 'cursor' : 'pointer'
58193                             },
58194                             '.sub-link' : {
58195                                 'color' : 'green'
58196                             },
58197                             '.de-act-sup-link' : {
58198                                 'color' : 'purple',
58199                                 'text-decoration' : 'line-through'
58200                             },
58201                             '.de-act-link' : {
58202                                 'color' : 'red',
58203                                 'text-decoration' : 'line-through'
58204                             },
58205                             '.course-timesheet .course-highlight' : {
58206                                 'border-top-style': 'dashed !important',
58207                                 'border-bottom-bottom': 'dashed !important'
58208                             },
58209                             '.course-timesheet .course-item' : {
58210                                 'font-family'   : 'tahoma, arial, helvetica',
58211                                 'font-size'     : '11px',
58212                                 'overflow'      : 'hidden',
58213                                 'padding-left'  : '10px',
58214                                 'padding-right' : '10px',
58215                                 'padding-top' : '10px' 
58216                             }
58217                             
58218                         }, Roo.id());
58219                                 this.ds.load({});
58220                     }
58221                 },
58222                 autoWidth : true,
58223                 monitorWindowResize : false,
58224                 cellrenderer : function(v,x,r)
58225                 {
58226                     return v;
58227                 },
58228                 sm : {
58229                     xtype: 'CellSelectionModel',
58230                     xns: Roo.grid
58231                 },
58232                 dataSource : {
58233                     xtype: 'Store',
58234                     xns: Roo.data,
58235                     listeners : {
58236                         beforeload : function (_self, options)
58237                         {
58238                             options.params = options.params || {};
58239                             options.params._month = _this.monthField.getValue();
58240                             options.params.limit = 9999;
58241                             options.params['sort'] = 'when_dt';    
58242                             options.params['dir'] = 'ASC';    
58243                             this.proxy.loadResponse = this.loadResponse;
58244                             Roo.log("load?");
58245                             //this.addColumns();
58246                         },
58247                         load : function (_self, records, options)
58248                         {
58249                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
58250                                 // if you click on the translation.. you can edit it...
58251                                 var el = Roo.get(this);
58252                                 var id = el.dom.getAttribute('data-id');
58253                                 var d = el.dom.getAttribute('data-date');
58254                                 var t = el.dom.getAttribute('data-time');
58255                                 //var id = this.child('span').dom.textContent;
58256                                 
58257                                 //Roo.log(this);
58258                                 Pman.Dialog.CourseCalendar.show({
58259                                     id : id,
58260                                     when_d : d,
58261                                     when_t : t,
58262                                     productitem_active : id ? 1 : 0
58263                                 }, function() {
58264                                     _this.grid.ds.load({});
58265                                 });
58266                            
58267                            });
58268                            
58269                            _this.panel.fireEvent('resize', [ '', '' ]);
58270                         }
58271                     },
58272                     loadResponse : function(o, success, response){
58273                             // this is overridden on before load..
58274                             
58275                             Roo.log("our code?");       
58276                             //Roo.log(success);
58277                             //Roo.log(response)
58278                             delete this.activeRequest;
58279                             if(!success){
58280                                 this.fireEvent("loadexception", this, o, response);
58281                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58282                                 return;
58283                             }
58284                             var result;
58285                             try {
58286                                 result = o.reader.read(response);
58287                             }catch(e){
58288                                 Roo.log("load exception?");
58289                                 this.fireEvent("loadexception", this, o, response, e);
58290                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58291                                 return;
58292                             }
58293                             Roo.log("ready...");        
58294                             // loop through result.records;
58295                             // and set this.tdate[date] = [] << array of records..
58296                             _this.tdata  = {};
58297                             Roo.each(result.records, function(r){
58298                                 //Roo.log(r.data);
58299                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
58300                                     _this.tdata[r.data.when_dt.format('j')] = [];
58301                                 }
58302                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
58303                             });
58304                             
58305                             //Roo.log(_this.tdata);
58306                             
58307                             result.records = [];
58308                             result.totalRecords = 6;
58309                     
58310                             // let's generate some duumy records for the rows.
58311                             //var st = _this.dateField.getValue();
58312                             
58313                             // work out monday..
58314                             //st = st.add(Date.DAY, -1 * st.format('w'));
58315                             
58316                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58317                             
58318                             var firstOfMonth = date.getFirstDayOfMonth();
58319                             var days = date.getDaysInMonth();
58320                             var d = 1;
58321                             var firstAdded = false;
58322                             for (var i = 0; i < result.totalRecords ; i++) {
58323                                 //var d= st.add(Date.DAY, i);
58324                                 var row = {};
58325                                 var added = 0;
58326                                 for(var w = 0 ; w < 7 ; w++){
58327                                     if(!firstAdded && firstOfMonth != w){
58328                                         continue;
58329                                     }
58330                                     if(d > days){
58331                                         continue;
58332                                     }
58333                                     firstAdded = true;
58334                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
58335                                     row['weekday'+w] = String.format(
58336                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
58337                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
58338                                                     d,
58339                                                     date.format('Y-m-')+dd
58340                                                 );
58341                                     added++;
58342                                     if(typeof(_this.tdata[d]) != 'undefined'){
58343                                         Roo.each(_this.tdata[d], function(r){
58344                                             var is_sub = '';
58345                                             var deactive = '';
58346                                             var id = r.id;
58347                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
58348                                             if(r.parent_id*1>0){
58349                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
58350                                                 id = r.parent_id;
58351                                             }
58352                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
58353                                                 deactive = 'de-act-link';
58354                                             }
58355                                             
58356                                             row['weekday'+w] += String.format(
58357                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
58358                                                     id, //0
58359                                                     r.product_id_name, //1
58360                                                     r.when_dt.format('h:ia'), //2
58361                                                     is_sub, //3
58362                                                     deactive, //4
58363                                                     desc // 5
58364                                             );
58365                                         });
58366                                     }
58367                                     d++;
58368                                 }
58369                                 
58370                                 // only do this if something added..
58371                                 if(added > 0){ 
58372                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
58373                                 }
58374                                 
58375                                 
58376                                 // push it twice. (second one with an hour..
58377                                 
58378                             }
58379                             //Roo.log(result);
58380                             this.fireEvent("load", this, o, o.request.arg);
58381                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
58382                         },
58383                     sortInfo : {field: 'when_dt', direction : 'ASC' },
58384                     proxy : {
58385                         xtype: 'HttpProxy',
58386                         xns: Roo.data,
58387                         method : 'GET',
58388                         url : baseURL + '/Roo/Shop_course.php'
58389                     },
58390                     reader : {
58391                         xtype: 'JsonReader',
58392                         xns: Roo.data,
58393                         id : 'id',
58394                         fields : [
58395                             {
58396                                 'name': 'id',
58397                                 'type': 'int'
58398                             },
58399                             {
58400                                 'name': 'when_dt',
58401                                 'type': 'string'
58402                             },
58403                             {
58404                                 'name': 'end_dt',
58405                                 'type': 'string'
58406                             },
58407                             {
58408                                 'name': 'parent_id',
58409                                 'type': 'int'
58410                             },
58411                             {
58412                                 'name': 'product_id',
58413                                 'type': 'int'
58414                             },
58415                             {
58416                                 'name': 'productitem_id',
58417                                 'type': 'int'
58418                             },
58419                             {
58420                                 'name': 'guid',
58421                                 'type': 'int'
58422                             }
58423                         ]
58424                     }
58425                 },
58426                 toolbar : {
58427                     xtype: 'Toolbar',
58428                     xns: Roo,
58429                     items : [
58430                         {
58431                             xtype: 'Button',
58432                             xns: Roo.Toolbar,
58433                             listeners : {
58434                                 click : function (_self, e)
58435                                 {
58436                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58437                                     sd.setMonth(sd.getMonth()-1);
58438                                     _this.monthField.setValue(sd.format('Y-m-d'));
58439                                     _this.grid.ds.load({});
58440                                 }
58441                             },
58442                             text : "Back"
58443                         },
58444                         {
58445                             xtype: 'Separator',
58446                             xns: Roo.Toolbar
58447                         },
58448                         {
58449                             xtype: 'MonthField',
58450                             xns: Roo.form,
58451                             listeners : {
58452                                 render : function (_self)
58453                                 {
58454                                     _this.monthField = _self;
58455                                    // _this.monthField.set  today
58456                                 },
58457                                 select : function (combo, date)
58458                                 {
58459                                     _this.grid.ds.load({});
58460                                 }
58461                             },
58462                             value : (function() { return new Date(); })()
58463                         },
58464                         {
58465                             xtype: 'Separator',
58466                             xns: Roo.Toolbar
58467                         },
58468                         {
58469                             xtype: 'TextItem',
58470                             xns: Roo.Toolbar,
58471                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
58472                         },
58473                         {
58474                             xtype: 'Fill',
58475                             xns: Roo.Toolbar
58476                         },
58477                         {
58478                             xtype: 'Button',
58479                             xns: Roo.Toolbar,
58480                             listeners : {
58481                                 click : function (_self, e)
58482                                 {
58483                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58484                                     sd.setMonth(sd.getMonth()+1);
58485                                     _this.monthField.setValue(sd.format('Y-m-d'));
58486                                     _this.grid.ds.load({});
58487                                 }
58488                             },
58489                             text : "Next"
58490                         }
58491                     ]
58492                 },
58493                  
58494             }
58495         };
58496         
58497         *//*
58498  * Based on:
58499  * Ext JS Library 1.1.1
58500  * Copyright(c) 2006-2007, Ext JS, LLC.
58501  *
58502  * Originally Released Under LGPL - original licence link has changed is not relivant.
58503  *
58504  * Fork - LGPL
58505  * <script type="text/javascript">
58506  */
58507  
58508 /**
58509  * @class Roo.LoadMask
58510  * A simple utility class for generically masking elements while loading data.  If the element being masked has
58511  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
58512  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
58513  * element's UpdateManager load indicator and will be destroyed after the initial load.
58514  * @constructor
58515  * Create a new LoadMask
58516  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
58517  * @param {Object} config The config object
58518  */
58519 Roo.LoadMask = function(el, config){
58520     this.el = Roo.get(el);
58521     Roo.apply(this, config);
58522     if(this.store){
58523         this.store.on('beforeload', this.onBeforeLoad, this);
58524         this.store.on('load', this.onLoad, this);
58525         this.store.on('loadexception', this.onLoadException, this);
58526         this.removeMask = false;
58527     }else{
58528         var um = this.el.getUpdateManager();
58529         um.showLoadIndicator = false; // disable the default indicator
58530         um.on('beforeupdate', this.onBeforeLoad, this);
58531         um.on('update', this.onLoad, this);
58532         um.on('failure', this.onLoad, this);
58533         this.removeMask = true;
58534     }
58535 };
58536
58537 Roo.LoadMask.prototype = {
58538     /**
58539      * @cfg {Boolean} removeMask
58540      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
58541      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
58542      */
58543     /**
58544      * @cfg {String} msg
58545      * The text to display in a centered loading message box (defaults to 'Loading...')
58546      */
58547     msg : 'Loading...',
58548     /**
58549      * @cfg {String} msgCls
58550      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
58551      */
58552     msgCls : 'x-mask-loading',
58553
58554     /**
58555      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
58556      * @type Boolean
58557      */
58558     disabled: false,
58559
58560     /**
58561      * Disables the mask to prevent it from being displayed
58562      */
58563     disable : function(){
58564        this.disabled = true;
58565     },
58566
58567     /**
58568      * Enables the mask so that it can be displayed
58569      */
58570     enable : function(){
58571         this.disabled = false;
58572     },
58573     
58574     onLoadException : function()
58575     {
58576         Roo.log(arguments);
58577         
58578         if (typeof(arguments[3]) != 'undefined') {
58579             Roo.MessageBox.alert("Error loading",arguments[3]);
58580         } 
58581         /*
58582         try {
58583             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
58584                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
58585             }   
58586         } catch(e) {
58587             
58588         }
58589         */
58590     
58591         
58592         
58593         this.el.unmask(this.removeMask);
58594     },
58595     // private
58596     onLoad : function()
58597     {
58598         this.el.unmask(this.removeMask);
58599     },
58600
58601     // private
58602     onBeforeLoad : function(){
58603         if(!this.disabled){
58604             this.el.mask(this.msg, this.msgCls);
58605         }
58606     },
58607
58608     // private
58609     destroy : function(){
58610         if(this.store){
58611             this.store.un('beforeload', this.onBeforeLoad, this);
58612             this.store.un('load', this.onLoad, this);
58613             this.store.un('loadexception', this.onLoadException, this);
58614         }else{
58615             var um = this.el.getUpdateManager();
58616             um.un('beforeupdate', this.onBeforeLoad, this);
58617             um.un('update', this.onLoad, this);
58618             um.un('failure', this.onLoad, this);
58619         }
58620     }
58621 };/*
58622  * Based on:
58623  * Ext JS Library 1.1.1
58624  * Copyright(c) 2006-2007, Ext JS, LLC.
58625  *
58626  * Originally Released Under LGPL - original licence link has changed is not relivant.
58627  *
58628  * Fork - LGPL
58629  * <script type="text/javascript">
58630  */
58631
58632
58633 /**
58634  * @class Roo.XTemplate
58635  * @extends Roo.Template
58636  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
58637 <pre><code>
58638 var t = new Roo.XTemplate(
58639         '&lt;select name="{name}"&gt;',
58640                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
58641         '&lt;/select&gt;'
58642 );
58643  
58644 // then append, applying the master template values
58645  </code></pre>
58646  *
58647  * Supported features:
58648  *
58649  *  Tags:
58650
58651 <pre><code>
58652       {a_variable} - output encoded.
58653       {a_variable.format:("Y-m-d")} - call a method on the variable
58654       {a_variable:raw} - unencoded output
58655       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
58656       {a_variable:this.method_on_template(...)} - call a method on the template object.
58657  
58658 </code></pre>
58659  *  The tpl tag:
58660 <pre><code>
58661         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
58662         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
58663         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
58664         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
58665   
58666         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
58667         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
58668 </code></pre>
58669  *      
58670  */
58671 Roo.XTemplate = function()
58672 {
58673     Roo.XTemplate.superclass.constructor.apply(this, arguments);
58674     if (this.html) {
58675         this.compile();
58676     }
58677 };
58678
58679
58680 Roo.extend(Roo.XTemplate, Roo.Template, {
58681
58682     /**
58683      * The various sub templates
58684      */
58685     tpls : false,
58686     /**
58687      *
58688      * basic tag replacing syntax
58689      * WORD:WORD()
58690      *
58691      * // you can fake an object call by doing this
58692      *  x.t:(test,tesT) 
58693      * 
58694      */
58695     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
58696
58697     /**
58698      * compile the template
58699      *
58700      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
58701      *
58702      */
58703     compile: function()
58704     {
58705         var s = this.html;
58706      
58707         s = ['<tpl>', s, '</tpl>'].join('');
58708     
58709         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
58710             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
58711             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
58712             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
58713             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
58714             m,
58715             id     = 0,
58716             tpls   = [];
58717     
58718         while(true == !!(m = s.match(re))){
58719             var forMatch   = m[0].match(nameRe),
58720                 ifMatch   = m[0].match(ifRe),
58721                 execMatch   = m[0].match(execRe),
58722                 namedMatch   = m[0].match(namedRe),
58723                 
58724                 exp  = null, 
58725                 fn   = null,
58726                 exec = null,
58727                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58728                 
58729             if (ifMatch) {
58730                 // if - puts fn into test..
58731                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58732                 if(exp){
58733                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58734                 }
58735             }
58736             
58737             if (execMatch) {
58738                 // exec - calls a function... returns empty if true is  returned.
58739                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58740                 if(exp){
58741                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58742                 }
58743             }
58744             
58745             
58746             if (name) {
58747                 // for = 
58748                 switch(name){
58749                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58750                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58751                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58752                 }
58753             }
58754             var uid = namedMatch ? namedMatch[1] : id;
58755             
58756             
58757             tpls.push({
58758                 id:     namedMatch ? namedMatch[1] : id,
58759                 target: name,
58760                 exec:   exec,
58761                 test:   fn,
58762                 body:   m[1] || ''
58763             });
58764             if (namedMatch) {
58765                 s = s.replace(m[0], '');
58766             } else { 
58767                 s = s.replace(m[0], '{xtpl'+ id + '}');
58768             }
58769             ++id;
58770         }
58771         this.tpls = [];
58772         for(var i = tpls.length-1; i >= 0; --i){
58773             this.compileTpl(tpls[i]);
58774             this.tpls[tpls[i].id] = tpls[i];
58775         }
58776         this.master = tpls[tpls.length-1];
58777         return this;
58778     },
58779     /**
58780      * same as applyTemplate, except it's done to one of the subTemplates
58781      * when using named templates, you can do:
58782      *
58783      * var str = pl.applySubTemplate('your-name', values);
58784      *
58785      * 
58786      * @param {Number} id of the template
58787      * @param {Object} values to apply to template
58788      * @param {Object} parent (normaly the instance of this object)
58789      */
58790     applySubTemplate : function(id, values, parent)
58791     {
58792         
58793         
58794         var t = this.tpls[id];
58795         
58796         
58797         try { 
58798             if(t.test && !t.test.call(this, values, parent)){
58799                 return '';
58800             }
58801         } catch(e) {
58802             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58803             Roo.log(e.toString());
58804             Roo.log(t.test);
58805             return ''
58806         }
58807         try { 
58808             
58809             if(t.exec && t.exec.call(this, values, parent)){
58810                 return '';
58811             }
58812         } catch(e) {
58813             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58814             Roo.log(e.toString());
58815             Roo.log(t.exec);
58816             return ''
58817         }
58818         try {
58819             var vs = t.target ? t.target.call(this, values, parent) : values;
58820             parent = t.target ? values : parent;
58821             if(t.target && vs instanceof Array){
58822                 var buf = [];
58823                 for(var i = 0, len = vs.length; i < len; i++){
58824                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58825                 }
58826                 return buf.join('');
58827             }
58828             return t.compiled.call(this, vs, parent);
58829         } catch (e) {
58830             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58831             Roo.log(e.toString());
58832             Roo.log(t.compiled);
58833             return '';
58834         }
58835     },
58836
58837     compileTpl : function(tpl)
58838     {
58839         var fm = Roo.util.Format;
58840         var useF = this.disableFormats !== true;
58841         var sep = Roo.isGecko ? "+" : ",";
58842         var undef = function(str) {
58843             Roo.log("Property not found :"  + str);
58844             return '';
58845         };
58846         
58847         var fn = function(m, name, format, args)
58848         {
58849             //Roo.log(arguments);
58850             args = args ? args.replace(/\\'/g,"'") : args;
58851             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58852             if (typeof(format) == 'undefined') {
58853                 format= 'htmlEncode';
58854             }
58855             if (format == 'raw' ) {
58856                 format = false;
58857             }
58858             
58859             if(name.substr(0, 4) == 'xtpl'){
58860                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58861             }
58862             
58863             // build an array of options to determine if value is undefined..
58864             
58865             // basically get 'xxxx.yyyy' then do
58866             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58867             //    (function () { Roo.log("Property not found"); return ''; })() :
58868             //    ......
58869             
58870             var udef_ar = [];
58871             var lookfor = '';
58872             Roo.each(name.split('.'), function(st) {
58873                 lookfor += (lookfor.length ? '.': '') + st;
58874                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58875             });
58876             
58877             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58878             
58879             
58880             if(format && useF){
58881                 
58882                 args = args ? ',' + args : "";
58883                  
58884                 if(format.substr(0, 5) != "this."){
58885                     format = "fm." + format + '(';
58886                 }else{
58887                     format = 'this.call("'+ format.substr(5) + '", ';
58888                     args = ", values";
58889                 }
58890                 
58891                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58892             }
58893              
58894             if (args.length) {
58895                 // called with xxyx.yuu:(test,test)
58896                 // change to ()
58897                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58898             }
58899             // raw.. - :raw modifier..
58900             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58901             
58902         };
58903         var body;
58904         // branched to use + in gecko and [].join() in others
58905         if(Roo.isGecko){
58906             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58907                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58908                     "';};};";
58909         }else{
58910             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58911             body.push(tpl.body.replace(/(\r\n|\n)/g,
58912                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58913             body.push("'].join('');};};");
58914             body = body.join('');
58915         }
58916         
58917         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58918        
58919         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58920         eval(body);
58921         
58922         return this;
58923     },
58924
58925     applyTemplate : function(values){
58926         return this.master.compiled.call(this, values, {});
58927         //var s = this.subs;
58928     },
58929
58930     apply : function(){
58931         return this.applyTemplate.apply(this, arguments);
58932     }
58933
58934  });
58935
58936 Roo.XTemplate.from = function(el){
58937     el = Roo.getDom(el);
58938     return new Roo.XTemplate(el.value || el.innerHTML);
58939 };